mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-10-17 02:45:05 +00:00
Gridnav and fixes
This commit is contained in:
parent
60133ddda6
commit
0b5c302aa2
@ -37,6 +37,8 @@ CONFIG_LV_FS_STDIO_LETTER=65
|
|||||||
CONFIG_LV_FS_STDIO_PATH=""
|
CONFIG_LV_FS_STDIO_PATH=""
|
||||||
CONFIG_LV_FS_STDIO_CACHE_SIZE=4096
|
CONFIG_LV_FS_STDIO_CACHE_SIZE=4096
|
||||||
CONFIG_LV_USE_LODEPNG=n
|
CONFIG_LV_USE_LODEPNG=n
|
||||||
|
# 2D keyboard navigation in grids (lvgl_grid_navigation_add())
|
||||||
|
CONFIG_LV_USE_GRIDNAV=y
|
||||||
CONFIG_LV_USE_BUILTIN_MALLOC=n
|
CONFIG_LV_USE_BUILTIN_MALLOC=n
|
||||||
# Routes lv_malloc/realloc/free through Modules/lvgl-module/source/lv_mem_custom.c, which prefers
|
# Routes lv_malloc/realloc/free through Modules/lvgl-module/source/lv_mem_custom.c, which prefers
|
||||||
# PSRAM (falls back to internal RAM automatically) instead of a fixed-size pool or plain malloc.
|
# PSRAM (falls back to internal RAM automatically) instead of a fixed-size pool or plain malloc.
|
||||||
|
|||||||
@ -103,7 +103,7 @@ static error_t start(Device* device) {
|
|||||||
|
|
||||||
error_t error = acquire_button(
|
error_t error = acquire_button(
|
||||||
config->pin_primary,
|
config->pin_primary,
|
||||||
two_button_mode ? (uint32_t)CODEPOINT_ENTER : (uint32_t)CODEPOINT_ARROW_DOWN,
|
two_button_mode ? (uint32_t)CODEPOINT_ENTER : (uint32_t)CODEPOINT_FOCUS_NEXT,
|
||||||
two_button_mode ? (uint32_t)CODEPOINT_ESCAPE : (uint32_t)CODEPOINT_ENTER,
|
two_button_mode ? (uint32_t)CODEPOINT_ESCAPE : (uint32_t)CODEPOINT_ENTER,
|
||||||
&internal->primary
|
&internal->primary
|
||||||
);
|
);
|
||||||
@ -112,7 +112,7 @@ static error_t start(Device* device) {
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = acquire_button(config->pin_secondary, CODEPOINT_ARROW_DOWN, CODEPOINT_ARROW_UP, &internal->secondary);
|
error = acquire_button(config->pin_secondary, CODEPOINT_FOCUS_NEXT, CODEPOINT_FOCUS_PREVIOUS, &internal->secondary);
|
||||||
if (error != ERROR_NONE) {
|
if (error != ERROR_NONE) {
|
||||||
if (internal->primary.in_use) {
|
if (internal->primary.in_use) {
|
||||||
gpio_descriptor_release(internal->primary.descriptor);
|
gpio_descriptor_release(internal->primary.descriptor);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
description: >
|
description: >
|
||||||
GPIO-attached rotary encoder wheel - a 2-phase quadrature encoder (decoded via the ESP32
|
GPIO-attached rotary encoder wheel - a 2-phase quadrature encoder (decoded via the ESP32
|
||||||
hardware PCNT peripheral) plus an optional separate click/enter button. Exposes a
|
hardware PCNT peripheral) plus an optional separate click/enter button. Exposes a
|
||||||
KEYBOARD_TYPE device: each wheel detent is translated to an arrow up/down key, and the
|
KEYBOARD_TYPE device: each wheel detent is translated to a focus next/previous key, and the
|
||||||
button (if present) press/release is translated to the enter key.
|
button (if present) press/release is translated to the enter key.
|
||||||
|
|
||||||
compatible: "tactility,gpio-encoder"
|
compatible: "tactility,gpio-encoder"
|
||||||
|
|||||||
@ -262,7 +262,7 @@ static void poll_wheel(GpioEncoderInternal* internal) {
|
|||||||
int32_t detents = total / internal->pulses_per_detent;
|
int32_t detents = total / internal->pulses_per_detent;
|
||||||
internal->pulse_remainder = total % internal->pulses_per_detent;
|
internal->pulse_remainder = total % internal->pulses_per_detent;
|
||||||
|
|
||||||
uint32_t key = detents >= 0 ? CODEPOINT_ARROW_DOWN : CODEPOINT_ARROW_UP;
|
uint32_t key = detents >= 0 ? CODEPOINT_FOCUS_NEXT : CODEPOINT_FOCUS_PREVIOUS;
|
||||||
int32_t count = detents >= 0 ? detents : -detents;
|
int32_t count = detents >= 0 ? detents : -detents;
|
||||||
for (int32_t i = 0; i < count; i++) {
|
for (int32_t i = 0; i < count; i++) {
|
||||||
// A press without its matching release would leave the consumer thinking the key
|
// A press without its matching release would leave the consumer thinking the key
|
||||||
|
|||||||
45
Modules/lvgl-module/include/lvgl/grid_navigation.h
Normal file
45
Modules/lvgl-module/include/lvgl/grid_navigation.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lets the arrow keys move the focus between the container's children by their position, e.g. in a grid of buttons.
|
||||||
|
* The container takes the place of its children in the focus group. Moving past the first or last row leaves the container.
|
||||||
|
* @param[in] container the container with the focusable children
|
||||||
|
*/
|
||||||
|
void lvgl_grid_navigation_add(lv_obj_t* container);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Turns grid navigation off again: the focusable children rejoin the container's focus group.
|
||||||
|
* @param[in] container a container that was passed to lvgl_grid_navigation_add()
|
||||||
|
*/
|
||||||
|
void lvgl_grid_navigation_remove(lv_obj_t* container);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Moves the focus to the next or previous child in their order, for devices that can only step through widgets.
|
||||||
|
* Past the last or first child, the focus moves to the next or previous object in the container's focus group.
|
||||||
|
* @param[in] container a container that was passed to lvgl_grid_navigation_add()
|
||||||
|
* @param[in] next true for the next child, false for the previous child
|
||||||
|
*/
|
||||||
|
void lvgl_grid_navigation_focus_next(lv_obj_t* container, bool next);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param[in] container a container that was passed to lvgl_grid_navigation_add()
|
||||||
|
* @return the focused child, or NULL when no child is focused
|
||||||
|
*/
|
||||||
|
lv_obj_t* lvgl_grid_navigation_get_focused(lv_obj_t* container);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param[in] obj the object to check
|
||||||
|
* @return true when the object is a container that was passed to lvgl_grid_navigation_add()
|
||||||
|
*/
|
||||||
|
bool lvgl_grid_navigation_is_container(const lv_obj_t* obj);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
#include <lvgl/devices/keyboard.h>
|
#include <lvgl/devices/keyboard.h>
|
||||||
#include <lvgl/devices/device_context.h>
|
#include <lvgl/devices/device_context.h>
|
||||||
|
#include <lvgl/grid_navigation.h>
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
|
||||||
#include <tactility/drivers/keyboard.h>
|
#include <tactility/drivers/keyboard.h>
|
||||||
@ -55,6 +56,8 @@ static uint32_t codepoint_to_lv_key(uint32_t key) {
|
|||||||
case CODEPOINT_ARROW_UP: return LV_KEY_PREV;
|
case CODEPOINT_ARROW_UP: return LV_KEY_PREV;
|
||||||
case CODEPOINT_ARROW_RIGHT: return LV_KEY_RIGHT;
|
case CODEPOINT_ARROW_RIGHT: return LV_KEY_RIGHT;
|
||||||
case CODEPOINT_ARROW_DOWN: return LV_KEY_NEXT;
|
case CODEPOINT_ARROW_DOWN: return LV_KEY_NEXT;
|
||||||
|
case CODEPOINT_FOCUS_NEXT: return LV_KEY_NEXT;
|
||||||
|
case CODEPOINT_FOCUS_PREVIOUS: return LV_KEY_PREV;
|
||||||
case CODEPOINT_HOME: return LV_KEY_HOME;
|
case CODEPOINT_HOME: return LV_KEY_HOME;
|
||||||
case CODEPOINT_END: return LV_KEY_END;
|
case CODEPOINT_END: return LV_KEY_END;
|
||||||
default: return key;
|
default: return key;
|
||||||
@ -72,6 +75,25 @@ static void lvgl_keyboard_read_cb(lv_indev_t* indev, lv_indev_data_t* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data->key = codepoint_to_lv_key(key_data.key);
|
data->key = codepoint_to_lv_key(key_data.key);
|
||||||
|
lv_group_t* group = lv_indev_get_group(indev);
|
||||||
|
lv_obj_t* focused = group != nullptr ? lv_group_get_focused(group) : nullptr;
|
||||||
|
if (lvgl_grid_navigation_is_container(focused)) {
|
||||||
|
if (key_data.key == CODEPOINT_ARROW_UP) {
|
||||||
|
// Grid navigation moves the focus in two dimensions with the arrow keys
|
||||||
|
data->key = LV_KEY_UP;
|
||||||
|
} else if (key_data.key == CODEPOINT_ARROW_DOWN) {
|
||||||
|
data->key = LV_KEY_DOWN;
|
||||||
|
} else if (key_data.key == CODEPOINT_FOCUS_NEXT || key_data.key == CODEPOINT_FOCUS_PREVIOUS) {
|
||||||
|
// Devices that can only step through widgets visit the children in their order.
|
||||||
|
// LVGL doesn't see these keys: the group would skip the children, as only the container is in the group.
|
||||||
|
if (key_data.pressed) {
|
||||||
|
lvgl_grid_navigation_focus_next(focused, key_data.key == CODEPOINT_FOCUS_NEXT);
|
||||||
|
}
|
||||||
|
data->state = LV_INDEV_STATE_RELEASED;
|
||||||
|
data->continue_reading = key_data.continue_reading;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
data->state = key_data.pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
data->state = key_data.pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||||
data->continue_reading = key_data.continue_reading;
|
data->continue_reading = key_data.continue_reading;
|
||||||
}
|
}
|
||||||
|
|||||||
107
Modules/lvgl-module/source/grid_navigation.cpp
Normal file
107
Modules/lvgl-module/source/grid_navigation.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#include <lvgl/grid_navigation.h>
|
||||||
|
|
||||||
|
// Marks containers with grid navigation, so the keyboard sends them up and down keys
|
||||||
|
constexpr lv_obj_flag_t GRID_NAVIGATION_FLAG = LV_OBJ_FLAG_USER_1;
|
||||||
|
|
||||||
|
static void remove_from_group(lv_obj_t* child) {
|
||||||
|
if (lv_obj_get_group(child) != nullptr) {
|
||||||
|
lv_group_remove_obj(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Children join the default group when they're created, but grid navigation focuses them through the container
|
||||||
|
static void on_child_created(lv_event_t* event) {
|
||||||
|
auto* child = static_cast<lv_obj_t*>(lv_event_get_param(event));
|
||||||
|
auto* container = lv_event_get_current_target_obj(event);
|
||||||
|
if (child != nullptr && lv_obj_get_parent(child) == container) {
|
||||||
|
remove_from_group(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lvgl_grid_navigation_add(lv_obj_t* container) {
|
||||||
|
lv_gridnav_add(container, LV_GRIDNAV_CTRL_NONE);
|
||||||
|
lv_obj_add_flag(container, GRID_NAVIGATION_FLAG);
|
||||||
|
|
||||||
|
const uint32_t child_count = lv_obj_get_child_count(container);
|
||||||
|
for (uint32_t i = 0; i < child_count; i++) {
|
||||||
|
remove_from_group(lv_obj_get_child(container, static_cast<int32_t>(i)));
|
||||||
|
}
|
||||||
|
lv_obj_add_event_cb(container, on_child_created, LV_EVENT_CHILD_CREATED, nullptr);
|
||||||
|
|
||||||
|
auto* group = lv_group_get_default();
|
||||||
|
if (group != nullptr && lv_obj_get_group(container) == nullptr) {
|
||||||
|
lv_group_add_obj(group, container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lvgl_grid_navigation_remove(lv_obj_t* container) {
|
||||||
|
if (!lvgl_grid_navigation_is_container(container)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lv_obj_t* focused_child = lvgl_grid_navigation_get_focused(container);
|
||||||
|
lv_group_t* group = lv_obj_get_group(container);
|
||||||
|
const bool container_focused = group != nullptr && lv_group_get_focused(group) == container;
|
||||||
|
|
||||||
|
lv_gridnav_remove(container);
|
||||||
|
lv_obj_remove_event_cb(container, on_child_created);
|
||||||
|
lv_obj_remove_flag(container, GRID_NAVIGATION_FLAG);
|
||||||
|
|
||||||
|
if (group == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The children are added at the end of the group
|
||||||
|
const uint32_t child_count = lv_obj_get_child_count(container);
|
||||||
|
for (uint32_t i = 0; i < child_count; i++) {
|
||||||
|
auto* child = lv_obj_get_child(container, static_cast<int32_t>(i));
|
||||||
|
if (lv_obj_is_group_def(child) && lv_obj_get_group(child) == nullptr) {
|
||||||
|
lv_group_add_obj(group, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (container_focused && focused_child != nullptr) {
|
||||||
|
lv_group_focus_obj(focused_child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches which children grid navigation can focus
|
||||||
|
static bool is_focusable(lv_obj_t* obj) {
|
||||||
|
return !lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN) && lv_obj_has_flag_any(obj, static_cast<lv_obj_flag_t>(LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_CLICK_FOCUSABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void lvgl_grid_navigation_focus_next(lv_obj_t* container, bool next) {
|
||||||
|
const auto child_count = static_cast<int32_t>(lv_obj_get_child_count(container));
|
||||||
|
lv_obj_t* focused = lvgl_grid_navigation_get_focused(container);
|
||||||
|
int32_t index = focused != nullptr ? lv_obj_get_index(focused) : (next ? -1 : child_count);
|
||||||
|
for (index += next ? 1 : -1; index >= 0 && index < child_count; index += next ? 1 : -1) {
|
||||||
|
auto* child = lv_obj_get_child(container, index);
|
||||||
|
if (is_focusable(child)) {
|
||||||
|
lv_gridnav_set_focused(container, child, LV_ANIM_ON);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_group_t* group = lv_obj_get_group(container);
|
||||||
|
if (group != nullptr) {
|
||||||
|
if (next) {
|
||||||
|
lv_group_focus_next(group);
|
||||||
|
} else {
|
||||||
|
lv_group_focus_prev(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_t* lvgl_grid_navigation_get_focused(lv_obj_t* container) {
|
||||||
|
// Grid navigation marks the focused child with the focused state
|
||||||
|
const uint32_t child_count = lv_obj_get_child_count(container);
|
||||||
|
for (uint32_t i = 0; i < child_count; i++) {
|
||||||
|
auto* child = lv_obj_get_child(container, static_cast<int32_t>(i));
|
||||||
|
if (lv_obj_has_state(child, LV_STATE_FOCUSED)) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lvgl_grid_navigation_is_container(const lv_obj_t* obj) {
|
||||||
|
return obj != nullptr && lv_obj_has_flag(obj, GRID_NAVIGATION_FLAG);
|
||||||
|
}
|
||||||
@ -10,6 +10,7 @@
|
|||||||
#include <lvgl/devices/pointer.h>
|
#include <lvgl/devices/pointer.h>
|
||||||
|
|
||||||
#include <lvgl/widgets/sliderbox.h>
|
#include <lvgl/widgets/sliderbox.h>
|
||||||
|
#include <lvgl/grid_navigation.h>
|
||||||
#include <lvgl/theme.h>
|
#include <lvgl/theme.h>
|
||||||
#include <lvgl/widgets/badge.h>
|
#include <lvgl/widgets/badge.h>
|
||||||
#include <lvgl/widgets/card.h>
|
#include <lvgl/widgets/card.h>
|
||||||
@ -61,6 +62,12 @@ const struct ModuleSymbol lvgl_module_symbols[] = {
|
|||||||
DEFINE_MODULE_SYMBOL(lvgl_pointer_add),
|
DEFINE_MODULE_SYMBOL(lvgl_pointer_add),
|
||||||
DEFINE_MODULE_SYMBOL(lvgl_pointer_get_slot_index),
|
DEFINE_MODULE_SYMBOL(lvgl_pointer_get_slot_index),
|
||||||
DEFINE_MODULE_SYMBOL(lvgl_pointer_remove),
|
DEFINE_MODULE_SYMBOL(lvgl_pointer_remove),
|
||||||
|
// lvgl_grid_navigation
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_grid_navigation_add),
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_grid_navigation_remove),
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_grid_navigation_focus_next),
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_grid_navigation_get_focused),
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_grid_navigation_is_container),
|
||||||
// lvgl_theme
|
// lvgl_theme
|
||||||
DEFINE_MODULE_SYMBOL(lvgl_theme_get_settings),
|
DEFINE_MODULE_SYMBOL(lvgl_theme_get_settings),
|
||||||
DEFINE_MODULE_SYMBOL(lvgl_theme_get_default_settings),
|
DEFINE_MODULE_SYMBOL(lvgl_theme_get_default_settings),
|
||||||
|
|||||||
@ -362,7 +362,6 @@ typedef struct {
|
|||||||
lv_style_t icon_button_tonal;
|
lv_style_t icon_button_tonal;
|
||||||
lv_style_t icon_button_tonal_pressed;
|
lv_style_t icon_button_tonal_pressed;
|
||||||
lv_style_t app_bar;
|
lv_style_t app_bar;
|
||||||
lv_style_t app_bar_actions;
|
|
||||||
lv_style_t app_bar_button;
|
lv_style_t app_bar_button;
|
||||||
lv_style_t app_bar_button_pressed;
|
lv_style_t app_bar_button_pressed;
|
||||||
lv_style_t app_bar_focus_ring;
|
lv_style_t app_bar_focus_ring;
|
||||||
@ -866,13 +865,6 @@ static void style_init(my_theme_t * theme)
|
|||||||
lv_style_set_pad_ver(&theme->styles.app_bar, 0);
|
lv_style_set_pad_ver(&theme->styles.app_bar, 0);
|
||||||
lv_style_set_pad_gap(&theme->styles.app_bar, APP_BAR_GAP);
|
lv_style_set_pad_gap(&theme->styles.app_bar, APP_BAR_GAP);
|
||||||
|
|
||||||
style_init_reset(&theme->styles.app_bar_actions);
|
|
||||||
lv_style_set_bg_opa(&theme->styles.app_bar_actions, LV_OPA_TRANSP);
|
|
||||||
lv_style_set_border_width(&theme->styles.app_bar_actions, 0);
|
|
||||||
lv_style_set_radius(&theme->styles.app_bar_actions, 0);
|
|
||||||
lv_style_set_pad_all(&theme->styles.app_bar_actions, 0);
|
|
||||||
lv_style_set_pad_gap(&theme->styles.app_bar_actions, APP_BAR_GAP);
|
|
||||||
|
|
||||||
style_init_reset(&theme->styles.app_bar_button);
|
style_init_reset(&theme->styles.app_bar_button);
|
||||||
lv_style_set_pad_all(&theme->styles.app_bar_button, 0);
|
lv_style_set_pad_all(&theme->styles.app_bar_button, 0);
|
||||||
lv_style_set_text_color(&theme->styles.app_bar_button, APP_BAR_CONTENT_COLOR);
|
lv_style_set_text_color(&theme->styles.app_bar_button, APP_BAR_CONTENT_COLOR);
|
||||||
@ -1617,11 +1609,6 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* toolbar > obj */
|
|
||||||
if(lv_obj_check_type(parent, &lvgl_toolbar_class)) {
|
|
||||||
lv_obj_add_style(obj, &theme->styles.app_bar_actions, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* obj */
|
/* obj */
|
||||||
lv_obj_add_style(obj, &theme->styles.container, 0);
|
lv_obj_add_style(obj, &theme->styles.container, 0);
|
||||||
lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR);
|
lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR);
|
||||||
@ -1705,18 +1692,6 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
|||||||
lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED);
|
lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* toolbar > obj > icon_button */
|
|
||||||
if(lv_obj_check_type(lss_ancestor(obj, 2), &lvgl_toolbar_class) && lv_obj_check_type(parent, &lv_obj_class)) {
|
|
||||||
lv_obj_add_style(obj, &theme->styles.icon_button, 0);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.app_bar_button, 0);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.transition_exit, 0);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.app_bar_button_pressed, LV_STATE_PRESSED);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.app_bar_focus_ring, LV_STATE_FOCUS_KEY);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.app_bar_focus_ring, LV_STATE_EDITED);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* icon_button */
|
/* icon_button */
|
||||||
lv_obj_add_style(obj, &theme->styles.icon_button, 0);
|
lv_obj_add_style(obj, &theme->styles.icon_button, 0);
|
||||||
lv_obj_add_style(obj, &theme->styles.transition_exit, 0);
|
lv_obj_add_style(obj, &theme->styles.transition_exit, 0);
|
||||||
@ -1729,8 +1704,8 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
|||||||
}
|
}
|
||||||
#if LV_USE_DROPDOWN
|
#if LV_USE_DROPDOWN
|
||||||
if(lv_obj_check_type(obj, &lv_dropdown_class)) {
|
if(lv_obj_check_type(obj, &lv_dropdown_class)) {
|
||||||
/* toolbar > obj > dropdown */
|
/* toolbar > dropdown */
|
||||||
if(lv_obj_check_type(lss_ancestor(obj, 2), &lvgl_toolbar_class) && lv_obj_check_type(parent, &lv_obj_class)) {
|
if(lv_obj_check_type(parent, &lvgl_toolbar_class)) {
|
||||||
lv_obj_add_style(obj, &theme->styles.field, 0);
|
lv_obj_add_style(obj, &theme->styles.field, 0);
|
||||||
lv_obj_add_style(obj, &theme->styles.transition_exit, 0);
|
lv_obj_add_style(obj, &theme->styles.transition_exit, 0);
|
||||||
lv_obj_add_style(obj, &theme->styles.state_pressed, LV_STATE_PRESSED);
|
lv_obj_add_style(obj, &theme->styles.state_pressed, LV_STATE_PRESSED);
|
||||||
|
|||||||
@ -226,7 +226,6 @@ typedef struct {
|
|||||||
lv_style_t icon_button_filled_pressed;
|
lv_style_t icon_button_filled_pressed;
|
||||||
lv_style_t icon_button_tonal;
|
lv_style_t icon_button_tonal;
|
||||||
lv_style_t app_bar;
|
lv_style_t app_bar;
|
||||||
lv_style_t app_bar_actions;
|
|
||||||
lv_style_t app_bar_button;
|
lv_style_t app_bar_button;
|
||||||
lv_style_t app_bar_focus_ring;
|
lv_style_t app_bar_focus_ring;
|
||||||
#if LV_USE_SWITCH
|
#if LV_USE_SWITCH
|
||||||
@ -620,13 +619,6 @@ static void style_init(my_theme_t * theme)
|
|||||||
lv_style_set_pad_ver(&theme->styles.app_bar, 0);
|
lv_style_set_pad_ver(&theme->styles.app_bar, 0);
|
||||||
lv_style_set_pad_gap(&theme->styles.app_bar, APP_BAR_GAP);
|
lv_style_set_pad_gap(&theme->styles.app_bar, APP_BAR_GAP);
|
||||||
|
|
||||||
style_init_reset(&theme->styles.app_bar_actions);
|
|
||||||
lv_style_set_bg_opa(&theme->styles.app_bar_actions, LV_OPA_TRANSP);
|
|
||||||
lv_style_set_border_width(&theme->styles.app_bar_actions, 0);
|
|
||||||
lv_style_set_radius(&theme->styles.app_bar_actions, 0);
|
|
||||||
lv_style_set_pad_all(&theme->styles.app_bar_actions, 0);
|
|
||||||
lv_style_set_pad_gap(&theme->styles.app_bar_actions, APP_BAR_GAP);
|
|
||||||
|
|
||||||
style_init_reset(&theme->styles.app_bar_button);
|
style_init_reset(&theme->styles.app_bar_button);
|
||||||
lv_style_set_pad_all(&theme->styles.app_bar_button, 0);
|
lv_style_set_pad_all(&theme->styles.app_bar_button, 0);
|
||||||
|
|
||||||
@ -1268,11 +1260,6 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* toolbar > obj */
|
|
||||||
if(lv_obj_check_type(parent, &lvgl_toolbar_class)) {
|
|
||||||
lv_obj_add_style(obj, &theme->styles.app_bar_actions, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* obj */
|
/* obj */
|
||||||
lv_obj_add_style(obj, &theme->styles.container, 0);
|
lv_obj_add_style(obj, &theme->styles.container, 0);
|
||||||
lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR);
|
lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR);
|
||||||
@ -1339,16 +1326,6 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
|||||||
lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED);
|
lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* toolbar > obj > icon_button */
|
|
||||||
if(lv_obj_check_type(lss_ancestor(obj, 2), &lvgl_toolbar_class) && lv_obj_check_type(parent, &lv_obj_class)) {
|
|
||||||
lv_obj_add_style(obj, &theme->styles.icon_button, 0);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.app_bar_button, 0);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.app_bar_focus_ring, LV_STATE_FOCUS_KEY);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.app_bar_focus_ring, LV_STATE_EDITED);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* icon_button */
|
/* icon_button */
|
||||||
lv_obj_add_style(obj, &theme->styles.icon_button, 0);
|
lv_obj_add_style(obj, &theme->styles.icon_button, 0);
|
||||||
lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED);
|
lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED);
|
||||||
|
|||||||
@ -4,12 +4,16 @@
|
|||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
|
||||||
#include <lvgl/fonts.h>
|
#include <lvgl/fonts.h>
|
||||||
|
#include <lvgl/grid_navigation.h>
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <lvgl/widgets/icon_button.h>
|
#include <lvgl/widgets/icon_button.h>
|
||||||
#include <lvgl/widgets/spinner.h>
|
#include <lvgl/widgets/spinner.h>
|
||||||
|
|
||||||
#include <tactility/check.h>
|
#include <tactility/check.h>
|
||||||
#include <tactility/drivers/pointer.h>
|
#include <tactility/drivers/pointer.h>
|
||||||
|
#include <tactility/log.h>
|
||||||
|
|
||||||
|
constexpr auto* TAG = "lvgl_toolbar";
|
||||||
|
|
||||||
static uint32_t getToolbarHeight(UiDensity uiDensity) {
|
static uint32_t getToolbarHeight(UiDensity uiDensity) {
|
||||||
if (uiDensity == LVGL_UI_DENSITY_COMPACT) {
|
if (uiDensity == LVGL_UI_DENSITY_COMPACT) {
|
||||||
@ -39,7 +43,6 @@ typedef struct {
|
|||||||
lv_obj_t* title_label;
|
lv_obj_t* title_label;
|
||||||
lv_obj_t* close_button;
|
lv_obj_t* close_button;
|
||||||
lv_obj_t* close_button_image;
|
lv_obj_t* close_button_image;
|
||||||
lv_obj_t* action_container;
|
|
||||||
uint8_t action_count;
|
uint8_t action_count;
|
||||||
lv_event_cb_t nav_action_callback;
|
lv_event_cb_t nav_action_callback;
|
||||||
} Toolbar;
|
} Toolbar;
|
||||||
@ -99,6 +102,8 @@ lv_obj_t* lvgl_toolbar_create(lv_obj_t* parent, const char* title) {
|
|||||||
lv_obj_center(obj);
|
lv_obj_center(obj);
|
||||||
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
|
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
|
||||||
lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||||
|
// The arrow keys move between the close button and the actions, and up and down leave the toolbar
|
||||||
|
lvgl_grid_navigation_add(obj);
|
||||||
|
|
||||||
toolbar->close_button = create_action_button(obj, ui_density);
|
toolbar->close_button = create_action_button(obj, ui_density);
|
||||||
toolbar->close_button_image = lv_image_create(toolbar->close_button);
|
toolbar->close_button_image = lv_image_create(toolbar->close_button);
|
||||||
@ -110,11 +115,6 @@ lv_obj_t* lvgl_toolbar_create(lv_obj_t* parent, const char* title) {
|
|||||||
lv_label_set_long_mode(toolbar->title_label, LV_LABEL_LONG_MODE_SCROLL);
|
lv_label_set_long_mode(toolbar->title_label, LV_LABEL_LONG_MODE_SCROLL);
|
||||||
lv_obj_set_flex_grow(toolbar->title_label, 1);
|
lv_obj_set_flex_grow(toolbar->title_label, 1);
|
||||||
|
|
||||||
toolbar->action_container = lv_obj_create(obj);
|
|
||||||
lv_obj_set_size(toolbar->action_container, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
|
||||||
lv_obj_set_flex_flow(toolbar->action_container, LV_FLEX_FLOW_ROW);
|
|
||||||
lv_obj_set_flex_align(toolbar->action_container, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
|
||||||
|
|
||||||
lvgl_toolbar_set_nav_action(obj, LV_SYMBOL_CLOSE, &default_nav_action, nullptr);
|
lvgl_toolbar_set_nav_action(obj, LV_SYMBOL_CLOSE, &default_nav_action, nullptr);
|
||||||
|
|
||||||
// If we don't have a touch device, we assume there's some other kind of input like a keyboard, an encoder or button control
|
// If we don't have a touch device, we assume there's some other kind of input like a keyboard, an encoder or button control
|
||||||
@ -122,8 +122,8 @@ lv_obj_t* lvgl_toolbar_create(lv_obj_t* parent, const char* title) {
|
|||||||
// an extra time for every screen.
|
// an extra time for every screen.
|
||||||
if (!device_has_active_by_type(&POINTER_TYPE)) {
|
if (!device_has_active_by_type(&POINTER_TYPE)) {
|
||||||
lv_obj_update_layout(obj); // Resolve flex layout first, so focus/state invalidate against final coords
|
lv_obj_update_layout(obj); // Resolve flex layout first, so focus/state invalidate against final coords
|
||||||
lv_group_focus_obj(toolbar->close_button);
|
lv_group_focus_obj(obj);
|
||||||
lv_obj_add_state(toolbar->close_button, LV_STATE_FOCUS_KEY);
|
lv_gridnav_set_focused(obj, toolbar->close_button, LV_ANIM_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
@ -151,7 +151,7 @@ static lv_obj_t* toolbar_add_button_action(lv_obj_t* obj, const char* imageOrBut
|
|||||||
check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
||||||
toolbar->action_count++;
|
toolbar->action_count++;
|
||||||
|
|
||||||
lv_obj_t* action_button = create_action_button(toolbar->action_container, lvgl_get_ui_density());
|
lv_obj_t* action_button = create_action_button(obj, lvgl_get_ui_density());
|
||||||
lv_obj_add_event_cb(action_button, callback, LV_EVENT_SHORT_CLICKED, user_data);
|
lv_obj_add_event_cb(action_button, callback, LV_EVENT_SHORT_CLICKED, user_data);
|
||||||
lv_obj_t* button_content;
|
lv_obj_t* button_content;
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
@ -179,7 +179,7 @@ lv_obj_t* lvgl_toolbar_add_switch_action(lv_obj_t* obj) {
|
|||||||
check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
||||||
toolbar->action_count++;
|
toolbar->action_count++;
|
||||||
|
|
||||||
return lv_switch_create(toolbar->action_container);
|
return lv_switch_create(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_t* lvgl_toolbar_add_dropdown_action(lv_obj_t* obj, const char* options, lv_coord_t width, const char* text) {
|
lv_obj_t* lvgl_toolbar_add_dropdown_action(lv_obj_t* obj, const char* options, lv_coord_t width, const char* text) {
|
||||||
@ -187,7 +187,11 @@ lv_obj_t* lvgl_toolbar_add_dropdown_action(lv_obj_t* obj, const char* options, l
|
|||||||
check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
||||||
toolbar->action_count++;
|
toolbar->action_count++;
|
||||||
|
|
||||||
lv_obj_t* widget = lv_dropdown_create(toolbar->action_container);
|
// The arrow keys can't both navigate the toolbar and the opened dropdown
|
||||||
|
LOG_W(TAG, "Dropdown actions are deprecated: the toolbar can't be navigated with the arrow keys when it has one");
|
||||||
|
lvgl_grid_navigation_remove(obj);
|
||||||
|
|
||||||
|
lv_obj_t* widget = lv_dropdown_create(obj);
|
||||||
lv_dropdown_set_options(widget, options);
|
lv_dropdown_set_options(widget, options);
|
||||||
lv_dropdown_set_selected_highlight(widget, false);
|
lv_dropdown_set_selected_highlight(widget, false);
|
||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
@ -205,7 +209,7 @@ lv_obj_t* lvgl_toolbar_add_spinner_action(lv_obj_t* obj) {
|
|||||||
check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
check(toolbar->action_count < TOOLBAR_ACTION_LIMIT, "max actions reached");
|
||||||
toolbar->action_count++;
|
toolbar->action_count++;
|
||||||
|
|
||||||
auto* spinner = lvgl_spinner_create(toolbar->action_container);
|
auto* spinner = lvgl_spinner_create(obj);
|
||||||
|
|
||||||
if (lv_display_get_color_format(lv_obj_get_display(obj)) == LV_COLOR_FORMAT_L8) {
|
if (lv_display_get_color_format(lv_obj_get_display(obj)) == LV_COLOR_FORMAT_L8) {
|
||||||
lv_obj_set_style_image_recolor(spinner, lv_theme_get_color_secondary(obj), LV_STATE_DEFAULT);
|
lv_obj_set_style_image_recolor(spinner, lv_theme_get_color_secondary(obj), LV_STATE_DEFAULT);
|
||||||
@ -217,6 +221,9 @@ lv_obj_t* lvgl_toolbar_add_spinner_action(lv_obj_t* obj) {
|
|||||||
|
|
||||||
void lvgl_toolbar_clear_actions(lv_obj_t* obj) {
|
void lvgl_toolbar_clear_actions(lv_obj_t* obj) {
|
||||||
auto* toolbar = reinterpret_cast<Toolbar*>(obj);
|
auto* toolbar = reinterpret_cast<Toolbar*>(obj);
|
||||||
lv_obj_clean(toolbar->action_container);
|
// The actions follow the close button and the title
|
||||||
|
while (lv_obj_get_child_count(obj) > 2) {
|
||||||
|
lv_obj_delete(lv_obj_get_child(obj, 2));
|
||||||
|
}
|
||||||
toolbar->action_count = 0;
|
toolbar->action_count = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -676,14 +676,6 @@ app_bar {
|
|||||||
padGap: @app-bar-gap;
|
padGap: @app-bar-gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
app_bar_actions {
|
|
||||||
bgOpa: transp;
|
|
||||||
borderWidth: 0;
|
|
||||||
radius: 0;
|
|
||||||
padAll: 0;
|
|
||||||
padGap: @app-bar-gap;
|
|
||||||
}
|
|
||||||
|
|
||||||
app_bar_button {
|
app_bar_button {
|
||||||
padAll: 0;
|
padAll: 0;
|
||||||
textColor: @app-bar-content-color;
|
textColor: @app-bar-content-color;
|
||||||
@ -1236,10 +1228,6 @@ apply(calendar > obj) {
|
|||||||
// No styles
|
// No styles
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(toolbar > obj) {
|
|
||||||
app_bar_actions: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
apply(obj) {
|
apply(obj) {
|
||||||
container: default;
|
container: default;
|
||||||
scrollbar: part.scrollbar;
|
scrollbar: part.scrollbar;
|
||||||
@ -1279,7 +1267,7 @@ apply(button) {
|
|||||||
button_disabled: state.disabled;
|
button_disabled: state.disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(toolbar > icon_button, toolbar > obj > icon_button) {
|
apply(toolbar > icon_button) {
|
||||||
icon_button: default;
|
icon_button: default;
|
||||||
app_bar_button: default;
|
app_bar_button: default;
|
||||||
transition_exit: default;
|
transition_exit: default;
|
||||||
@ -1290,7 +1278,7 @@ apply(toolbar > icon_button, toolbar > obj > icon_button) {
|
|||||||
disabled: state.disabled;
|
disabled: state.disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(toolbar > obj > dropdown) {
|
apply(toolbar > dropdown) {
|
||||||
@include dropdown;
|
@include dropdown;
|
||||||
app_bar_field: default;
|
app_bar_field: default;
|
||||||
app_bar_focus_ring: state.focusKey;
|
app_bar_focus_ring: state.focusKey;
|
||||||
|
|||||||
@ -470,14 +470,6 @@ app_bar {
|
|||||||
padGap: @app-bar-gap;
|
padGap: @app-bar-gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
app_bar_actions {
|
|
||||||
bgOpa: transp;
|
|
||||||
borderWidth: 0;
|
|
||||||
radius: 0;
|
|
||||||
padAll: 0;
|
|
||||||
padGap: @app-bar-gap;
|
|
||||||
}
|
|
||||||
|
|
||||||
app_bar_button {
|
app_bar_button {
|
||||||
padAll: 0;
|
padAll: 0;
|
||||||
}
|
}
|
||||||
@ -944,10 +936,6 @@ apply(calendar > obj) {
|
|||||||
// No styles
|
// No styles
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(toolbar > obj) {
|
|
||||||
app_bar_actions: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
apply(obj) {
|
apply(obj) {
|
||||||
container: default;
|
container: default;
|
||||||
scrollbar: part.scrollbar;
|
scrollbar: part.scrollbar;
|
||||||
@ -963,7 +951,7 @@ apply(tabview > *[0] > button) {
|
|||||||
edit_ring: state.edited;
|
edit_ring: state.edited;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(toolbar > icon_button, toolbar > obj > icon_button) {
|
apply(toolbar > icon_button) {
|
||||||
icon_button: default;
|
icon_button: default;
|
||||||
app_bar_button: default;
|
app_bar_button: default;
|
||||||
inverted: state.pressed;
|
inverted: state.pressed;
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <lvgl/devices/indev.h>
|
#include <lvgl/devices/indev.h>
|
||||||
#include <lvgl/fonts.h>
|
#include <lvgl/fonts.h>
|
||||||
|
#include <lvgl/grid_navigation.h>
|
||||||
#include <lvgl/widgets/badge.h>
|
#include <lvgl/widgets/badge.h>
|
||||||
#include <lvgl/widgets/icon_button.h>
|
#include <lvgl/widgets/icon_button.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
@ -104,13 +105,14 @@ void AppGrid::requestRepopulate(bool keepSelection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AppGrid::populate() {
|
void AppGrid::populate() {
|
||||||
// Captured before lv_obj_clean() deletes the currently focused tile below: LVGL moves
|
// Captured before lv_obj_clean() deletes the currently focused tile below: creating
|
||||||
// focus elsewhere as it leaves the group, and creating replacement tiles doesn't restore
|
// replacement tiles doesn't restore the focus, so the app id is remembered here and
|
||||||
// it, so the app id is remembered here and re-focused on its new tile once rebuilt.
|
// re-focused on its new tile once rebuilt.
|
||||||
std::string focusedAppId;
|
std::string focusedAppId;
|
||||||
lv_group_t* group = lv_group_get_default();
|
lv_group_t* group = lv_group_get_default();
|
||||||
if (group != nullptr && keepSelectionOnRepopulate) {
|
if (group != nullptr && keepSelectionOnRepopulate) {
|
||||||
lv_obj_t* focused = lv_group_get_focused(group);
|
// Grid navigation focuses the grid in the group, and a tile within the grid
|
||||||
|
lv_obj_t* focused = lv_group_get_focused(group) == grid ? lvgl_grid_navigation_get_focused(grid) : nullptr;
|
||||||
if (focused != nullptr && lv_obj_get_parent(focused) == grid) {
|
if (focused != nullptr && lv_obj_get_parent(focused) == grid) {
|
||||||
focusedAppId = static_cast<const AppGridItem*>(lv_obj_get_user_data(focused))->manifest.id;
|
focusedAppId = static_cast<const AppGridItem*>(lv_obj_get_user_data(focused))->manifest.id;
|
||||||
}
|
}
|
||||||
@ -193,8 +195,8 @@ void AppGrid::populate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (focusedTile != nullptr) {
|
if (focusedTile != nullptr) {
|
||||||
lv_group_focus_obj(focusedTile);
|
lv_group_focus_obj(grid);
|
||||||
lv_obj_add_state(focusedTile, LV_STATE_FOCUS_KEY);
|
lv_gridnav_set_focused(grid, focusedTile, LV_ANIM_OFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +213,8 @@ void AppGrid::createWidgets(lv_obj_t* parent, lv_obj_t* toolbar) {
|
|||||||
lv_obj_set_style_pad_column(grid, TILE_GAP, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_column(grid, TILE_GAP, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_border_width(grid, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_width(grid, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_remove_flag(grid, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_remove_flag(grid, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
// The arrow keys move between the tiles in rows and columns
|
||||||
|
lvgl_grid_navigation_add(grid);
|
||||||
|
|
||||||
// Resolves the grid's flex_grow height, which the page size is derived from.
|
// Resolves the grid's flex_grow height, which the page size is derived from.
|
||||||
lv_obj_update_layout(parent);
|
lv_obj_update_layout(parent);
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
#include <lvgl/icons/launcher.h>
|
#include <lvgl/icons/launcher.h>
|
||||||
#include <lvgl/fonts.h>
|
#include <lvgl/fonts.h>
|
||||||
|
#include <lvgl/grid_navigation.h>
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <lvgl/theme.h>
|
#include <lvgl/theme.h>
|
||||||
#include <lvgl/widgets/icon_button.h>
|
#include <lvgl/widgets/icon_button.h>
|
||||||
@ -158,6 +159,8 @@ void createWidgets(lv_obj_t* parent, void*) {
|
|||||||
createAppButton(buttons_wrapper, variant, LVGL_ICON_LAUNCHER_FOLDER, "tactility.files");
|
createAppButton(buttons_wrapper, variant, LVGL_ICON_LAUNCHER_FOLDER, "tactility.files");
|
||||||
createAppButton(buttons_wrapper, variant, LVGL_ICON_LAUNCHER_SETTINGS, "tactility.settings");
|
createAppButton(buttons_wrapper, variant, LVGL_ICON_LAUNCHER_SETTINGS, "tactility.settings");
|
||||||
applyButtonMargins(buttons_wrapper, is_landscape_display);
|
applyButtonMargins(buttons_wrapper, is_landscape_display);
|
||||||
|
// The arrow keys move between the buttons, in a row or a column
|
||||||
|
lvgl_grid_navigation_add(buttons_wrapper);
|
||||||
|
|
||||||
// The launcher's container is several levels below the screen, and LVGL only sends
|
// The launcher's container is several levels below the screen, and LVGL only sends
|
||||||
// LV_EVENT_SIZE_CHANGED to the screen object itself on a resolution change - so the
|
// LV_EVENT_SIZE_CHANGED to the screen object itself on a resolution change - so the
|
||||||
@ -182,8 +185,8 @@ void createWidgets(lv_obj_t* parent, void*) {
|
|||||||
// an extra time.
|
// an extra time.
|
||||||
if (!device_has_active_by_type(&POINTER_TYPE)) {
|
if (!device_has_active_by_type(&POINTER_TYPE)) {
|
||||||
// lv_obj_update_layout(parent); // Resolve flex layout first, so focus/state invalidate against final coords
|
// lv_obj_update_layout(parent); // Resolve flex layout first, so focus/state invalidate against final coords
|
||||||
lv_group_focus_obj(app_list_button);
|
lv_group_focus_obj(buttons_wrapper);
|
||||||
lv_obj_add_state(app_list_button, LV_STATE_FOCUS_KEY);
|
lv_gridnav_set_focused(buttons_wrapper, app_list_button, LV_ANIM_OFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,10 @@ typedef enum {
|
|||||||
CODEPOINT_ARROW_DOWN = 0x2193,
|
CODEPOINT_ARROW_DOWN = 0x2193,
|
||||||
CODEPOINT_HOME = 0x21F1,
|
CODEPOINT_HOME = 0x21F1,
|
||||||
CODEPOINT_END = 0x21F2,
|
CODEPOINT_END = 0x21F2,
|
||||||
|
/** Moves the focus to the next widget, for devices that can only step through widgets (e.g. an encoder wheel) */
|
||||||
|
CODEPOINT_FOCUS_NEXT = 0x21E5,
|
||||||
|
/** Moves the focus to the previous widget, for devices that can only step through widgets (e.g. an encoder wheel) */
|
||||||
|
CODEPOINT_FOCUS_PREVIOUS = 0x21E4,
|
||||||
} CodePoint;
|
} CodePoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -773,7 +773,7 @@
|
|||||||
#define LV_USE_MONKEY 0
|
#define LV_USE_MONKEY 0
|
||||||
|
|
||||||
/*1: Enable grid navigation*/
|
/*1: Enable grid navigation*/
|
||||||
#define LV_USE_GRIDNAV 0
|
#define LV_USE_GRIDNAV 1
|
||||||
|
|
||||||
/*1: Enable lv_obj fragment*/
|
/*1: Enable lv_obj fragment*/
|
||||||
#define LV_USE_FRAGMENT 0
|
#define LV_USE_FRAGMENT 0
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user