1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-14 06:42:58 +08:00

Implement custom, opt-in hit-testing handlers for objects (#1318)

This commit is contained in:
embeddedt 2019-12-22 13:51:02 +00:00 committed by GitHub
parent 65d79a7905
commit 7b4f461944
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 193 additions and 30 deletions

View File

@ -1034,25 +1034,7 @@ lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t *point)
lv_obj_t * found_p = NULL;
/*If the point is on this object check its children too*/
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
lv_area_t ext_area;
ext_area.x1 = obj->coords.x1 - obj->ext_click_pad_hor;
ext_area.x2 = obj->coords.x2 + obj->ext_click_pad_hor;
ext_area.y1 = obj->coords.y1 - obj->ext_click_pad_ver;
ext_area.y2 = obj->coords.y2 + obj->ext_click_pad_ver;
if(lv_area_is_point_on(&ext_area, point)) {
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
lv_area_t ext_area;
ext_area.x1 = obj->coords.x1 - obj->ext_click_pad.x1;
ext_area.x2 = obj->coords.x2 + obj->ext_click_pad.x2;
ext_area.y1 = obj->coords.y1 - obj->ext_click_pad.y1;
ext_area.y2 = obj->coords.y2 + obj->ext_click_pad.y2;
if(lv_area_is_point_on(&ext_area, point)) {
#else
if(lv_area_is_point_on(&obj->coords, point)) {
#endif
if(lv_obj_hittest(obj, point)) {
lv_obj_t * i;
LV_LL_READ(obj->child_ll, i)

View File

@ -201,6 +201,7 @@ lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy)
new_obj->group_p = NULL;
#endif
/*Set attributes*/
new_obj->adv_hittest = 0;
new_obj->click = 0;
new_obj->drag = 0;
new_obj->drag_throw = 0;
@ -1275,6 +1276,17 @@ void lv_obj_set_hidden(lv_obj_t * obj, bool en)
par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
}
/**
* Set whether advanced hit-testing is enabled on an object
* @param obj pointer to an object
* @param en true: advanced hit-testing is enabled
*/
void lv_obj_set_adv_hittest(lv_obj_t * obj, bool en) {
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
obj->adv_hittest = en == false ? 0 : 1;
}
/**
* Enable or disable the clicking of an object
* @param obj pointer to an object
@ -2053,6 +2065,18 @@ bool lv_obj_get_hidden(const lv_obj_t * obj)
return obj->hidden == 0 ? false : true;
}
/**
* Get whether advanced hit-testing is enabled on an object
* @param obj pointer to an object
* @return true: advanced hit-testing is enabled
*/
bool lv_obj_get_adv_hittest(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
return obj->adv_hittest == 0 ? false : true;
}
/**
* Get the click enable attribute of an object
* @param obj pointer to an object
@ -2368,6 +2392,45 @@ bool lv_obj_is_focused(const lv_obj_t * obj)
* OTHER FUNCTIONS
*------------------*/
/**
* Hit-test an object given a particular point in screen space.
* @param obj object to hit-test
* @param point screen-space point
* @return true if the object is considered under the point
*/
bool lv_obj_hittest(lv_obj_t * obj, lv_point_t * point) {
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
lv_area_t ext_area;
ext_area.x1 = obj->coords.x1 - obj->ext_click_pad_hor;
ext_area.x2 = obj->coords.x2 + obj->ext_click_pad_hor;
ext_area.y1 = obj->coords.y1 - obj->ext_click_pad_ver;
ext_area.y2 = obj->coords.y2 + obj->ext_click_pad_ver;
if(!lv_area_is_point_on(&ext_area, point, 0)) {
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
lv_area_t ext_area;
ext_area.x1 = obj->coords.x1 - obj->ext_click_pad.x1;
ext_area.x2 = obj->coords.x2 + obj->ext_click_pad.x2;
ext_area.y1 = obj->coords.y1 - obj->ext_click_pad.y1;
ext_area.y2 = obj->coords.y2 + obj->ext_click_pad.y2;
if(!lv_area_is_point_on(&ext_area, point, 0)) {
#else
if(!lv_area_is_point_on(&obj->coords, point, 0)) {
#endif
return false;
}
if(obj->adv_hittest) {
lv_hit_test_info_t hit_info;
hit_info.point = point;
hit_info.result = true;
obj->signal_cb(obj, LV_SIGNAL_HIT_TEST, &hit_info);
if(!hit_info.result)
return false;
}
return true;
}
/**
* Used in the signal callback to handle `LV_SIGNAL_GET_TYPE` signal
* @param obj pointer to an object

View File

@ -128,6 +128,7 @@ enum {
LV_SIGNAL_GET_TYPE, /**< LittlevGL needs to retrieve the object's type */
/*Input device related*/
LV_SIGNAL_HIT_TEST, /**< Advanced hit-testing */
LV_SIGNAL_PRESSED, /**< The object has been pressed*/
LV_SIGNAL_PRESSING, /**< The object is being pressed (called continuously while pressing)*/
LV_SIGNAL_PRESS_LOST, /**< User is still pressing but slid cursor/finger off of the object */
@ -225,7 +226,8 @@ typedef struct _lv_obj_t
uint8_t parent_event : 1; /**< 1: Send the object's events to the parent too. */
lv_drag_dir_t drag_dir : 3; /**< Which directions the object can be dragged in */
lv_bidi_dir_t base_dir : 2; /**< Base direction of texts related to this object */
uint8_t reserved : 3; /**< Reserved for future use*/
uint8_t adv_hittest : 1; /**< 1: Use advanced hit-testing (slower) */
uint8_t reserved : 2; /**< Reserved for future use*/
uint8_t protect; /**< Automatically happening actions can be prevented. 'OR'ed values from
`lv_protect_t`*/
lv_opa_t opa_scale; /**< Scale down the opacity by this factor. Effects all children as well*/
@ -263,6 +265,12 @@ typedef struct
... [x]: "lv_obj" */
} lv_obj_type_t;
typedef struct _lv_hit_test_info_t
{
lv_point_t *point;
bool result;
} lv_hit_test_info_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
@ -466,6 +474,13 @@ void lv_obj_report_style_mod(lv_style_t * style);
*/
void lv_obj_set_hidden(lv_obj_t * obj, bool en);
/**
* Set whether advanced hit-testing is enabled on an object
* @param obj pointer to an object
* @param en true: advanced hit-testing is enabled
*/
void lv_obj_set_adv_hittest(lv_obj_t * obj, bool en);
/**
* Enable or disable the clicking of an object
* @param obj pointer to an object
@ -808,6 +823,13 @@ const lv_style_t * lv_obj_get_style(const lv_obj_t * obj);
*/
bool lv_obj_get_hidden(const lv_obj_t * obj);
/**
* Get whether advanced hit-testing is enabled on an object
* @param obj pointer to an object
* @return true: advanced hit-testing is enabled
*/
bool lv_obj_get_adv_hittest(const lv_obj_t * obj);
/**
* Get the click enable attribute of an object
* @param obj pointer to an object
@ -914,6 +936,8 @@ lv_event_cb_t lv_obj_get_event_cb(const lv_obj_t * obj);
* Other get
*-----------------*/
bool lv_obj_hittest(lv_obj_t * obj, lv_point_t * point);
/**
* Get the ext pointer
* @param obj pointer to an object

View File

@ -27,6 +27,8 @@
* STATIC PROTOTYPES
**********************/
static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p);
/**********************
* STATIC VARIABLES
**********************/
@ -148,15 +150,62 @@ void lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t *
* @param p_p pointer to a point
* @return false:the point is out of the area
*/
bool lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p)
bool lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, lv_coord_t radius)
{
bool is_on = false;
/*First check the basic area*/
bool is_on_rect = false;
if((p_p->x >= a_p->x1 && p_p->x <= a_p->x2) && ((p_p->y >= a_p->y1 && p_p->y <= a_p->y2))) {
is_on = true;
is_on_rect = true;
}
return is_on;
if(!is_on_rect)
return false;
/*Now handle potential rounded rectangles*/
if(radius <= 0) {
/*No radius, it is within the rectangle*/
return true;
}
lv_coord_t max_radius = LV_MATH_MIN(lv_area_get_width(a_p) / 2, lv_area_get_height(a_p) / 2);
if(radius > max_radius)
radius = max_radius;
/*Check if it's in one of the corners*/
lv_area_t corner_area;
/*Top left*/
corner_area.x1 = a_p->x1;
corner_area.x2 = a_p->x1 + radius;
corner_area.y1 = a_p->y1;
corner_area.y2 = a_p->y1 + radius;
if(lv_area_is_point_on(&corner_area, p_p, 0)) {
corner_area.x2 += radius;
corner_area.y2 += radius;
return lv_point_within_circle(&corner_area, p_p);
}
/*Bottom left*/
corner_area.y1 = a_p->y2 - radius;
corner_area.y2 = a_p->y2;
if(lv_area_is_point_on(&corner_area, p_p, 0)) {
corner_area.x2 += radius;
corner_area.y1 -= radius;
return lv_point_within_circle(&corner_area, p_p);
}
/*Bottom right*/
corner_area.x1 = a_p->x2 - radius;
corner_area.x2 = a_p->x2;
if(lv_area_is_point_on(&corner_area, p_p, 0)) {
corner_area.x1 -= radius;
corner_area.y1 -= radius;
return lv_point_within_circle(&corner_area, p_p);
}
/*Top right*/
corner_area.y1 = a_p->y1;
corner_area.y2 = a_p->y1 + radius;
if(lv_area_is_point_on(&corner_area, p_p, 0)) {
corner_area.x1 -= radius;
corner_area.y2 += radius;
return lv_point_within_circle(&corner_area, p_p);
}
/*Not within corners*/
return false;
}
/**
@ -208,3 +257,24 @@ void lv_area_increment(lv_area_t * a_p, const lv_coord_t amount)
/**********************
* STATIC FUNCTIONS
**********************/
static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p)
{
lv_coord_t r = (area->x2 - area->x1) / 2;
/* Circle center */
lv_coord_t cx = area->x1 + r;
lv_coord_t cy = area->y1 + r;
/*Simplify the code by moving everything to (0, 0) */
lv_coord_t px = p->x - cx;
lv_coord_t py = p->y - cy;
int32_t r_sqrd = r*r;
int32_t dist = (px*px) + (py*py);
if(dist <= r_sqrd)
return true;
else
return false;
}

View File

@ -148,9 +148,10 @@ void lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t *
* Check if a point is on an area
* @param a_p pointer to an area
* @param p_p pointer to a point
* @param radius radius of area (e.g. for rounded rectangle)
* @return false:the point is out of the area
*/
bool lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p);
bool lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, lv_coord_t radius);
/**
* Check if two area has common parts

View File

@ -1056,7 +1056,7 @@ static uint16_t get_button_from_point(lv_obj_t * btnm, lv_point_t * p)
btn_area.y1 += btnm_cords.y1;
btn_area.x2 += btnm_cords.x1;
btn_area.y2 += btnm_cords.y1;
if(lv_area_is_point_on(&btn_area, p) != false) {
if(lv_area_is_point_on(&btn_area, p, 0) != false) {
break;
}
}

View File

@ -508,7 +508,7 @@ static lv_res_t lv_calendar_signal(lv_obj_t * calendar, lv_signal_t sign, void *
lv_indev_get_point(indev, &p);
/*If the header is pressed mark an arrow as pressed*/
if(lv_area_is_point_on(&header_area, &p)) {
if(lv_area_is_point_on(&header_area, &p, 0)) {
if(p.x < header_area.x1 + lv_area_get_width(&header_area) / 2) {
if(ext->btn_pressing != -1) lv_obj_invalidate(calendar);
ext->btn_pressing = -1;
@ -605,7 +605,7 @@ static bool calculate_touched_day(lv_obj_t * calendar, const lv_point_t * touche
days_area.y1 =
calendar->coords.y1 + get_header_height(calendar) + get_day_names_height(calendar) - style_bg->body.padding.top;
if(lv_area_is_point_on(&days_area, touched_point)) {
if(lv_area_is_point_on(&days_area, touched_point, 0)) {
lv_coord_t w = (days_area.x2 - days_area.x1 + 1) / 7;
lv_coord_t h = (days_area.y2 - days_area.y1 + 1) / 6;
uint8_t x_pos = 0;

View File

@ -57,6 +57,7 @@
**********************/
static lv_design_res_t lv_cpicker_design(lv_obj_t * cpicker, const lv_area_t * clip_area, lv_design_mode_t mode);
static lv_res_t lv_cpicker_signal(lv_obj_t * cpicker, lv_signal_t sign, void * param);
static bool lv_cpicker_hit(lv_obj_t * cpicker, const lv_point_t * p);
static void draw_rect_grad(lv_obj_t * cpicker, const lv_area_t * mask, lv_opa_t opa_scale);
static void draw_disc_grad(lv_obj_t * cpicker, const lv_area_t * mask, lv_opa_t opa_scale);
@ -889,11 +890,33 @@ static lv_res_t lv_cpicker_signal(lv_obj_t * cpicker, lv_signal_t sign, void * p
res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return res;
}
} else if(sign == LV_SIGNAL_HIT_TEST) {
lv_hit_test_info_t *info = param;
info->result = lv_cpicker_hit(cpicker, info->point);
}
return res;
}
static bool lv_cpicker_hit(lv_obj_t * cpicker, const lv_point_t * p)
{
lv_cpicker_ext_t * ext = (lv_cpicker_ext_t *)lv_obj_get_ext_attr(cpicker);
if(ext->type != LV_CPICKER_TYPE_DISC || ext->preview)
return true;
const lv_style_t * style_main = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN);
lv_area_t area_mid;
lv_area_copy(&area_mid, &cpicker->coords);
area_mid.x1 += style_main->line.width;
area_mid.y1 += style_main->line.width;
area_mid.x2 -= style_main->line.width;
area_mid.y2 -= style_main->line.width;
if(lv_area_is_point_on(&area_mid, p, LV_RADIUS_CIRCLE))
return false;
return true;
}
static void next_color_mode(lv_obj_t * cpicker)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);