mirror of
https://github.com/lvgl/lvgl.git
synced 2025-01-28 07:03:00 +08:00
feat(hover): add Hover support for pointer device. (#5947)
Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
parent
0a03d9da1e
commit
67fad672e3
@ -712,6 +712,12 @@ static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
|
||||
lv_obj_remove_state(obj, LV_STATE_PRESSED);
|
||||
lv_obj_remove_state(obj, LV_STATE_SCROLLED);
|
||||
}
|
||||
else if(code == LV_EVENT_HOVER_OVER) {
|
||||
lv_obj_add_state(obj, LV_STATE_HOVERED);
|
||||
}
|
||||
else if(code == LV_EVENT_HOVER_LEAVE) {
|
||||
lv_obj_remove_state(obj, LV_STATE_HOVERED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -193,7 +193,9 @@ lv_indev_t * lv_event_get_indev(lv_event_t * e)
|
||||
e->code == LV_EVENT_KEY ||
|
||||
e->code == LV_EVENT_FOCUSED ||
|
||||
e->code == LV_EVENT_DEFOCUSED ||
|
||||
e->code == LV_EVENT_LEAVE) {
|
||||
e->code == LV_EVENT_LEAVE ||
|
||||
e->code == LV_EVENT_HOVER_OVER ||
|
||||
e->code == LV_EVENT_HOVER_LEAVE) {
|
||||
return lv_event_get_param(e);
|
||||
}
|
||||
else {
|
||||
|
@ -114,12 +114,12 @@ struct _lv_display_t {
|
||||
|
||||
/** Screens of the display*/
|
||||
lv_obj_t ** screens; /**< Array of screen objects.*/
|
||||
lv_obj_t * sys_layer; /**< @see lv_display_get_layer_sys*/
|
||||
lv_obj_t * top_layer; /**< @see lv_display_get_layer_top*/
|
||||
lv_obj_t * act_scr; /**< Currently active screen on this display*/
|
||||
lv_obj_t * bottom_layer;/**< @see lv_display_get_layer_bottom*/
|
||||
lv_obj_t * prev_scr; /**< Previous screen. Used during screen animations*/
|
||||
lv_obj_t * scr_to_load; /**< The screen prepared to load in lv_screen_load_anim*/
|
||||
lv_obj_t * bottom_layer; /**< @see lv_display_get_layer_bottom*/
|
||||
lv_obj_t * top_layer; /**< @see lv_display_get_layer_top*/
|
||||
lv_obj_t * sys_layer; /**< @see lv_display_get_layer_sys*/
|
||||
uint32_t screen_cnt;
|
||||
uint8_t draw_prev_over_act : 1;/** 1: Draw previous screen over active screen*/
|
||||
uint8_t del_prev : 1; /** 1: Automatically delete the previous screen when the screen load animation is ready*/
|
||||
|
@ -679,7 +679,6 @@ static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
|
||||
|
||||
i->pointer.last_point.x = i->pointer.act_point.x;
|
||||
i->pointer.last_point.y = i->pointer.act_point.y;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1121,7 +1120,7 @@ static void indev_proc_press(lv_indev_t * indev)
|
||||
indev_obj_act = pointer_search_obj(disp, &indev->pointer.act_point);
|
||||
new_obj_searched = true;
|
||||
}
|
||||
/*If there is an active object it's not scrolled and not protected also search*/
|
||||
/*If there is an active object it's not scrolled and not press locked also search*/
|
||||
else if(indev->pointer.scroll_obj == NULL &&
|
||||
lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_PRESS_LOCK) == false) {
|
||||
indev_obj_act = pointer_search_obj(disp, &indev->pointer.act_point);
|
||||
@ -1130,7 +1129,6 @@ static void indev_proc_press(lv_indev_t * indev)
|
||||
|
||||
/*The scroll object might have scroll throw. Stop it manually*/
|
||||
if(new_obj_searched && indev->pointer.scroll_obj) {
|
||||
|
||||
/*Attempt to stop scroll throw animation firstly*/
|
||||
if(indev->scroll_throw_anim) {
|
||||
lv_anim_delete(indev, indev_scroll_throw_anim_cb);
|
||||
@ -1146,6 +1144,17 @@ static void indev_proc_press(lv_indev_t * indev)
|
||||
indev->pointer.last_point.x = indev->pointer.act_point.x;
|
||||
indev->pointer.last_point.y = indev->pointer.act_point.y;
|
||||
|
||||
/*Without `LV_OBJ_FLAG_PRESS_LOCK` new widget can be found while pressing.*/
|
||||
if(indev->pointer.last_hovered && indev->pointer.last_hovered != indev_obj_act) {
|
||||
lv_obj_send_event(indev->pointer.last_hovered, LV_EVENT_HOVER_LEAVE, indev);
|
||||
if(indev_reset_check(indev)) return;
|
||||
|
||||
lv_indev_send_event(indev, LV_EVENT_HOVER_LEAVE, indev->pointer.last_hovered);
|
||||
if(indev_reset_check(indev)) return;
|
||||
|
||||
indev->pointer.last_hovered = indev_obj_act;
|
||||
}
|
||||
|
||||
/*If a new object found the previous was lost, so send a PRESS_LOST event*/
|
||||
if(indev->pointer.act_obj != NULL) {
|
||||
/*Save the obj because in special cases `act_obj` can change in the event */
|
||||
@ -1181,6 +1190,9 @@ static void indev_proc_press(lv_indev_t * indev)
|
||||
|
||||
const bool is_enabled = !lv_obj_has_state(indev_obj_act, LV_STATE_DISABLED);
|
||||
if(is_enabled) {
|
||||
if(indev->pointer.last_hovered != indev_obj_act) {
|
||||
if(send_event(LV_EVENT_HOVER_OVER, indev_act) == LV_RESULT_INVALID) return;
|
||||
}
|
||||
if(send_event(LV_EVENT_PRESSED, indev_act) == LV_RESULT_INVALID) return;
|
||||
}
|
||||
|
||||
@ -1252,6 +1264,25 @@ static void indev_proc_press(lv_indev_t * indev)
|
||||
*/
|
||||
static void indev_proc_release(lv_indev_t * indev)
|
||||
{
|
||||
if(indev->wait_until_release || /*Hover the new widget even if the coordinates didn't changed*/
|
||||
(indev->pointer.last_point.x != indev->pointer.act_point.x ||
|
||||
indev->pointer.last_point.y != indev->pointer.act_point.y)) {
|
||||
lv_obj_t ** last = &indev->pointer.last_hovered;
|
||||
lv_obj_t * hovered = pointer_search_obj(lv_display_get_default(), &indev->pointer.act_point);
|
||||
if(*last != hovered) {
|
||||
lv_obj_send_event(hovered, LV_EVENT_HOVER_OVER, indev);
|
||||
if(indev_reset_check(indev)) return;
|
||||
lv_indev_send_event(indev, LV_EVENT_HOVER_OVER, hovered);
|
||||
if(indev_reset_check(indev)) return;
|
||||
|
||||
lv_obj_send_event(*last, LV_EVENT_HOVER_LEAVE, indev);
|
||||
if(indev_reset_check(indev)) return;
|
||||
lv_indev_send_event(indev, LV_EVENT_HOVER_LEAVE, *last);
|
||||
if(indev_reset_check(indev)) return;
|
||||
*last = hovered;
|
||||
}
|
||||
}
|
||||
|
||||
if(indev->wait_until_release) {
|
||||
lv_obj_send_event(indev->pointer.act_obj, LV_EVENT_PRESS_LOST, indev_act);
|
||||
if(indev_reset_check(indev)) return;
|
||||
@ -1390,7 +1421,8 @@ static void indev_proc_reset_query_handler(lv_indev_t * indev)
|
||||
if(indev->reset_query) {
|
||||
indev->pointer.act_obj = NULL;
|
||||
indev->pointer.last_obj = NULL;
|
||||
indev->pointer.scroll_obj = NULL;
|
||||
indev->pointer.scroll_obj = NULL;
|
||||
indev->pointer.last_hovered = NULL;
|
||||
indev->long_pr_sent = 0;
|
||||
indev->pr_timestamp = 0;
|
||||
indev->longpr_rep_timestamp = 0;
|
||||
@ -1582,6 +1614,9 @@ static void indev_reset_core(lv_indev_t * indev, lv_obj_t * obj)
|
||||
scroll_obj = NULL;
|
||||
}
|
||||
}
|
||||
if(obj == NULL || indev->pointer.last_hovered == obj) {
|
||||
indev->pointer.last_hovered = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@ struct _lv_indev_t {
|
||||
lv_obj_t * last_obj; /*The last object which was pressed*/
|
||||
lv_obj_t * scroll_obj; /*The object being scrolled*/
|
||||
lv_obj_t * last_pressed; /*The lastly pressed object*/
|
||||
lv_obj_t * last_hovered; /*The lastly hovered object*/
|
||||
lv_area_t scroll_area;
|
||||
lv_point_t gesture_sum; /*Count the gesture pixels to check LV_INDEV_DEF_GESTURE_LIMIT*/
|
||||
int32_t diff;
|
||||
|
@ -61,6 +61,8 @@ typedef enum {
|
||||
LV_EVENT_LEAVE, /**< The object is defocused but still selected*/
|
||||
LV_EVENT_HIT_TEST, /**< Perform advanced hit-testing*/
|
||||
LV_EVENT_INDEV_RESET, /**< Indev has been reset*/
|
||||
LV_EVENT_HOVER_OVER, /**< Indev hover over object*/
|
||||
LV_EVENT_HOVER_LEAVE, /**< Indev hover leave object*/
|
||||
|
||||
/** Drawing events*/
|
||||
LV_EVENT_COVER_CHECK, /**< Check if the object fully covers an area. The event parameter is `lv_cover_check_info_t *`.*/
|
||||
|
95
tests/src/test_cases/test_hover.c
Normal file
95
tests/src/test_cases/test_hover.c
Normal file
@ -0,0 +1,95 @@
|
||||
#if LV_BUILD_TEST
|
||||
#include "../lvgl.h"
|
||||
#include "../lv_test_indev.h"
|
||||
#include "unity/unity.h"
|
||||
|
||||
#define TEST_HOVER_COUNTS 20
|
||||
|
||||
typedef struct _test_hover_t {
|
||||
/* data */
|
||||
uint32_t id;
|
||||
uint32_t counts;
|
||||
} test_hover_t;
|
||||
|
||||
static test_hover_t label_hovered;
|
||||
static test_hover_t btn_hovered;
|
||||
|
||||
static const lv_point_t pointer1[] = {
|
||||
{0, 0}, {110, 20}, {150, 26}, {120, 19}, {0, 0},
|
||||
};
|
||||
|
||||
static const lv_point_t pointer2[] = {
|
||||
{0, 0}, {60, 100}, {80, 100}, {120, 120}, {0, 0},
|
||||
};
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
/* Function run before every test */
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
/* Function run after every test */
|
||||
lv_obj_clean(lv_screen_active());
|
||||
}
|
||||
|
||||
static void hovered_event_cb(lv_event_t * e)
|
||||
{
|
||||
test_hover_t * hover = (test_hover_t *)e->user_data;
|
||||
lv_log("Object(ID:%d) hovered %d/%d times.\n", hover->id, hover->counts, TEST_HOVER_COUNTS);
|
||||
}
|
||||
|
||||
static void test_move_mouse(lv_point_t * point, uint8_t size)
|
||||
{
|
||||
lv_point_t * p = point;
|
||||
|
||||
for(uint8_t j = 0; j < TEST_HOVER_COUNTS; j++) {
|
||||
for(uint8_t i = 0; i < size; i++) {
|
||||
lv_test_mouse_move_to(p[i].x, p[i].y);
|
||||
lv_test_indev_wait(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_hover_basic(void)
|
||||
{
|
||||
lv_obj_t * label = lv_label_create(lv_screen_active());
|
||||
lv_obj_set_size(label, 200, 20);
|
||||
lv_label_set_text(label, "Clickable text can be hovered!");
|
||||
lv_obj_set_pos(label, 100, 20);
|
||||
lv_obj_add_flag(label, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(0x5be1b6), LV_PART_MAIN | LV_STATE_HOVERED);
|
||||
|
||||
/*Set hover callback*/
|
||||
label_hovered.id = (lv_uintptr_t)label->id;
|
||||
label_hovered.counts = 0;
|
||||
lv_obj_add_event_cb(label, hovered_event_cb, LV_EVENT_HOVER_OVER, &label_hovered);
|
||||
|
||||
lv_obj_t * btn = lv_button_create(lv_screen_active());
|
||||
lv_obj_set_pos(btn, 64, 100);
|
||||
lv_obj_set_size(btn, 128, 48);
|
||||
lv_obj_set_style_bg_opa(btn, 128, LV_PART_MAIN | LV_STATE_HOVERED);
|
||||
|
||||
/*Set hover callback*/
|
||||
btn_hovered.id = (lv_uintptr_t)btn->id;
|
||||
btn_hovered.counts = 0;
|
||||
lv_obj_add_event_cb(btn, hovered_event_cb, LV_EVENT_HOVER_OVER, &btn_hovered);
|
||||
|
||||
test_move_mouse((lv_point_t *)pointer1, 5);
|
||||
test_move_mouse((lv_point_t *)pointer2, 5);
|
||||
}
|
||||
|
||||
void test_hover_delete(void)
|
||||
{
|
||||
lv_obj_t * button = lv_button_create(lv_screen_active());
|
||||
lv_obj_set_size(button, 200, 100);
|
||||
|
||||
lv_test_indev_wait(50);
|
||||
lv_test_mouse_move_to(50, 50);
|
||||
lv_test_indev_wait(50);
|
||||
lv_obj_delete(button); /*No crash while deleting the hovered button*/
|
||||
lv_test_indev_wait(50);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user