1
0
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:
Samuel 2024-05-03 00:23:35 +08:00 committed by GitHub
parent 0a03d9da1e
commit 67fad672e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 149 additions and 8 deletions

View File

@ -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);
}
}
/**

View File

@ -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 {

View File

@ -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*/

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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 *`.*/

View 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