Skip to content

Commit

Permalink
feat(indev): detect double and triple click (closes lvgl#6020) (lvgl#…
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasf authored Sep 12, 2024
1 parent 40592dd commit 651f69f
Show file tree
Hide file tree
Showing 13 changed files with 290 additions and 19 deletions.
7 changes: 5 additions & 2 deletions docs/overview/event.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,13 @@ Input device events
- :cpp:enumerator:`LV_EVENT_PRESSED`: The object has been pressed
- :cpp:enumerator:`LV_EVENT_PRESSING`: The object is being pressed (called continuously while pressing)
- :cpp:enumerator:`LV_EVENT_PRESS_LOST`: The object is still being pressed but slid cursor/finger off of the object
- :cpp:enumerator:`LV_EVENT_SHORT_CLICKED`: The object was pressed for a short period of time, then released it. Not called if scrolled.
- :cpp:enumerator:`LV_EVENT_SHORT_CLICKED`: The object was pressed for a short period of time, and then released without scrolling.
- :cpp:enumerator:`LV_EVENT_SINGLE_CLICKED`: The object was pressed for a short period of time, and then released without scrolling, for the first time in a click streak. A click streak refers to multiple short clicks within a short period of time and a small distance.
- :cpp:enumerator:`LV_EVENT_DOUBLE_CLICKED`: The object was pressed for a short period of time, and then released without scrolling, for the second time in a click streak.
- :cpp:enumerator:`LV_EVENT_TRIPLE_CLICKED`: The object was pressed for a short period of time, and then released without scrolling, for the third time in a click streak.
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED`: Object has been pressed for at least `long_press_time`. Not called if scrolled.
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED_REPEAT`: Called after `long_press_time` in every `long_press_repeat_time` ms. Not called if scrolled.
- :cpp:enumerator:`LV_EVENT_CLICKED`: Called on release if not scrolled (regardless to long press)
- :cpp:enumerator:`LV_EVENT_CLICKED`: Called on release if not scrolled (regardless of long press)
- :cpp:enumerator:`LV_EVENT_RELEASED`: Called in every cases when the object has been released
- :cpp:enumerator:`LV_EVENT_SCROLL_BEGIN`: Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified
- :cpp:enumerator:`LV_EVENT_SCROLL_THROW_BEGIN`:
Expand Down
14 changes: 10 additions & 4 deletions examples/event/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,29 @@
Button click event
------------------

.. lv_example:: event/lv_example_event_1
.. lv_example:: event/lv_example_event_click
:language: c

Click streaks
-------------

.. lv_example:: event/lv_example_event_streak
:language: c

Handle multiple events
----------------------
.. lv_example:: event/lv_example_event_2
.. lv_example:: event/lv_example_event_button
:language: c


Event bubbling
--------------
.. lv_example:: event/lv_example_event_3
.. lv_example:: event/lv_example_event_bubble
:language: c

Draw event
----------
.. lv_example:: event/lv_example_event_4
.. lv_example:: event/lv_example_event_draw
:language: c


9 changes: 5 additions & 4 deletions examples/event/lv_example_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ extern "C" {
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_example_event_1(void);
void lv_example_event_2(void);
void lv_example_event_3(void);
void lv_example_event_4(void);
void lv_example_event_click(void);
void lv_example_event_streak(void);
void lv_example_event_button(void);
void lv_example_event_bubble(void);
void lv_example_event_draw(void);

/**********************
* MACROS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ static void event_cb(lv_event_t * e)
/**
* Demonstrate event bubbling
*/
void lv_example_event_3(void)
void lv_example_event_bubble(void)
{

lv_obj_t * cont = lv_obj_create(lv_screen_active());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ static void event_cb(lv_event_t * e)
/**
* Handle multiple events
*/
void lv_example_event_2(void)
void lv_example_event_button(void)
{
lv_obj_t * btn = lv_button_create(lv_screen_active());
lv_obj_set_size(btn, 100, 50);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ static void event_cb(lv_event_t * e)
/**
* Add click event to a button
*/
void lv_example_event_1(void)
void lv_example_event_click(void)
{
lv_obj_t * btn = lv_button_create(lv_screen_active());
lv_obj_set_size(btn, 100, 50);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ static void event_cb(lv_event_t * e)
/**
* Demonstrate the usage of draw event
*/
void lv_example_event_4(void)
void lv_example_event_draw(void)
{
lv_obj_t * cont = lv_obj_create(lv_screen_active());
lv_obj_set_size(cont, 200, 200);
Expand Down
40 changes: 40 additions & 0 deletions examples/event/lv_example_event_streak.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL

static void short_click_event_cb(lv_event_t * e)
{
LV_LOG_USER("Short clicked");

lv_obj_t * info_label = lv_event_get_user_data(e);
lv_indev_t * indev = lv_event_get_param(e);
uint8_t cnt = lv_indev_get_short_click_streak(indev);
lv_label_set_text_fmt(info_label, "Short click streak: %"LV_PRIu32, cnt);
}

static void streak_event_cb(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * label = lv_obj_get_child(btn, 0);
const char * text = lv_event_get_user_data(e);
lv_label_set_text(label, text);
}

void lv_example_event_streak(void)
{
lv_obj_t * info_label = lv_label_create(lv_screen_active());
lv_label_set_text(info_label, "No events yet");

lv_obj_t * btn = lv_button_create(lv_screen_active());
lv_obj_set_size(btn, 100, 50);
lv_obj_center(btn);
lv_obj_add_event_cb(btn, short_click_event_cb, LV_EVENT_SHORT_CLICKED, info_label);
lv_obj_add_event_cb(btn, streak_event_cb, LV_EVENT_SINGLE_CLICKED, "Single clicked");
lv_obj_add_event_cb(btn, streak_event_cb, LV_EVENT_DOUBLE_CLICKED, "Double clicked");
lv_obj_add_event_cb(btn, streak_event_cb, LV_EVENT_TRIPLE_CLICKED, "Triple clicked");

lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Click me!");
lv_obj_center(label);
}

#endif
50 changes: 46 additions & 4 deletions src/indev/lv_indev.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data);
static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data);
static void indev_proc_press(lv_indev_t * indev);
static void indev_proc_release(lv_indev_t * indev);
static lv_result_t indev_proc_short_click(lv_indev_t * indev);
static void indev_proc_pointer_diff(lv_indev_t * indev);
static lv_obj_t * pointer_search_obj(lv_display_t * disp, lv_point_t * p);
static void indev_proc_reset_query_handler(lv_indev_t * indev);
Expand Down Expand Up @@ -485,6 +486,11 @@ uint32_t lv_indev_get_key(const lv_indev_t * indev)
return key;
}

uint8_t lv_indev_get_short_click_streak(const lv_indev_t * indev)
{
return indev->pointer.short_click_streak;
}

lv_dir_t lv_indev_get_scroll_dir(const lv_indev_t * indev)
{
if(indev == NULL) return false;
Expand Down Expand Up @@ -852,7 +858,7 @@ static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data)
if(send_event(LV_EVENT_RELEASED, indev_act) == LV_RESULT_INVALID) return;

if(i->long_pr_sent == 0) {
if(send_event(LV_EVENT_SHORT_CLICKED, indev_act) == LV_RESULT_INVALID) return;
if(indev_proc_short_click(i) == LV_RESULT_INVALID) return;
}

if(send_event(LV_EVENT_CLICKED, indev_act) == LV_RESULT_INVALID) return;
Expand All @@ -864,6 +870,8 @@ static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data)
indev_obj_act = NULL;
}



/**
* Process a new point from LV_INDEV_TYPE_ENCODER input device
* @param i pointer to an input device
Expand Down Expand Up @@ -1015,7 +1023,7 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
}

if(i->long_pr_sent == 0 && is_enabled) {
if(send_event(LV_EVENT_SHORT_CLICKED, indev_act) == LV_RESULT_INVALID) return;
if(indev_proc_short_click(i) == LV_RESULT_INVALID) return;
}

if(is_enabled) {
Expand All @@ -1029,7 +1037,7 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
if(!i->long_pr_sent || lv_group_get_obj_count(g) <= 1) {
if(is_enabled) {
if(send_event(LV_EVENT_RELEASED, indev_act) == LV_RESULT_INVALID) return;
if(send_event(LV_EVENT_SHORT_CLICKED, indev_act) == LV_RESULT_INVALID) return;
if(indev_proc_short_click(i) == LV_RESULT_INVALID) return;
if(send_event(LV_EVENT_CLICKED, indev_act) == LV_RESULT_INVALID) return;
}

Expand Down Expand Up @@ -1353,7 +1361,7 @@ static void indev_proc_release(lv_indev_t * indev)
if(is_enabled) {
if(scroll_obj == NULL) {
if(indev->long_pr_sent == 0) {
if(send_event(LV_EVENT_SHORT_CLICKED, indev_act) == LV_RESULT_INVALID) return;
if(indev_proc_short_click(indev) == LV_RESULT_INVALID) return;
}
if(send_event(LV_EVENT_CLICKED, indev_act) == LV_RESULT_INVALID) return;
}
Expand Down Expand Up @@ -1401,6 +1409,40 @@ static void indev_proc_release(lv_indev_t * indev)
}
}

static lv_result_t indev_proc_short_click(lv_indev_t * indev)
{
/*Update streak for clicks within small distance and short time*/
indev->pointer.short_click_streak++;
if(lv_tick_elaps(indev->pointer.last_short_click_timestamp) > indev->long_press_time) {
indev->pointer.short_click_streak = 1;
}
else if(indev->type == LV_INDEV_TYPE_POINTER || indev->type == LV_INDEV_TYPE_BUTTON) {
int32_t dx = indev->pointer.last_short_click_point.x - indev->pointer.act_point.x;
int32_t dy = indev->pointer.last_short_click_point.y - indev->pointer.act_point.y;
if(dx * dx + dy * dy > indev->scroll_limit * indev->scroll_limit) indev->pointer.short_click_streak = 1;
}

indev->pointer.last_short_click_timestamp = lv_tick_get();
lv_indev_get_point(indev, &indev->pointer.last_short_click_point);

/*Simple short click*/
lv_result_t res = send_event(LV_EVENT_SHORT_CLICKED, indev_act);
if(res == LV_RESULT_INVALID) {
return res;
}

/*Cycle through single/double/triple click*/
switch((indev->pointer.short_click_streak - 1) % 3) {
case 0:
return send_event(LV_EVENT_SINGLE_CLICKED, indev_act);
case 1:
return send_event(LV_EVENT_DOUBLE_CLICKED, indev_act);
case 2:
return send_event(LV_EVENT_TRIPLE_CLICKED, indev_act);
}
return res;
}

static void indev_proc_pointer_diff(lv_indev_t * indev)
{
lv_obj_t * obj = indev->pointer.last_pressed;
Expand Down
9 changes: 9 additions & 0 deletions src/indev/lv_indev.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,15 @@ lv_dir_t lv_indev_get_gesture_dir(const lv_indev_t * indev);
*/
uint32_t lv_indev_get_key(const lv_indev_t * indev);


/**
* Get the counter for consecutive clicks within a short distance and time.
* The counter is updated before LV_EVENT_SHORT_CLICKED is fired.
* @param indev pointer to an input device
* @return short click streak counter
*/
uint8_t lv_indev_get_short_click_streak(const lv_indev_t * indev);

/**
* Check the current scroll direction of an input device (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
Expand Down
5 changes: 4 additions & 1 deletion src/indev/lv_indev_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ struct _lv_indev_t {
lv_area_t scroll_area;
lv_point_t gesture_sum; /*Count the gesture pixels to check LV_INDEV_DEF_GESTURE_LIMIT*/
int32_t diff;

/*Short click streaks*/
uint8_t short_click_streak;
lv_point_t last_short_click_point;
uint32_t last_short_click_timestamp;
/*Flags*/
uint8_t scroll_dir : 4;
uint8_t gesture_dir : 4;
Expand Down
3 changes: 3 additions & 0 deletions src/misc/lv_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ typedef enum {
LV_EVENT_PRESSING, /**< The object is being pressed (called continuously while pressing)*/
LV_EVENT_PRESS_LOST, /**< The object is still being pressed but slid cursor/finger off of the object */
LV_EVENT_SHORT_CLICKED, /**< The object was pressed for a short period of time, then released it. Not called if scrolled.*/
LV_EVENT_SINGLE_CLICKED, /**< Called for the first short click within a small distance and short time*/
LV_EVENT_DOUBLE_CLICKED, /**< Called for the second short click within small distance and short time*/
LV_EVENT_TRIPLE_CLICKED, /**< Called for the third short click within small distance and short time*/
LV_EVENT_LONG_PRESSED, /**< Object has been pressed for at least `long_press_time`. Not called if scrolled.*/
LV_EVENT_LONG_PRESSED_REPEAT, /**< Called after `long_press_time` in every `long_press_repeat_time` ms. Not called if scrolled.*/
LV_EVENT_CLICKED, /**< Called on release if not scrolled (regardless to long press)*/
Expand Down
Loading

0 comments on commit 651f69f

Please sign in to comment.