1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

feat(gridnav): single axis movement flags (#6044)

This commit is contained in:
Liam 2024-05-02 09:54:25 -04:00 committed by GitHub
parent 94651d8620
commit a1a909fc24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 216 additions and 6 deletions

View File

@ -27,7 +27,7 @@ the object so that gridnav can process the arrow keys.
To move the focus to the next widget of the group use
:cpp:enumerator:`LV_KEY_NEXT` or :cpp:enumerator:`LV_KEY_PREV`.
Optionally you can also use :cpp:func:`lv_group_focus_next`
Optionally you can also use :cpp:func:`lv_group_focus_next`
or :cpp:func:`lv_group_focus_prev` or the ``TAB``
key on keyboard as usual.
@ -52,6 +52,13 @@ To add the gridnav feature to an object use
object can be scrolled in that direction then it will be scrolled instead of
going to the next/previous object. If there is no more room for scrolling the
next/previous object will be focused normally
- :cpp:enumerator:`LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY`: Only use the left/right keys
for grid navigation. Up/down key events will be sent to the focused object.
- :cpp:enumerator:`LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY`: Only use the up/down keys
for grid navigation. Left/right key events will be sent to the focused object.
:cpp:enumerator:`LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY` and :cpp:enumerator:`LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY`
should not be used together.
:cpp:expr:`lv_gridnav_remove(cont)` Removes gridnav from an object.

View File

@ -21,4 +21,10 @@ Simple navigation on a list widget
----------------------------------
.. lv_example:: others/gridnav/lv_example_gridnav_4
:language: c
:language: c
Grid navigation for only one axis
---------------------------------
.. lv_example:: others/gridnav/lv_example_gridnav_5
:language: c

View File

@ -29,6 +29,7 @@ void lv_example_gridnav_1(void);
void lv_example_gridnav_2(void);
void lv_example_gridnav_3(void);
void lv_example_gridnav_4(void);
void lv_example_gridnav_5(void);
/**********************
* MACROS

View File

@ -0,0 +1,69 @@
#include "../../lv_examples.h"
#if LV_USE_GRIDNAV && LV_USE_FLEX && LV_BUILD_EXAMPLES
static const char * opts[] = {"0\n1\n2\n3\n4\n5", "0\n1\n2\n3\n4\n5\n6\n7\n8\n9", "s\nm\nh"};
static const int32_t opts_counts[] = {6, 10, 3};
static lv_obj_t * sliders[3];
static lv_obj_t * rollers[3];
static void slider_key_cb(lv_event_t * e)
{
uint8_t i = (uint32_t)(uintptr_t)lv_event_get_user_data(e);
lv_roller_set_selected(rollers[i], lv_slider_get_value(sliders[i]), LV_ANIM_ON);
}
static void roller_key_cb(lv_event_t * e)
{
uint8_t i = (uint32_t)(uintptr_t)lv_event_get_user_data(e);
lv_slider_set_value(sliders[i], lv_roller_get_selected(rollers[i]), LV_ANIM_ON);
}
/**
* Grid navigation for only one axis
*/
void lv_example_gridnav_5(void)
{
/*It's assumed that the default group is set and
*there is a keyboard indev*/
lv_group_t * group = lv_group_get_default();
lv_obj_t * cont;
cont = lv_obj_create(lv_screen_active());
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_size(cont, lv_pct(100), lv_pct(50));
lv_obj_align(cont, LV_ALIGN_TOP_MID, 0, 0);
/* only up/down keys will be used for grid navigation in this container. */
/* left/right will be sent to the sliders */
lv_gridnav_add(cont, LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY);
lv_group_add_obj(group, cont);
for(uint32_t i = 0; i < 3; i++) {
lv_obj_t * slider = lv_slider_create(cont);
lv_slider_set_range(slider, 0, opts_counts[i] - 1);
lv_group_remove_obj(slider);
lv_obj_set_width(slider, lv_pct(85));
sliders[i] = slider;
lv_obj_add_event_cb(slider, slider_key_cb, LV_EVENT_KEY, (void *)(uintptr_t)i);
}
cont = lv_obj_create(lv_screen_active());
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_size(cont, lv_pct(100), lv_pct(50));
lv_obj_align(cont, LV_ALIGN_BOTTOM_MID, 0, 0);
/* only left/right keys will be used for grid navigation in this container. */
/* up/down will be sent to the rollers */
lv_gridnav_add(cont, LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY);
lv_group_add_obj(group, cont);
for(uint32_t i = 0; i < 3; i++) {
lv_obj_t * roller = lv_roller_create(cont);
lv_roller_set_options(roller, opts[i], LV_ROLLER_MODE_INFINITE);
lv_obj_set_size(roller, lv_pct(30), lv_pct(100));
lv_group_remove_obj(roller);
rollers[i] = roller;
lv_obj_add_event_cb(roller, roller_key_cb, LV_EVENT_KEY, (void *)(uintptr_t)i);
}
}
#endif

View File

@ -143,7 +143,7 @@ static void gridnav_event_cb(lv_event_t * e)
uint32_t key = lv_event_get_key(e);
lv_obj_t * guess = NULL;
if(key == LV_KEY_RIGHT) {
if(key == LV_KEY_RIGHT && !(dsc->ctrl & LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY)) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_right(dsc->focused_obj) > 0) {
int32_t d = lv_obj_get_width(dsc->focused_obj) / 4;
@ -163,7 +163,7 @@ static void gridnav_event_cb(lv_event_t * e)
}
}
}
else if(key == LV_KEY_LEFT) {
else if(key == LV_KEY_LEFT && !(dsc->ctrl & LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY)) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_left(dsc->focused_obj) > 0) {
int32_t d = lv_obj_get_width(dsc->focused_obj) / 4;
@ -183,7 +183,7 @@ static void gridnav_event_cb(lv_event_t * e)
}
}
}
else if(key == LV_KEY_DOWN) {
else if(key == LV_KEY_DOWN && !(dsc->ctrl & LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY)) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_bottom(dsc->focused_obj) > 0) {
int32_t d = lv_obj_get_height(dsc->focused_obj) / 4;
@ -202,7 +202,7 @@ static void gridnav_event_cb(lv_event_t * e)
}
}
}
else if(key == LV_KEY_UP) {
else if(key == LV_KEY_UP && !(dsc->ctrl & LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY)) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_top(dsc->focused_obj) > 0) {
int32_t d = lv_obj_get_height(dsc->focused_obj) / 4;

View File

@ -80,6 +80,18 @@ typedef enum {
* If there is no more room for scrolling the next/previous object will be focused normally */
LV_GRIDNAV_CTRL_SCROLL_FIRST = 0x2,
/**
* Only use left/right keys for grid navigation. Up/down key events will be sent to the
* focused object.
*/
LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY = 0x4,
/**
* Only use up/down keys for grid navigation. Left/right key events will be sent to the
* focused object.
*/
LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY = 0x8
} lv_gridnav_ctrl_t;
/**********************

View File

@ -90,6 +90,7 @@
#define LV_USE_VECTOR_GRAPHIC 1
#define LV_USE_PROFILER 1
#define LV_PROFILER_INCLUDE "lv_profiler_builtin.h"
#define LV_USE_GRIDNAV 1
#define LV_BUILD_EXAMPLES 1
#define LV_USE_DEMO_WIDGETS 1

View File

@ -0,0 +1,114 @@
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "unity/unity.h"
#include "lv_test_indev.h"
static lv_obj_t * g_screen;
static lv_group_t * g_group;
static struct {
bool press_happened;
uint32_t key;
lv_obj_t * obj;
} g_key_data;
void setUp(void)
{
g_screen = lv_screen_active();
g_group = lv_group_create();
g_key_data.press_happened = false;
}
void tearDown(void)
{
lv_obj_clean(g_screen);
lv_group_delete(g_group); /* also removes all indevs set to the group */
}
static void key_event_cb(lv_event_t * e)
{
TEST_ASSERT_FALSE(g_key_data.press_happened);
g_key_data.press_happened = true;
g_key_data.key = *(uint32_t *)lv_event_get_param(e);
g_key_data.obj = lv_event_get_target_obj(e);
}
static void gridnav_one_axis_move_only(uint32_t key_grid_axis_next,
uint32_t key_grid_axis_prev,
uint32_t key_obj_axis_next,
uint32_t key_obj_axis_prev,
lv_gridnav_ctrl_t gridnav_ctrl,
lv_flex_flow_t flex_flow)
{
lv_indev_set_group(lv_test_keypad_indev, g_group);
lv_obj_t * cont = lv_obj_create(g_screen);
lv_obj_set_flex_flow(cont, flex_flow);
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_size(cont, lv_pct(100), lv_pct(100));
lv_obj_center(cont);
lv_gridnav_add(cont, gridnav_ctrl);
lv_group_add_obj(g_group, cont);
lv_obj_t * objs[3];
for(uint32_t i = 0; i < 3; i++) {
lv_obj_t * obj = lv_obj_create(cont);
lv_obj_create(obj); /* the obj needs a child to be focusable by gridnav */
lv_group_remove_obj(obj);
lv_obj_add_event_cb(obj, key_event_cb, LV_EVENT_KEY, NULL);
objs[i] = obj;
}
TEST_ASSERT(lv_obj_get_state(objs[0]) & LV_STATE_FOCUSED);
/* gridnav direction key moves the focus */
lv_test_key_hit(key_grid_axis_next);
TEST_ASSERT(lv_obj_get_state(objs[1]) & LV_STATE_FOCUSED);
TEST_ASSERT_FALSE(g_key_data.press_happened);
/* non gridnav direction key does not move the focus. */
/* the key is sent to the object instead */
lv_test_key_hit(key_obj_axis_next);
TEST_ASSERT(lv_obj_get_state(objs[1]) & LV_STATE_FOCUSED);
TEST_ASSERT_TRUE(g_key_data.press_happened);
TEST_ASSERT(g_key_data.key == key_obj_axis_next);
TEST_ASSERT(g_key_data.obj == objs[1]);
g_key_data.press_happened = false;
lv_test_key_hit(key_obj_axis_prev);
TEST_ASSERT(lv_obj_get_state(objs[1]) & LV_STATE_FOCUSED);
TEST_ASSERT_TRUE(g_key_data.press_happened);
TEST_ASSERT(g_key_data.key == key_obj_axis_prev);
TEST_ASSERT(g_key_data.obj == objs[1]);
g_key_data.press_happened = false;
/* go back */
lv_test_key_hit(key_grid_axis_prev);
TEST_ASSERT(lv_obj_get_state(objs[0]) & LV_STATE_FOCUSED);
TEST_ASSERT_FALSE(g_key_data.press_happened);
/* at the beginning, can't move further back */
lv_test_key_hit(key_grid_axis_prev);
TEST_ASSERT(lv_obj_get_state(objs[0]) & LV_STATE_FOCUSED);
TEST_ASSERT_FALSE(g_key_data.press_happened);
}
void test_gridnav_vertical_move_only(void)
{
gridnav_one_axis_move_only(LV_KEY_DOWN,
LV_KEY_UP,
LV_KEY_RIGHT,
LV_KEY_LEFT,
LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY,
LV_FLEX_FLOW_COLUMN);
}
void test_gridnav_horizontal_move_only(void)
{
gridnav_one_axis_move_only(LV_KEY_RIGHT,
LV_KEY_LEFT,
LV_KEY_DOWN,
LV_KEY_UP,
LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY,
LV_FLEX_FLOW_ROW);
}
#endif