mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-10-17 02:45:05 +00:00
Compare commits
2 Commits
2522f5da71
...
0b5c302aa2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b5c302aa2 | ||
|
|
60133ddda6 |
@ -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.
|
||||||
|
|||||||
@ -22,5 +22,3 @@ lvgl.colorDepth=8
|
|||||||
lvgl.fontSize=24
|
lvgl.fontSize=24
|
||||||
lvgl.theme=Mono
|
lvgl.theme=Mono
|
||||||
lvgl.statusbarColorsInverted=true
|
lvgl.statusbarColorsInverted=true
|
||||||
|
|
||||||
sdkconfig.CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=0
|
|
||||||
|
|||||||
@ -23,11 +23,7 @@ display.shape=rectangle
|
|||||||
display.dpi=128
|
display.dpi=128
|
||||||
|
|
||||||
lvgl.colorDepth=8
|
lvgl.colorDepth=8
|
||||||
# Monochrome theme: the default colour theme renders accent-coloured UI that
|
|
||||||
# thresholds to invisible on a 1bpp panel (enables CONFIG_LV_USE_THEME_MONO).
|
|
||||||
lvgl.theme=Mono
|
lvgl.theme=Mono
|
||||||
lvgl.statusbarColorsInverted=true
|
lvgl.statusbarColorsInverted=true
|
||||||
|
|
||||||
cdn.warningMessage=Display not reliably working. Use at your own risk.
|
cdn.warningMessage=Display not reliably working. Use at your own risk.
|
||||||
|
|
||||||
sdkconfig.CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=0
|
|
||||||
|
|||||||
@ -22,11 +22,7 @@ display.shape=rectangle
|
|||||||
display.dpi=128
|
display.dpi=128
|
||||||
|
|
||||||
lvgl.colorDepth=8
|
lvgl.colorDepth=8
|
||||||
# Monochrome theme: the default colour theme renders accent-coloured UI that
|
|
||||||
# thresholds to invisible on a 1bpp panel (enables CONFIG_LV_USE_THEME_MONO).
|
|
||||||
lvgl.theme=Mono
|
lvgl.theme=Mono
|
||||||
lvgl.statusbarColorsInverted=true
|
lvgl.statusbarColorsInverted=true
|
||||||
|
|
||||||
cdn.warningMessage=Only for hardware revision 1.0! Display not reliably working. Use at your own risk.
|
cdn.warningMessage=Only for hardware revision 1.0! Display not reliably working. Use at your own risk.
|
||||||
|
|
||||||
sdkconfig.CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=0
|
|
||||||
|
|||||||
@ -25,4 +25,3 @@ lvgl.theme=Mono
|
|||||||
lvgl.statusbarColorsInverted=true
|
lvgl.statusbarColorsInverted=true
|
||||||
|
|
||||||
sdkconfig.CONFIG_EPD_DISPLAY_TYPE_ED047TC2=y
|
sdkconfig.CONFIG_EPD_DISPLAY_TYPE_ED047TC2=y
|
||||||
sdkconfig.CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=0
|
|
||||||
@ -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
|
||||||
|
|||||||
@ -29,6 +29,15 @@ else ()
|
|||||||
set(ui_density_variable "LVGL_UI_DENSITY_DEFAULT")
|
set(ui_density_variable "LVGL_UI_DENSITY_DEFAULT")
|
||||||
message("UI density set to default: ${ui_density}")
|
message("UI density set to default: ${ui_density}")
|
||||||
endif ()
|
endif ()
|
||||||
|
# Read the theme: whether the device uses dark mode by default
|
||||||
|
GET_VALUE_FROM_MAP(device_properties "lvgl.theme" lvgl_theme)
|
||||||
|
if (NOT lvgl_theme OR lvgl_theme STREQUAL "DefaultDark")
|
||||||
|
set(theme_dark 1)
|
||||||
|
elseif (lvgl_theme STREQUAL "DefaultLight" OR lvgl_theme STREQUAL "Mono")
|
||||||
|
set(theme_dark 0)
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR "Invalid lvgl.theme: '${lvgl_theme}'. Must be 'DefaultDark', 'DefaultLight' or 'Mono'")
|
||||||
|
endif ()
|
||||||
|
|
||||||
# ####################################
|
# ####################################
|
||||||
# Create module
|
# Create module
|
||||||
@ -74,4 +83,6 @@ target_compile_definitions(${MODULE_NAME} PUBLIC
|
|||||||
"-DLV_LVGL_H_INCLUDE_SIMPLE"
|
"-DLV_LVGL_H_INCLUDE_SIMPLE"
|
||||||
# UiDensity
|
# UiDensity
|
||||||
"-DTT_LVGL_UI_DENSITY=${ui_density_variable}"
|
"-DTT_LVGL_UI_DENSITY=${ui_density_variable}"
|
||||||
|
# Default theme mode: 1 for dark, 0 for light
|
||||||
|
"-DTT_LVGL_THEME_DARK=${theme_dark}"
|
||||||
)
|
)
|
||||||
|
|||||||
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
|
||||||
@ -31,6 +31,12 @@ void lvgl_theme_get_settings(LvglThemeSettings* settings);
|
|||||||
*/
|
*/
|
||||||
void lvgl_theme_get_default_settings(LvglThemeSettings* settings);
|
void lvgl_theme_get_default_settings(LvglThemeSettings* settings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether the active theme is the monochrome theme: on monochrome and greyscale displays, or when selected in the settings.
|
||||||
|
* @return true when the monochrome theme is active
|
||||||
|
*/
|
||||||
|
bool lvgl_theme_is_mono(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the theme settings. They are used for displays that are added afterwards, e.g. when LVGL (re)starts.
|
* @brief Sets the theme settings. They are used for displays that are added afterwards, e.g. when LVGL (re)starts.
|
||||||
* @param[in] settings the theme settings
|
* @param[in] settings the theme settings
|
||||||
|
|||||||
20
Modules/lvgl-module/include/lvgl/widgets/badge.h
Normal file
20
Modules/lvgl-module/include/lvgl/widgets/badge.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a badge: a small dot that marks its parent (e.g. a favourite item).
|
||||||
|
* The theme determines its size and colour. It ignores the parent's layout, so the app aligns it (e.g. LV_ALIGN_TOP_RIGHT).
|
||||||
|
* @param[in] parent the object to mark
|
||||||
|
* @return the created badge
|
||||||
|
*/
|
||||||
|
lv_obj_t* lvgl_badge_create(lv_obj_t* parent);
|
||||||
|
|
||||||
|
#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,7 +10,9 @@
|
|||||||
#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/card.h>
|
#include <lvgl/widgets/card.h>
|
||||||
#include <lvgl/widgets/chip.h>
|
#include <lvgl/widgets/chip.h>
|
||||||
#include <lvgl/widgets/icon_button.h>
|
#include <lvgl/widgets/icon_button.h>
|
||||||
@ -60,10 +62,19 @@ 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),
|
||||||
DEFINE_MODULE_SYMBOL(lvgl_theme_set_settings),
|
DEFINE_MODULE_SYMBOL(lvgl_theme_set_settings),
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_theme_is_mono),
|
||||||
|
// lvgl_badge
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_badge_create),
|
||||||
// lvgl_card
|
// lvgl_card
|
||||||
DEFINE_MODULE_SYMBOL(lvgl_card_create),
|
DEFINE_MODULE_SYMBOL(lvgl_card_create),
|
||||||
// lvgl_chip
|
// lvgl_chip
|
||||||
|
|||||||
@ -23,11 +23,7 @@ static ActiveTheme active_theme = ActiveTheme::None;
|
|||||||
void lvgl_theme_get_default_settings(LvglThemeSettings* settings) {
|
void lvgl_theme_get_default_settings(LvglThemeSettings* settings) {
|
||||||
lv_theme_material_config_t material_config;
|
lv_theme_material_config_t material_config;
|
||||||
lv_theme_material_config_init(&material_config);
|
lv_theme_material_config_init(&material_config);
|
||||||
#if defined(LV_THEME_DEFAULT_DARK) && LV_THEME_DEFAULT_DARK
|
settings->is_dark = TT_LVGL_THEME_DARK;
|
||||||
settings->is_dark = true;
|
|
||||||
#else
|
|
||||||
settings->is_dark = false;
|
|
||||||
#endif
|
|
||||||
settings->is_mono = false;
|
settings->is_mono = false;
|
||||||
settings->animations_enabled = true;
|
settings->animations_enabled = true;
|
||||||
settings->color_primary = material_config.color_primary;
|
settings->color_primary = material_config.color_primary;
|
||||||
@ -48,6 +44,10 @@ void lvgl_theme_set_settings(const LvglThemeSettings* settings) {
|
|||||||
theme_settings_set = true;
|
theme_settings_set = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool lvgl_theme_is_mono() {
|
||||||
|
return active_theme == ActiveTheme::MaterialMono;
|
||||||
|
}
|
||||||
|
|
||||||
static lv_theme_t* get_active_theme() {
|
static lv_theme_t* get_active_theme() {
|
||||||
switch (active_theme) {
|
switch (active_theme) {
|
||||||
case ActiveTheme::Material:
|
case ActiveTheme::Material:
|
||||||
|
|||||||
@ -15,6 +15,7 @@ extern const lv_obj_class_t lvgl_icon_button_tonal_class;
|
|||||||
extern const lv_obj_class_t lvgl_sliderbox_class;
|
extern const lv_obj_class_t lvgl_sliderbox_class;
|
||||||
extern const lv_obj_class_t lvgl_card_class;
|
extern const lv_obj_class_t lvgl_card_class;
|
||||||
extern const lv_obj_class_t lvgl_chip_class;
|
extern const lv_obj_class_t lvgl_chip_class;
|
||||||
|
extern const lv_obj_class_t lvgl_badge_class;
|
||||||
|
|
||||||
#define NEUTRAL_4 lv_color_hex(0x0E0E0E)
|
#define NEUTRAL_4 lv_color_hex(0x0E0E0E)
|
||||||
#define NEUTRAL_6 lv_color_hex(0x131313)
|
#define NEUTRAL_6 lv_color_hex(0x131313)
|
||||||
@ -136,6 +137,8 @@ extern const lv_obj_class_t lvgl_chip_class;
|
|||||||
#define CHIP_SHAPE (theme->config.is_compact ? CORNER_COMPACT : CORNER_SMALL)
|
#define CHIP_SHAPE (theme->config.is_compact ? CORNER_COMPACT : CORNER_SMALL)
|
||||||
#define CHIP_PAD_HOR (theme->config.is_compact ? 3 : SPACE_4)
|
#define CHIP_PAD_HOR (theme->config.is_compact ? 3 : SPACE_4)
|
||||||
#define CHIP_PAD_VER (theme->config.is_compact ? 2 : LV_DPX_CALC(theme->disp_dpi, 6))
|
#define CHIP_PAD_VER (theme->config.is_compact ? 2 : LV_DPX_CALC(theme->disp_dpi, 6))
|
||||||
|
#define BADGE_COLOR COLOR_PRIMARY
|
||||||
|
#define BADGE_SIZE (theme->config.is_compact ? 4 : LV_DPX_CALC(theme->disp_dpi, 6))
|
||||||
#define SCROLLBAR_COLOR COLOR_OUTLINE
|
#define SCROLLBAR_COLOR COLOR_OUTLINE
|
||||||
#define SCROLLBAR_OPACITY (LV_OPA_40)
|
#define SCROLLBAR_OPACITY (LV_OPA_40)
|
||||||
#define SCROLLBAR_SCROLLED_OPACITY (LV_OPA_70)
|
#define SCROLLBAR_SCROLLED_OPACITY (LV_OPA_70)
|
||||||
@ -159,7 +162,6 @@ extern const lv_obj_class_t lvgl_chip_class;
|
|||||||
#define TEXT_BUTTON_PRESSED_CONTAINER_COLOR COLOR_PRIMARY
|
#define TEXT_BUTTON_PRESSED_CONTAINER_COLOR COLOR_PRIMARY
|
||||||
#define TEXT_BUTTON_PAD_HOR (theme->config.is_compact ? 3 : SPACE_3)
|
#define TEXT_BUTTON_PAD_HOR (theme->config.is_compact ? 3 : SPACE_3)
|
||||||
#define ICON_BUTTON_COLOR COLOR_ON_SURFACE_VARIANT
|
#define ICON_BUTTON_COLOR COLOR_ON_SURFACE_VARIANT
|
||||||
#define ICON_BUTTON_SELECTED_COLOR COLOR_PRIMARY
|
|
||||||
#define ICON_BUTTON_CONTAINER_SHAPE (theme->config.is_compact ? CORNER_COMPACT : CORNER_LARGE)
|
#define ICON_BUTTON_CONTAINER_SHAPE (theme->config.is_compact ? CORNER_COMPACT : CORNER_LARGE)
|
||||||
#define ICON_BUTTON_FILLED_COLOR COLOR_PRIMARY
|
#define ICON_BUTTON_FILLED_COLOR COLOR_PRIMARY
|
||||||
#define ICON_BUTTON_FILLED_ICON_COLOR COLOR_ON_PRIMARY
|
#define ICON_BUTTON_FILLED_ICON_COLOR COLOR_ON_PRIMARY
|
||||||
@ -332,6 +334,7 @@ typedef struct {
|
|||||||
lv_style_t screen;
|
lv_style_t screen;
|
||||||
lv_style_t container;
|
lv_style_t container;
|
||||||
lv_style_t card;
|
lv_style_t card;
|
||||||
|
lv_style_t badge;
|
||||||
lv_style_t chip;
|
lv_style_t chip;
|
||||||
lv_style_t chip_selected;
|
lv_style_t chip_selected;
|
||||||
#if LV_USE_BUTTON
|
#if LV_USE_BUTTON
|
||||||
@ -354,13 +357,11 @@ typedef struct {
|
|||||||
lv_style_t text_button_pressed;
|
lv_style_t text_button_pressed;
|
||||||
#endif
|
#endif
|
||||||
lv_style_t icon_button;
|
lv_style_t icon_button;
|
||||||
lv_style_t icon_button_selected;
|
|
||||||
lv_style_t icon_button_filled;
|
lv_style_t icon_button_filled;
|
||||||
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 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;
|
||||||
@ -757,6 +758,13 @@ static void style_init(my_theme_t * theme)
|
|||||||
lv_style_set_pad_all(&theme->styles.card, CARD_PAD);
|
lv_style_set_pad_all(&theme->styles.card, CARD_PAD);
|
||||||
lv_style_set_pad_gap(&theme->styles.card, CARD_GAP);
|
lv_style_set_pad_gap(&theme->styles.card, CARD_GAP);
|
||||||
|
|
||||||
|
style_init_reset(&theme->styles.badge);
|
||||||
|
lv_style_set_width(&theme->styles.badge, BADGE_SIZE);
|
||||||
|
lv_style_set_height(&theme->styles.badge, BADGE_SIZE);
|
||||||
|
lv_style_set_radius(&theme->styles.badge, LV_RADIUS_CIRCLE);
|
||||||
|
lv_style_set_bg_color(&theme->styles.badge, BADGE_COLOR);
|
||||||
|
lv_style_set_bg_opa(&theme->styles.badge, LV_OPA_COVER);
|
||||||
|
|
||||||
style_init_reset(&theme->styles.chip);
|
style_init_reset(&theme->styles.chip);
|
||||||
lv_style_set_bg_opa(&theme->styles.chip, LV_OPA_TRANSP);
|
lv_style_set_bg_opa(&theme->styles.chip, LV_OPA_TRANSP);
|
||||||
lv_style_set_text_color(&theme->styles.chip, CHIP_LABEL_COLOR);
|
lv_style_set_text_color(&theme->styles.chip, CHIP_LABEL_COLOR);
|
||||||
@ -827,9 +835,6 @@ static void style_init(my_theme_t * theme)
|
|||||||
lv_style_set_radius(&theme->styles.icon_button, BUTTON_SHAPE);
|
lv_style_set_radius(&theme->styles.icon_button, BUTTON_SHAPE);
|
||||||
lv_style_set_pad_all(&theme->styles.icon_button, BUTTON_PAD_VER);
|
lv_style_set_pad_all(&theme->styles.icon_button, BUTTON_PAD_VER);
|
||||||
|
|
||||||
style_init_reset(&theme->styles.icon_button_selected);
|
|
||||||
lv_style_set_text_color(&theme->styles.icon_button_selected, ICON_BUTTON_SELECTED_COLOR);
|
|
||||||
|
|
||||||
style_init_reset(&theme->styles.icon_button_filled);
|
style_init_reset(&theme->styles.icon_button_filled);
|
||||||
lv_style_set_bg_color(&theme->styles.icon_button_filled, ICON_BUTTON_FILLED_COLOR);
|
lv_style_set_bg_color(&theme->styles.icon_button_filled, ICON_BUTTON_FILLED_COLOR);
|
||||||
lv_style_set_bg_opa(&theme->styles.icon_button_filled, LV_OPA_COVER);
|
lv_style_set_bg_opa(&theme->styles.icon_button_filled, LV_OPA_COVER);
|
||||||
@ -860,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);
|
||||||
@ -1611,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);
|
||||||
@ -1699,21 +1692,8 @@ 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.icon_button_selected, LV_STATE_CHECKED);
|
|
||||||
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);
|
||||||
lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED);
|
lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED);
|
||||||
@ -1724,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);
|
||||||
@ -1853,6 +1833,11 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if(lv_obj_check_type(obj, &lvgl_badge_class)) {
|
||||||
|
/* badge */
|
||||||
|
lv_obj_add_style(obj, &theme->styles.badge, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(lv_obj_check_type(obj, &lvgl_chip_class)) {
|
if(lv_obj_check_type(obj, &lvgl_chip_class)) {
|
||||||
/* chip */
|
/* chip */
|
||||||
lv_obj_add_style(obj, &theme->styles.chip, 0);
|
lv_obj_add_style(obj, &theme->styles.chip, 0);
|
||||||
|
|||||||
@ -15,6 +15,7 @@ extern const lv_obj_class_t lvgl_icon_button_tonal_class;
|
|||||||
extern const lv_obj_class_t lvgl_sliderbox_class;
|
extern const lv_obj_class_t lvgl_sliderbox_class;
|
||||||
extern const lv_obj_class_t lvgl_card_class;
|
extern const lv_obj_class_t lvgl_card_class;
|
||||||
extern const lv_obj_class_t lvgl_chip_class;
|
extern const lv_obj_class_t lvgl_chip_class;
|
||||||
|
extern const lv_obj_class_t lvgl_badge_class;
|
||||||
|
|
||||||
#define BLACK lv_color_black()
|
#define BLACK lv_color_black()
|
||||||
#define WHITE lv_color_white()
|
#define WHITE lv_color_white()
|
||||||
@ -86,6 +87,8 @@ extern const lv_obj_class_t lvgl_chip_class;
|
|||||||
#define CHIP_SHAPE (theme->config.is_compact ? CORNER_COMPACT : CORNER_SMALL)
|
#define CHIP_SHAPE (theme->config.is_compact ? CORNER_COMPACT : CORNER_SMALL)
|
||||||
#define CHIP_PAD_HOR (theme->config.is_compact ? 3 : SPACE_4)
|
#define CHIP_PAD_HOR (theme->config.is_compact ? 3 : SPACE_4)
|
||||||
#define CHIP_PAD_VER (theme->config.is_compact ? 2 : SPACE_2)
|
#define CHIP_PAD_VER (theme->config.is_compact ? 2 : SPACE_2)
|
||||||
|
#define BADGE_COLOR COLOR_ON_SURFACE
|
||||||
|
#define BADGE_SIZE (theme->config.is_compact ? 4 : LV_DPX_CALC(theme->disp_dpi, 6))
|
||||||
#define SCROLLBAR_COLOR COLOR_ON_SURFACE
|
#define SCROLLBAR_COLOR COLOR_ON_SURFACE
|
||||||
#define SCROLLBAR_WIDTH (theme->config.is_compact ? 2 : LV_DPX_CALC(theme->disp_dpi, 3))
|
#define SCROLLBAR_WIDTH (theme->config.is_compact ? 2 : LV_DPX_CALC(theme->disp_dpi, 3))
|
||||||
#define SCROLLBAR_PAD 1
|
#define SCROLLBAR_PAD 1
|
||||||
@ -213,17 +216,16 @@ typedef struct {
|
|||||||
lv_style_t screen;
|
lv_style_t screen;
|
||||||
lv_style_t container;
|
lv_style_t container;
|
||||||
lv_style_t card;
|
lv_style_t card;
|
||||||
|
lv_style_t badge;
|
||||||
lv_style_t chip;
|
lv_style_t chip;
|
||||||
#if LV_USE_BUTTON || LV_USE_MSGBOX
|
#if LV_USE_BUTTON || LV_USE_MSGBOX
|
||||||
lv_style_t button;
|
lv_style_t button;
|
||||||
#endif
|
#endif
|
||||||
lv_style_t icon_button;
|
lv_style_t icon_button;
|
||||||
lv_style_t icon_button_selected;
|
|
||||||
lv_style_t icon_button_filled;
|
lv_style_t icon_button_filled;
|
||||||
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
|
||||||
@ -546,6 +548,13 @@ static void style_init(my_theme_t * theme)
|
|||||||
lv_style_set_pad_all(&theme->styles.card, CARD_PAD);
|
lv_style_set_pad_all(&theme->styles.card, CARD_PAD);
|
||||||
lv_style_set_pad_gap(&theme->styles.card, CARD_GAP);
|
lv_style_set_pad_gap(&theme->styles.card, CARD_GAP);
|
||||||
|
|
||||||
|
style_init_reset(&theme->styles.badge);
|
||||||
|
lv_style_set_width(&theme->styles.badge, BADGE_SIZE);
|
||||||
|
lv_style_set_height(&theme->styles.badge, BADGE_SIZE);
|
||||||
|
lv_style_set_radius(&theme->styles.badge, LV_RADIUS_CIRCLE);
|
||||||
|
lv_style_set_bg_color(&theme->styles.badge, BADGE_COLOR);
|
||||||
|
lv_style_set_bg_opa(&theme->styles.badge, LV_OPA_COVER);
|
||||||
|
|
||||||
style_init_reset(&theme->styles.chip);
|
style_init_reset(&theme->styles.chip);
|
||||||
lv_style_set_bg_color(&theme->styles.chip, COLOR_SURFACE);
|
lv_style_set_bg_color(&theme->styles.chip, COLOR_SURFACE);
|
||||||
lv_style_set_bg_opa(&theme->styles.chip, LV_OPA_COVER);
|
lv_style_set_bg_opa(&theme->styles.chip, LV_OPA_COVER);
|
||||||
@ -576,10 +585,6 @@ static void style_init(my_theme_t * theme)
|
|||||||
lv_style_set_radius(&theme->styles.icon_button, BUTTON_SHAPE);
|
lv_style_set_radius(&theme->styles.icon_button, BUTTON_SHAPE);
|
||||||
lv_style_set_pad_all(&theme->styles.icon_button, BUTTON_PAD_VER);
|
lv_style_set_pad_all(&theme->styles.icon_button, BUTTON_PAD_VER);
|
||||||
|
|
||||||
style_init_reset(&theme->styles.icon_button_selected);
|
|
||||||
lv_style_set_border_color(&theme->styles.icon_button_selected, COLOR_OUTLINE);
|
|
||||||
lv_style_set_border_width(&theme->styles.icon_button_selected, BORDER);
|
|
||||||
|
|
||||||
style_init_reset(&theme->styles.icon_button_filled);
|
style_init_reset(&theme->styles.icon_button_filled);
|
||||||
lv_style_set_bg_color(&theme->styles.icon_button_filled, ICON_BUTTON_FILLED_COLOR);
|
lv_style_set_bg_color(&theme->styles.icon_button_filled, ICON_BUTTON_FILLED_COLOR);
|
||||||
lv_style_set_bg_opa(&theme->styles.icon_button_filled, LV_OPA_COVER);
|
lv_style_set_bg_opa(&theme->styles.icon_button_filled, LV_OPA_COVER);
|
||||||
@ -614,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);
|
||||||
|
|
||||||
@ -1262,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);
|
||||||
@ -1333,19 +1326,8 @@ 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.icon_button_selected, LV_STATE_CHECKED);
|
|
||||||
lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED);
|
lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED);
|
||||||
lv_obj_add_style(obj, &theme->styles.focus_ring_inset, LV_STATE_FOCUS_KEY);
|
lv_obj_add_style(obj, &theme->styles.focus_ring_inset, LV_STATE_FOCUS_KEY);
|
||||||
lv_obj_add_style(obj, &theme->styles.edit_ring_inset, LV_STATE_EDITED);
|
lv_obj_add_style(obj, &theme->styles.edit_ring_inset, LV_STATE_EDITED);
|
||||||
@ -1446,6 +1428,11 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if(lv_obj_check_type(obj, &lvgl_badge_class)) {
|
||||||
|
/* badge */
|
||||||
|
lv_obj_add_style(obj, &theme->styles.badge, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(lv_obj_check_type(obj, &lvgl_chip_class)) {
|
if(lv_obj_check_type(obj, &lvgl_chip_class)) {
|
||||||
/* chip */
|
/* chip */
|
||||||
lv_obj_add_style(obj, &theme->styles.chip, 0);
|
lv_obj_add_style(obj, &theme->styles.chip, 0);
|
||||||
|
|||||||
33
Modules/lvgl-module/source/widgets/badge.cpp
Normal file
33
Modules/lvgl-module/source/widgets/badge.cpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#define LV_USE_PRIVATE_API 1 // For the lv_obj_class_t declaration
|
||||||
|
|
||||||
|
#include <lvgl/widgets/badge.h>
|
||||||
|
|
||||||
|
static void badge_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) {
|
||||||
|
LV_UNUSED(class_p);
|
||||||
|
lv_obj_remove_flag(obj, LV_OBJ_FLAG_CLICKABLE);
|
||||||
|
lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
lv_obj_add_flag(obj, LV_OBJ_FLAG_IGNORE_LAYOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not static: the theme styles badges by this class
|
||||||
|
extern "C" const lv_obj_class_t lvgl_badge_class = {
|
||||||
|
.base_class = &lv_obj_class,
|
||||||
|
.constructor_cb = badge_constructor,
|
||||||
|
.destructor_cb = nullptr,
|
||||||
|
.event_cb = nullptr,
|
||||||
|
.user_data = nullptr,
|
||||||
|
.name = "badge",
|
||||||
|
.width_def = 0,
|
||||||
|
.height_def = 0,
|
||||||
|
.editable = LV_OBJ_CLASS_EDITABLE_INHERIT,
|
||||||
|
.group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
|
||||||
|
.instance_size = 0,
|
||||||
|
.theme_inheritable = LV_OBJ_CLASS_THEME_INHERITABLE_FALSE
|
||||||
|
};
|
||||||
|
|
||||||
|
lv_obj_t* lvgl_badge_create(lv_obj_t* parent) {
|
||||||
|
lv_obj_t* obj = lv_obj_class_create_obj(&lvgl_badge_class, parent);
|
||||||
|
lv_obj_class_init_obj(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
@widget sliderbox: lvgl_sliderbox_class;
|
@widget sliderbox: lvgl_sliderbox_class;
|
||||||
@widget card: lvgl_card_class;
|
@widget card: lvgl_card_class;
|
||||||
@widget chip: lvgl_chip_class;
|
@widget chip: lvgl_chip_class;
|
||||||
|
@widget badge: lvgl_badge_class;
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 1. Base tokens
|
// 1. Base tokens
|
||||||
@ -198,6 +199,10 @@
|
|||||||
@chip-pad-hor: if($isCompact, 3, @space-4);
|
@chip-pad-hor: if($isCompact, 3, @space-4);
|
||||||
@chip-pad-ver: if($isCompact, 2, 6dp);
|
@chip-pad-ver: if($isCompact, 2, 6dp);
|
||||||
|
|
||||||
|
// Badge (lvgl_badge_create): a small dot that marks an item, e.g. a favourite
|
||||||
|
@badge-color: @color-primary;
|
||||||
|
@badge-size: if($isCompact, 4, 6dp);
|
||||||
|
|
||||||
// Scrollbar
|
// Scrollbar
|
||||||
@scrollbar-color: @color-outline;
|
@scrollbar-color: @color-outline;
|
||||||
@scrollbar-opacity: 40%;
|
@scrollbar-opacity: 40%;
|
||||||
@ -228,7 +233,6 @@
|
|||||||
|
|
||||||
// Icon button (dialog and window header buttons)
|
// Icon button (dialog and window header buttons)
|
||||||
@icon-button-color: @color-on-surface-variant;
|
@icon-button-color: @color-on-surface-variant;
|
||||||
@icon-button-selected-color: @color-primary;
|
|
||||||
|
|
||||||
// Filled and tonal icon buttons: Material 3 large icon buttons with a container
|
// Filled and tonal icon buttons: Material 3 large icon buttons with a container
|
||||||
@icon-button-container-shape: if($isCompact, @corner-compact, @corner-large);
|
@icon-button-container-shape: if($isCompact, @corner-compact, @corner-large);
|
||||||
@ -561,6 +565,14 @@ card {
|
|||||||
padGap: @card-gap;
|
padGap: @card-gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
badge {
|
||||||
|
width: @badge-size;
|
||||||
|
height: @badge-size;
|
||||||
|
radius: circle;
|
||||||
|
bgColor: @badge-color;
|
||||||
|
bgOpa: cover;
|
||||||
|
}
|
||||||
|
|
||||||
chip {
|
chip {
|
||||||
bgOpa: transp;
|
bgOpa: transp;
|
||||||
textColor: @chip-label-color;
|
textColor: @chip-label-color;
|
||||||
@ -629,11 +641,6 @@ icon_button {
|
|||||||
padAll: @button-pad-ver;
|
padAll: @button-pad-ver;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Material 3 selected (toggled) icon button
|
|
||||||
icon_button_selected {
|
|
||||||
textColor: @icon-button-selected-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
icon_button_filled {
|
icon_button_filled {
|
||||||
bgColor: @icon-button-filled-color;
|
bgColor: @icon-button-filled-color;
|
||||||
bgOpa: cover;
|
bgOpa: cover;
|
||||||
@ -669,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;
|
||||||
@ -1229,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;
|
||||||
@ -1272,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;
|
||||||
@ -1283,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;
|
||||||
@ -1295,7 +1290,6 @@ apply(toolbar) {
|
|||||||
|
|
||||||
apply(icon_button) {
|
apply(icon_button) {
|
||||||
icon_button: default;
|
icon_button: default;
|
||||||
icon_button_selected: state.checked;
|
|
||||||
transition_exit: default;
|
transition_exit: default;
|
||||||
state_pressed: state.pressed;
|
state_pressed: state.pressed;
|
||||||
transition_enter: state.pressed;
|
transition_enter: state.pressed;
|
||||||
@ -1380,6 +1374,10 @@ apply(checkbox) {
|
|||||||
disabled: state.disabled;
|
disabled: state.disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply(badge) {
|
||||||
|
badge: default;
|
||||||
|
}
|
||||||
|
|
||||||
apply(chip) {
|
apply(chip) {
|
||||||
chip: default;
|
chip: default;
|
||||||
transition_exit: default;
|
transition_exit: default;
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
@widget sliderbox: lvgl_sliderbox_class;
|
@widget sliderbox: lvgl_sliderbox_class;
|
||||||
@widget card: lvgl_card_class;
|
@widget card: lvgl_card_class;
|
||||||
@widget chip: lvgl_chip_class;
|
@widget chip: lvgl_chip_class;
|
||||||
|
@widget badge: lvgl_badge_class;
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 1. Base tokens
|
// 1. Base tokens
|
||||||
@ -141,6 +142,10 @@
|
|||||||
@chip-pad-hor: if($isCompact, 3, @space-4);
|
@chip-pad-hor: if($isCompact, 3, @space-4);
|
||||||
@chip-pad-ver: if($isCompact, 2, @space-2);
|
@chip-pad-ver: if($isCompact, 2, @space-2);
|
||||||
|
|
||||||
|
// Badge (lvgl_badge_create): a small dot that marks an item, e.g. a favourite
|
||||||
|
@badge-color: @color-on-surface;
|
||||||
|
@badge-size: if($isCompact, 4, 6dp);
|
||||||
|
|
||||||
// Scrollbar
|
// Scrollbar
|
||||||
@scrollbar-color: @color-on-surface;
|
@scrollbar-color: @color-on-surface;
|
||||||
@scrollbar-width: if($isCompact, 2, 3dp);
|
@scrollbar-width: if($isCompact, 2, 3dp);
|
||||||
@ -387,6 +392,14 @@ card {
|
|||||||
padGap: @card-gap;
|
padGap: @card-gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
badge {
|
||||||
|
width: @badge-size;
|
||||||
|
height: @badge-size;
|
||||||
|
radius: circle;
|
||||||
|
bgColor: @badge-color;
|
||||||
|
bgOpa: cover;
|
||||||
|
}
|
||||||
|
|
||||||
chip {
|
chip {
|
||||||
bgColor: @color-surface;
|
bgColor: @color-surface;
|
||||||
bgOpa: cover;
|
bgOpa: cover;
|
||||||
@ -418,12 +431,6 @@ icon_button {
|
|||||||
padAll: @button-pad-ver;
|
padAll: @button-pad-ver;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selected (toggled) icon button: outlined, as there is no colour to show it with
|
|
||||||
icon_button_selected {
|
|
||||||
borderColor: @color-outline;
|
|
||||||
borderWidth: @border;
|
|
||||||
}
|
|
||||||
|
|
||||||
icon_button_filled {
|
icon_button_filled {
|
||||||
bgColor: @icon-button-filled-color;
|
bgColor: @icon-button-filled-color;
|
||||||
bgOpa: cover;
|
bgOpa: cover;
|
||||||
@ -463,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;
|
||||||
}
|
}
|
||||||
@ -937,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;
|
||||||
@ -956,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;
|
||||||
@ -971,7 +966,6 @@ apply(toolbar) {
|
|||||||
|
|
||||||
apply(icon_button) {
|
apply(icon_button) {
|
||||||
icon_button: default;
|
icon_button: default;
|
||||||
icon_button_selected: state.checked;
|
|
||||||
inverted: state.pressed;
|
inverted: state.pressed;
|
||||||
focus_ring_inset: state.focusKey;
|
focus_ring_inset: state.focusKey;
|
||||||
edit_ring_inset: state.edited;
|
edit_ring_inset: state.edited;
|
||||||
@ -1043,6 +1037,10 @@ apply(checkbox) {
|
|||||||
disabled: part.indicator, state.disabled;
|
disabled: part.indicator, state.disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply(badge) {
|
||||||
|
badge: default;
|
||||||
|
}
|
||||||
|
|
||||||
apply(chip) {
|
apply(chip) {
|
||||||
chip: default;
|
chip: default;
|
||||||
inverted: state.checked;
|
inverted: state.checked;
|
||||||
|
|||||||
@ -17,8 +17,7 @@ namespace tt::app::selectiondialog {
|
|||||||
* @a callerAppInstanceId (a new-model app - see app/manager.h). The caller receives the
|
* @a callerAppInstanceId (a new-model app - see app/manager.h). The caller receives the
|
||||||
* result as an APP_EVENT_RESULT in its own event loop: result is the selected item's index
|
* result as an APP_EVENT_RESULT in its own event loop: result is the selected item's index
|
||||||
* (>= 0), -1 if 0 items were provided (an error - the dialog auto-closes without showing
|
* (>= 0), -1 if 0 items were provided (an error - the dialog auto-closes without showing
|
||||||
* anything), or a value not matching any item (currently always 1) if the dialog was
|
* anything), or -2 if the dialog was dismissed without a selection. No result_bundle. If exactly 1 item is provided, the dialog
|
||||||
* dismissed without a selection. No result_bundle. If exactly 1 item is provided, the dialog
|
|
||||||
* auto-closes with result index 0 without showing anything. The caller is responsible for
|
* auto-closes with result index 0 without showing anything. The caller is responsible for
|
||||||
* calling app_manager_stop() on the returned instance id once it has handled the result.
|
* calling app_manager_stop() on the returned instance id once it has handled the result.
|
||||||
* @return the new dialog's app instance id
|
* @return the new dialog's app instance id
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#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/icon_button.h>
|
#include <lvgl/widgets/icon_button.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
|
||||||
@ -103,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;
|
||||||
}
|
}
|
||||||
@ -149,11 +152,8 @@ void AppGrid::populate() {
|
|||||||
for (uint32_t i = first; i < last; i++) {
|
for (uint32_t i = first; i < last; i++) {
|
||||||
const auto& item = items[i];
|
const auto& item = items[i];
|
||||||
|
|
||||||
// The theme styles the tile as an icon button: its focus, pressed and selected (favourite) states
|
// The theme styles the tile as an icon button, including its focus and pressed states
|
||||||
lv_obj_t* tile = lvgl_icon_button_create(grid);
|
lv_obj_t* tile = lvgl_icon_button_create(grid);
|
||||||
if (item.highlighted) {
|
|
||||||
lv_obj_add_state(tile, LV_STATE_CHECKED);
|
|
||||||
}
|
|
||||||
lv_obj_set_size(tile, layout.tileWidth, layout.tileHeight);
|
lv_obj_set_size(tile, layout.tileWidth, layout.tileHeight);
|
||||||
lv_obj_set_style_pad_all(tile, layout.pad, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_all(tile, layout.pad, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_pad_row(tile, layout.pad / 2, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_row(tile, layout.pad / 2, LV_STATE_DEFAULT);
|
||||||
@ -175,6 +175,12 @@ void AppGrid::populate() {
|
|||||||
lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_DOTS);
|
lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_DOTS);
|
||||||
lv_label_set_text(label, item.manifest.name);
|
lv_label_set_text(label, item.manifest.name);
|
||||||
|
|
||||||
|
// A badge marks favourites, as it doesn't look like the focus indicator
|
||||||
|
if (item.highlighted) {
|
||||||
|
auto* badge = lvgl_badge_create(tile);
|
||||||
|
lv_obj_align(badge, LV_ALIGN_TOP_RIGHT, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
lv_obj_add_event_cb(tile, onTileClicked, LV_EVENT_SHORT_CLICKED, this);
|
lv_obj_add_event_cb(tile, onTileClicked, LV_EVENT_SHORT_CLICKED, this);
|
||||||
if (callbacks.onLongPressed != nullptr) {
|
if (callbacks.onLongPressed != nullptr) {
|
||||||
lv_obj_add_event_cb(tile, onTileLongPressed, LV_EVENT_LONG_PRESSED, this);
|
lv_obj_add_event_cb(tile, onTileLongPressed, LV_EVENT_LONG_PRESSED, this);
|
||||||
@ -189,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,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);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#include <Tactility/app/fileselection/FileSelection.h>
|
#include <Tactility/app/fileselection/FileSelection.h>
|
||||||
|
#include <Tactility/app/selectiondialog/SelectionDialog.h>
|
||||||
#include <Tactility/file/File.h>
|
#include <Tactility/file/File.h>
|
||||||
#include <Tactility/lvgl/Fonts.h>
|
#include <Tactility/lvgl/Fonts.h>
|
||||||
#include <Tactility/lvgl/Lvgl.h>
|
#include <Tactility/lvgl/Lvgl.h>
|
||||||
@ -73,6 +74,14 @@ constexpr NamedColor PALETTE_COLORS[] = {
|
|||||||
{ "Grey", LV_PALETTE_GREY },
|
{ "Grey", LV_PALETTE_GREY },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr size_t COLOR_COUNT = 3;
|
||||||
|
constexpr const char* COLOR_TITLES[COLOR_COUNT] = { "Primary color", "Secondary color", "Error color" };
|
||||||
|
|
||||||
|
struct ColorRowWidgets {
|
||||||
|
lv_obj_t* row = nullptr;
|
||||||
|
lv_obj_t* swatch = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
struct FontRowWidgets {
|
struct FontRowWidgets {
|
||||||
lv_obj_t* defaultButton = nullptr;
|
lv_obj_t* defaultButton = nullptr;
|
||||||
lv_obj_t* fileLabel = nullptr;
|
lv_obj_t* fileLabel = nullptr;
|
||||||
@ -84,6 +93,7 @@ struct Context {
|
|||||||
TaskEventGroup* eventGroup = nullptr;
|
TaskEventGroup* eventGroup = nullptr;
|
||||||
uint32_t selectRegularBit = 0;
|
uint32_t selectRegularBit = 0;
|
||||||
uint32_t selectMonoBit = 0;
|
uint32_t selectMonoBit = 0;
|
||||||
|
uint32_t selectColorBit = 0;
|
||||||
uint32_t applyBit = 0;
|
uint32_t applyBit = 0;
|
||||||
uint32_t cacheUpdatedBit = 0;
|
uint32_t cacheUpdatedBit = 0;
|
||||||
|
|
||||||
@ -101,6 +111,9 @@ struct Context {
|
|||||||
std::atomic<bool> cacheUpdateSucceeded = false;
|
std::atomic<bool> cacheUpdateSucceeded = false;
|
||||||
|
|
||||||
uint32_t selectLaunchId = 0;
|
uint32_t selectLaunchId = 0;
|
||||||
|
/** The colour that the selection dialog is for */
|
||||||
|
size_t selectColorIndex = 0;
|
||||||
|
uint32_t colorSelectLaunchId = 0;
|
||||||
FontSlot selectSlot = FontSlot::Regular;
|
FontSlot selectSlot = FontSlot::Regular;
|
||||||
AppStream selectStream {};
|
AppStream selectStream {};
|
||||||
uint8_t selectBuffer[256] {};
|
uint8_t selectBuffer[256] {};
|
||||||
@ -115,7 +128,7 @@ struct Context {
|
|||||||
lv_obj_t* darkChip = nullptr;
|
lv_obj_t* darkChip = nullptr;
|
||||||
lv_obj_t* regularThemeChip = nullptr;
|
lv_obj_t* regularThemeChip = nullptr;
|
||||||
lv_obj_t* monoThemeChip = nullptr;
|
lv_obj_t* monoThemeChip = nullptr;
|
||||||
lv_obj_t* colorRows[3] = {};
|
ColorRowWidgets colorRows[COLOR_COUNT];
|
||||||
/** The display can only show the mono theme */
|
/** The display can only show the mono theme */
|
||||||
bool isMonoDisplay = false;
|
bool isMonoDisplay = false;
|
||||||
};
|
};
|
||||||
@ -183,6 +196,8 @@ void setChecked(lv_obj_t* object, bool checked) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateColorRow(Context* ctx, size_t index);
|
||||||
|
|
||||||
void updateThemeWidgets(Context* ctx) {
|
void updateThemeWidgets(Context* ctx) {
|
||||||
const bool dark = isDark(ctx->pendingSettings);
|
const bool dark = isDark(ctx->pendingSettings);
|
||||||
setChecked(ctx->lightChip, !dark);
|
setChecked(ctx->lightChip, !dark);
|
||||||
@ -192,9 +207,10 @@ void updateThemeWidgets(Context* ctx) {
|
|||||||
setChecked(ctx->monoThemeChip, ctx->pendingSettings.monoTheme);
|
setChecked(ctx->monoThemeChip, ctx->pendingSettings.monoTheme);
|
||||||
}
|
}
|
||||||
// The mono theme doesn't use colours
|
// The mono theme doesn't use colours
|
||||||
for (auto* row : ctx->colorRows) {
|
for (size_t i = 0; i < COLOR_COUNT; i++) {
|
||||||
if (row != nullptr) {
|
if (ctx->colorRows[i].row != nullptr) {
|
||||||
setHidden(row, ctx->pendingSettings.monoTheme);
|
setHidden(ctx->colorRows[i].row, ctx->pendingSettings.monoTheme);
|
||||||
|
updateColorRow(ctx, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,18 +272,35 @@ uint32_t getPaletteColor(lv_palette_t palette) {
|
|||||||
|
|
||||||
constexpr size_t PALETTE_COLOR_COUNT = sizeof(PALETTE_COLORS) / sizeof(PALETTE_COLORS[0]);
|
constexpr size_t PALETTE_COLOR_COUNT = sizeof(PALETTE_COLORS) / sizeof(PALETTE_COLORS[0]);
|
||||||
|
|
||||||
void onColorChanged(lv_event_t* event) {
|
std::optional<uint32_t>& getColor(settings::appearance::AppearanceSettings& settings, size_t index) {
|
||||||
auto* ctx = static_cast<Context*>(lv_event_get_user_data(event));
|
switch (index) {
|
||||||
auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(event));
|
case 0:
|
||||||
auto* color = static_cast<std::optional<uint32_t>*>(lv_obj_get_user_data(dropdown));
|
return settings.primaryColor;
|
||||||
const uint32_t selected = lv_dropdown_get_selected(dropdown);
|
case 1:
|
||||||
if (selected == 0) {
|
return settings.secondaryColor;
|
||||||
color->reset();
|
default:
|
||||||
} else if (selected <= PALETTE_COLOR_COUNT) {
|
return settings.errorColor;
|
||||||
*color = getPaletteColor(PALETTE_COLORS[selected - 1].palette);
|
|
||||||
}
|
}
|
||||||
// Any other option is the custom colour from the settings file, which stays unchanged
|
}
|
||||||
updateWidgets(ctx);
|
|
||||||
|
uint32_t getDefaultColor(size_t index) {
|
||||||
|
LvglThemeSettings defaults;
|
||||||
|
lvgl_theme_get_default_settings(&defaults);
|
||||||
|
const lv_color_t colors[COLOR_COUNT] = { defaults.color_primary, defaults.color_secondary, defaults.color_error };
|
||||||
|
return lv_color_to_u32(colors[index]) & 0xFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateColorRow(Context* ctx, size_t index) {
|
||||||
|
const auto& widgets = ctx->colorRows[index];
|
||||||
|
const auto& color = getColor(ctx->pendingSettings, index);
|
||||||
|
lv_obj_set_style_bg_color(widgets.swatch, lv_color_hex(color.value_or(getDefaultColor(index))), LV_STATE_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onColorButtonPressed(lv_event_t* event) {
|
||||||
|
auto* ctx = static_cast<Context*>(lv_event_get_user_data(event));
|
||||||
|
auto* button = static_cast<lv_obj_t*>(lv_event_get_target(event));
|
||||||
|
ctx->selectColorIndex = reinterpret_cast<uintptr_t>(lv_obj_get_user_data(button));
|
||||||
|
task_event_group_signal(ctx->eventGroup, ctx->selectColorBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBackPressed(lv_event_t* event) {
|
void onBackPressed(lv_event_t* event) {
|
||||||
@ -373,29 +406,32 @@ lv_obj_t* createChip(lv_obj_t* parent, const char* text, lv_event_cb_t callback,
|
|||||||
return chip;
|
return chip;
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_t* createColorRow(lv_obj_t* parent, const char* text, std::optional<uint32_t>* color, Context* ctx) {
|
ColorRowWidgets createColorRow(lv_obj_t* parent, size_t index, Context* ctx) {
|
||||||
auto* row = createLabeledRow(parent, text);
|
ColorRowWidgets widgets;
|
||||||
auto* dropdown = lv_dropdown_create(row);
|
// "Title [swatch] [Change]"
|
||||||
|
widgets.row = createLabeledRow(parent, COLOR_TITLES[index]);
|
||||||
|
|
||||||
std::string options = "Default";
|
// The colour preview: its colour is the setting, its border uses the theme's text colour
|
||||||
uint32_t selected = 0;
|
widgets.swatch = lv_obj_create(widgets.row);
|
||||||
for (size_t i = 0; i < PALETTE_COLOR_COUNT; i++) {
|
lv_obj_remove_style_all(widgets.swatch);
|
||||||
options += "\n";
|
lv_obj_remove_flag(widgets.swatch, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
options += PALETTE_COLORS[i].name;
|
lv_obj_remove_flag(widgets.swatch, LV_OBJ_FLAG_CLICKABLE);
|
||||||
if (color->has_value() && **color == getPaletteColor(PALETTE_COLORS[i].palette)) {
|
lv_obj_set_style_bg_opa(widgets.swatch, LV_OPA_COVER, LV_STATE_DEFAULT);
|
||||||
selected = i + 1;
|
lv_obj_set_style_border_width(widgets.swatch, 1, LV_STATE_DEFAULT);
|
||||||
}
|
lv_obj_set_style_border_color(widgets.swatch, lv_obj_get_style_text_color(widgets.swatch, LV_PART_MAIN), LV_STATE_DEFAULT);
|
||||||
}
|
lv_obj_set_style_radius(widgets.swatch, LV_DPX(4), LV_STATE_DEFAULT);
|
||||||
// A colour that isn't in the palette was set in the settings file
|
|
||||||
if (color->has_value() && selected == 0) {
|
auto* button = lv_button_create(widgets.row);
|
||||||
options += "\nCustom";
|
auto* button_label = lv_label_create(button);
|
||||||
selected = PALETTE_COLOR_COUNT + 1;
|
lv_label_set_text(button_label, "Change");
|
||||||
}
|
lv_obj_set_user_data(button, reinterpret_cast<void*>(static_cast<uintptr_t>(index)));
|
||||||
lv_dropdown_set_options(dropdown, options.c_str());
|
lv_obj_add_event_cb(button, onColorButtonPressed, LV_EVENT_SHORT_CLICKED, ctx);
|
||||||
lv_dropdown_set_selected(dropdown, selected);
|
|
||||||
lv_obj_set_user_data(dropdown, color);
|
// As high as the button
|
||||||
lv_obj_add_event_cb(dropdown, onColorChanged, LV_EVENT_VALUE_CHANGED, ctx);
|
lv_obj_update_layout(button);
|
||||||
return row;
|
const int32_t swatch_size = lv_obj_get_height(button);
|
||||||
|
lv_obj_set_size(widgets.swatch, swatch_size, swatch_size);
|
||||||
|
return widgets;
|
||||||
}
|
}
|
||||||
|
|
||||||
void createThemeCard(lv_obj_t* parent, Context* ctx) {
|
void createThemeCard(lv_obj_t* parent, Context* ctx) {
|
||||||
@ -418,9 +454,9 @@ void createThemeCard(lv_obj_t* parent, Context* ctx) {
|
|||||||
lv_obj_add_event_cb(animations_checkbox, onAnimationsChanged, LV_EVENT_VALUE_CHANGED, ctx);
|
lv_obj_add_event_cb(animations_checkbox, onAnimationsChanged, LV_EVENT_VALUE_CHANGED, ctx);
|
||||||
|
|
||||||
if (!ctx->isMonoDisplay) {
|
if (!ctx->isMonoDisplay) {
|
||||||
ctx->colorRows[0] = createColorRow(card, "Primary color", &ctx->pendingSettings.primaryColor, ctx);
|
for (size_t i = 0; i < COLOR_COUNT; i++) {
|
||||||
ctx->colorRows[1] = createColorRow(card, "Secondary color", &ctx->pendingSettings.secondaryColor, ctx);
|
ctx->colorRows[i] = createColorRow(card, i, ctx);
|
||||||
ctx->colorRows[2] = createColorRow(card, "Error color", &ctx->pendingSettings.errorColor, ctx);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,8 +557,8 @@ void destroyWidgets(void* userData) {
|
|||||||
ctx->darkChip = nullptr;
|
ctx->darkChip = nullptr;
|
||||||
ctx->regularThemeChip = nullptr;
|
ctx->regularThemeChip = nullptr;
|
||||||
ctx->monoThemeChip = nullptr;
|
ctx->monoThemeChip = nullptr;
|
||||||
for (auto*& row : ctx->colorRows) {
|
for (auto& row : ctx->colorRows) {
|
||||||
row = nullptr;
|
row = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,6 +667,37 @@ void startFileSelection(Context* ctx, FontSlot slot) {
|
|||||||
ctx->selectLaunchId = fileselection::startForExistingFile(ctx->appInstanceId, ctx->selectStream, ctx->selectBuffer, sizeof(ctx->selectBuffer), ctx->eventGroup);
|
ctx->selectLaunchId = fileselection::startForExistingFile(ctx->appInstanceId, ctx->selectStream, ctx->selectBuffer, sizeof(ctx->selectBuffer), ctx->eventGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void startColorSelection(Context* ctx) {
|
||||||
|
if (ctx->colorSelectLaunchId != 0 || ctx->applying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::vector<std::string> items;
|
||||||
|
items.reserve(PALETTE_COLOR_COUNT + 1);
|
||||||
|
items.emplace_back("Default");
|
||||||
|
for (const auto& named_color : PALETTE_COLORS) {
|
||||||
|
items.emplace_back(named_color.name);
|
||||||
|
}
|
||||||
|
ctx->colorSelectLaunchId = selectiondialog::start(ctx->appInstanceId, COLOR_TITLES[ctx->selectColorIndex], items);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onColorSelectionResult(Context* ctx, const AppEvent& event) {
|
||||||
|
ctx->colorSelectLaunchId = 0;
|
||||||
|
// Index 0 is "Default", the others are the palette colours. Other results mean that the dialog was dismissed.
|
||||||
|
const int32_t selected = event.result.result;
|
||||||
|
if (selected >= 0 && selected <= static_cast<int32_t>(PALETTE_COLOR_COUNT)) {
|
||||||
|
lvgl_lock();
|
||||||
|
auto& color = getColor(ctx->pendingSettings, ctx->selectColorIndex);
|
||||||
|
if (selected == 0) {
|
||||||
|
color.reset();
|
||||||
|
} else {
|
||||||
|
color = getPaletteColor(PALETTE_COLORS[selected - 1].palette);
|
||||||
|
}
|
||||||
|
updateWidgets(ctx);
|
||||||
|
lvgl_unlock();
|
||||||
|
}
|
||||||
|
app_manager_stop(event.result.launch_id);
|
||||||
|
}
|
||||||
|
|
||||||
void onFileSelectionResult(Context* ctx, const AppEvent& event) {
|
void onFileSelectionResult(Context* ctx, const AppEvent& event) {
|
||||||
ctx->selectLaunchId = 0;
|
ctx->selectLaunchId = 0;
|
||||||
if (event.result.result == 0 /* Ok */) {
|
if (event.result.result == 0 /* Ok */) {
|
||||||
@ -665,6 +732,7 @@ int32_t appMain(int argc, char* argv[]) {
|
|||||||
ctx.eventGroup = &event_group;
|
ctx.eventGroup = &event_group;
|
||||||
check(task_event_group_claim_bit(&event_group, &ctx.selectRegularBit) == ERROR_NONE);
|
check(task_event_group_claim_bit(&event_group, &ctx.selectRegularBit) == ERROR_NONE);
|
||||||
check(task_event_group_claim_bit(&event_group, &ctx.selectMonoBit) == ERROR_NONE);
|
check(task_event_group_claim_bit(&event_group, &ctx.selectMonoBit) == ERROR_NONE);
|
||||||
|
check(task_event_group_claim_bit(&event_group, &ctx.selectColorBit) == ERROR_NONE);
|
||||||
check(task_event_group_claim_bit(&event_group, &ctx.applyBit) == ERROR_NONE);
|
check(task_event_group_claim_bit(&event_group, &ctx.applyBit) == ERROR_NONE);
|
||||||
check(task_event_group_claim_bit(&event_group, &ctx.cacheUpdatedBit) == ERROR_NONE);
|
check(task_event_group_claim_bit(&event_group, &ctx.cacheUpdatedBit) == ERROR_NONE);
|
||||||
|
|
||||||
@ -684,6 +752,9 @@ int32_t appMain(int argc, char* argv[]) {
|
|||||||
if (flags & ctx.selectMonoBit) {
|
if (flags & ctx.selectMonoBit) {
|
||||||
startFileSelection(&ctx, FontSlot::Mono);
|
startFileSelection(&ctx, FontSlot::Mono);
|
||||||
}
|
}
|
||||||
|
if (flags & ctx.selectColorBit) {
|
||||||
|
startColorSelection(&ctx);
|
||||||
|
}
|
||||||
if (flags & ctx.applyBit) {
|
if (flags & ctx.applyBit) {
|
||||||
startApply(&ctx);
|
startApply(&ctx);
|
||||||
}
|
}
|
||||||
@ -697,6 +768,8 @@ int32_t appMain(int argc, char* argv[]) {
|
|||||||
should_close = true;
|
should_close = true;
|
||||||
} else if (event.type == APP_EVENT_RESULT && event.result.launch_id == ctx.selectLaunchId) {
|
} else if (event.type == APP_EVENT_RESULT && event.result.launch_id == ctx.selectLaunchId) {
|
||||||
onFileSelectionResult(&ctx, event);
|
onFileSelectionResult(&ctx, event);
|
||||||
|
} else if (event.type == APP_EVENT_RESULT && event.result.launch_id == ctx.colorSelectLaunchId) {
|
||||||
|
onColorSelectionResult(&ctx, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,9 @@
|
|||||||
#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/widgets/icon_button.h>
|
#include <lvgl/widgets/icon_button.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
@ -151,10 +153,14 @@ void createWidgets(lv_obj_t* parent, void*) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The app list is the main action, so it stands out more than the others
|
// The app list is the main action, so it stands out more than the others
|
||||||
auto* app_list_button = createAppButton(buttons_wrapper, LVGL_ICON_BUTTON_FILLED, LVGL_ICON_LAUNCHER_APPS, "tactility.applist");
|
// Filled buttons would be solid black blocks with the monochrome theme
|
||||||
createAppButton(buttons_wrapper, LVGL_ICON_BUTTON_FILLED, LVGL_ICON_LAUNCHER_FOLDER, "tactility.files");
|
const auto variant = lvgl_theme_is_mono() ? LVGL_ICON_BUTTON_STANDARD : LVGL_ICON_BUTTON_FILLED;
|
||||||
createAppButton(buttons_wrapper, LVGL_ICON_BUTTON_FILLED, LVGL_ICON_LAUNCHER_SETTINGS, "tactility.settings");
|
auto* app_list_button = createAppButton(buttons_wrapper, variant, LVGL_ICON_LAUNCHER_APPS, "tactility.applist");
|
||||||
|
createAppButton(buttons_wrapper, variant, LVGL_ICON_LAUNCHER_FOLDER, "tactility.files");
|
||||||
|
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
|
||||||
@ -179,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,7 @@ struct Context {
|
|||||||
// The eventual appMain() return value - see AlertDialog.cpp's Context::result for why this
|
// The eventual appMain() return value - see AlertDialog.cpp's Context::result for why this
|
||||||
// is a plain (non-atomic) field safely shared between the LVGL thread (writer, before
|
// is a plain (non-atomic) field safely shared between the LVGL thread (writer, before
|
||||||
// emitting APP_EVENT_CLOSE) and this dialog's own thread (reader, after waking from it).
|
// emitting APP_EVENT_CLOSE) and this dialog's own thread (reader, after waking from it).
|
||||||
int32_t result = 1; // Cancelled - safety-net default if closed without selecting an item
|
int32_t result = -2; // Cancelled - safety-net default if closed without selecting an item
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ItemContext {
|
struct ItemContext {
|
||||||
|
|||||||
@ -30,11 +30,25 @@ static std::optional<uint32_t> parseColor(const std::map<std::string, std::strin
|
|||||||
return static_cast<uint32_t>(std::strtoul(entry->second.c_str(), nullptr, 16)) & 0xFFFFFF;
|
return static_cast<uint32_t>(std::strtoul(entry->second.c_str(), nullptr, 16)) & 0xFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Saving only updates the keys that are written, so an unset colour is written as an empty value
|
||||||
static void putColor(std::map<std::string, std::string>& map, const char* key, std::optional<uint32_t> color) {
|
static void putColor(std::map<std::string, std::string>& map, const char* key, std::optional<uint32_t> color) {
|
||||||
if (color.has_value()) {
|
if (color.has_value()) {
|
||||||
char text[8];
|
char text[8];
|
||||||
std::snprintf(text, sizeof(text), "%06" PRIX32, *color);
|
std::snprintf(text, sizeof(text), "%06" PRIX32, *color);
|
||||||
map[key] = text;
|
map[key] = text;
|
||||||
|
} else {
|
||||||
|
map[key] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* toString(ThemeMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case ThemeMode::Light:
|
||||||
|
return "light";
|
||||||
|
case ThemeMode::Dark:
|
||||||
|
return "dark";
|
||||||
|
default:
|
||||||
|
return "default";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,9 +117,7 @@ bool save(const AppearanceSettings& settings) {
|
|||||||
map[SETTINGS_KEY_FONT_SIZE] = std::to_string(settings.fontSize);
|
map[SETTINGS_KEY_FONT_SIZE] = std::to_string(settings.fontSize);
|
||||||
map[SETTINGS_KEY_REGULAR_FONT_PATH] = settings.regularFontPath;
|
map[SETTINGS_KEY_REGULAR_FONT_PATH] = settings.regularFontPath;
|
||||||
map[SETTINGS_KEY_MONO_FONT_PATH] = settings.monoFontPath;
|
map[SETTINGS_KEY_MONO_FONT_PATH] = settings.monoFontPath;
|
||||||
if (settings.themeMode != ThemeMode::DeviceDefault) {
|
map[SETTINGS_KEY_THEME_MODE] = toString(settings.themeMode);
|
||||||
map[SETTINGS_KEY_THEME_MODE] = settings.themeMode == ThemeMode::Dark ? "dark" : "light";
|
|
||||||
}
|
|
||||||
map[SETTINGS_KEY_MONO_THEME] = settings.monoTheme ? "true" : "false";
|
map[SETTINGS_KEY_MONO_THEME] = settings.monoTheme ? "true" : "false";
|
||||||
map[SETTINGS_KEY_ANIMATIONS] = settings.animationsEnabled ? "true" : "false";
|
map[SETTINGS_KEY_ANIMATIONS] = settings.animationsEnabled ? "true" : "false";
|
||||||
putColor(map, SETTINGS_KEY_PRIMARY_COLOR, settings.primaryColor);
|
putColor(map, SETTINGS_KEY_PRIMARY_COLOR, settings.primaryColor);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
17
device.py
17
device.py
@ -292,20 +292,13 @@ def write_lvgl_variables(output_file, device_properties: dict):
|
|||||||
has_statusbar_colors_inverted = get_boolean_property_or_false(device_properties, "lvgl.statusbarColorsInverted")
|
has_statusbar_colors_inverted = get_boolean_property_or_false(device_properties, "lvgl.statusbarColorsInverted")
|
||||||
if has_statusbar_colors_inverted:
|
if has_statusbar_colors_inverted:
|
||||||
output_file.write("CONFIG_TT_LVGL_STATUSBAR_COLORS_INVERTED=y\n")
|
output_file.write("CONFIG_TT_LVGL_STATUSBAR_COLORS_INVERTED=y\n")
|
||||||
|
# Tactility uses its own themes (Modules/lvgl-module), and lvgl.theme only selects the default light or dark mode
|
||||||
theme = get_property_or_default(device_properties, "lvgl.theme", "DefaultDark")
|
theme = get_property_or_default(device_properties, "lvgl.theme", "DefaultDark")
|
||||||
if theme == "DefaultDark":
|
if theme not in ("DefaultDark", "DefaultLight", "Mono"):
|
||||||
output_file.write("CONFIG_LV_THEME_DEFAULT_DARK=y\n")
|
|
||||||
elif theme == "DefaultLight":
|
|
||||||
output_file.write("CONFIG_LV_THEME_DEFAULT_LIGHT=y\n")
|
|
||||||
elif theme == "Mono":
|
|
||||||
output_file.write("CONFIG_LV_USE_THEME_MONO=y\n")
|
|
||||||
# LVGL selects the theme with #if LV_USE_THEME_DEFAULT / #elif LV_USE_THEME_SIMPLE /
|
|
||||||
# #elif LV_USE_THEME_MONO, and the Kconfig defaults enable DEFAULT+SIMPLE for any
|
|
||||||
# non-1bpp color depth so MONO only takes effect once those are disabled.
|
|
||||||
output_file.write("CONFIG_LV_USE_THEME_DEFAULT=n\n")
|
|
||||||
output_file.write("CONFIG_LV_USE_THEME_SIMPLE=n\n")
|
|
||||||
else:
|
|
||||||
exit_with_error(f"Unknown theme: {theme}")
|
exit_with_error(f"Unknown theme: {theme}")
|
||||||
|
output_file.write("CONFIG_LV_USE_THEME_DEFAULT=n\n")
|
||||||
|
output_file.write("CONFIG_LV_USE_THEME_SIMPLE=n\n")
|
||||||
|
output_file.write("CONFIG_LV_USE_THEME_MONO=n\n")
|
||||||
font_size_text = get_property_or_none(device_properties, "font.defaultSize")
|
font_size_text = get_property_or_none(device_properties, "font.defaultSize")
|
||||||
if font_size_text is None:
|
if font_size_text is None:
|
||||||
font_size_text = get_property_or_default(device_properties, "lvgl.fontSize", "14")
|
font_size_text = get_property_or_default(device_properties, "lvgl.fontSize", "14")
|
||||||
|
|||||||
19
lv_conf.h
19
lv_conf.h
@ -557,22 +557,13 @@
|
|||||||
* THEMES
|
* THEMES
|
||||||
*==================*/
|
*==================*/
|
||||||
|
|
||||||
|
/*Tactility uses its own themes (Modules/lvgl-module), so LVGL's themes are disabled*/
|
||||||
|
|
||||||
/*A simple, impressive and very complete theme*/
|
/*A simple, impressive and very complete theme*/
|
||||||
#define LV_USE_THEME_DEFAULT 1
|
#define LV_USE_THEME_DEFAULT 0
|
||||||
#if LV_USE_THEME_DEFAULT
|
|
||||||
|
|
||||||
/*0: Light mode; 1: Dark mode*/
|
|
||||||
#define LV_THEME_DEFAULT_DARK 1
|
|
||||||
|
|
||||||
/*1: Enable grow on press*/
|
|
||||||
#define LV_THEME_DEFAULT_GROW 1
|
|
||||||
|
|
||||||
/*Default transition time in [ms]*/
|
|
||||||
#define LV_THEME_DEFAULT_TRANSITION_TIME 80
|
|
||||||
#endif /*LV_USE_THEME_DEFAULT*/
|
|
||||||
|
|
||||||
/*A very simple theme that is a good starting point for a custom theme*/
|
/*A very simple theme that is a good starting point for a custom theme*/
|
||||||
#define LV_USE_THEME_SIMPLE 1
|
#define LV_USE_THEME_SIMPLE 0
|
||||||
|
|
||||||
/*A theme designed for monochrome displays*/
|
/*A theme designed for monochrome displays*/
|
||||||
#define LV_USE_THEME_MONO 0
|
#define LV_USE_THEME_MONO 0
|
||||||
@ -782,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