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:
parent
65d79a7905
commit
7b4f461944
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user