mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-10-17 02:45:05 +00:00
Theming WIP
This commit is contained in:
parent
f8172446fd
commit
7638602201
@ -115,13 +115,6 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
|||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=utime" APPEND)
|
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=utime" APPEND)
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=rmdir" APPEND)
|
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=rmdir" APPEND)
|
||||||
|
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_button_create" APPEND)
|
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_dropdown_create" APPEND)
|
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_list_create" APPEND)
|
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_list_add_button" APPEND)
|
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_obj_create" APPEND)
|
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_obj_set_flex_flow" APPEND)
|
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_switch_create" APPEND)
|
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_textarea_create" APPEND)
|
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_textarea_create" APPEND)
|
||||||
|
|
||||||
else ()
|
else ()
|
||||||
|
|||||||
@ -7,7 +7,9 @@
|
|||||||
|
|
||||||
## Higher Priority
|
## Higher Priority
|
||||||
|
|
||||||
- Terminal app should always run in non-LVGL mode if no PSRAM is present.
|
- Update to LVGL 9.6.x (ESP-IDF and POSIX)
|
||||||
|
- Terminal app: on devices with pointer device: LVGL rendering mode should hide the toolbar by default, but tapping the terminal should toggle the visibility of the toolbar.
|
||||||
|
- Terminal app: should always run in non-LVGL mode if no PSRAM is present.
|
||||||
- Crash app should work without LVGL, like Boot app. It should wait for any input (keyboard or pointer) to continue.
|
- Crash app should work without LVGL, like Boot app. It should wait for any input (keyboard or pointer) to continue.
|
||||||
- NimBLE looses pairing key after reboot
|
- NimBLE looses pairing key after reboot
|
||||||
- CrashDiagnostics shouldn't show a QR when there's no callstack
|
- CrashDiagnostics shouldn't show a QR when there's no callstack
|
||||||
|
|||||||
20
Modules/lvgl-module/include/lvgl/widgets/card.h
Normal file
20
Modules/lvgl-module/include/lvgl/widgets/card.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 card: a container that groups related content and stands out from its background.
|
||||||
|
* The theme styles it as a card. Plain containers (lv_obj_create()) don't stand out, as they share the background colour.
|
||||||
|
* @param[in] parent the parent object for the new card
|
||||||
|
* @return the created card
|
||||||
|
*/
|
||||||
|
lv_obj_t* lvgl_card_create(lv_obj_t* parent);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
38
Modules/lvgl-module/include/lvgl/widgets/icon_button.h
Normal file
38
Modules/lvgl-module/include/lvgl/widgets/icon_button.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** The look of an icon button, as styled by the theme */
|
||||||
|
enum LvglIconButtonVariant {
|
||||||
|
/** No container, the icon uses the theme's icon colour */
|
||||||
|
LVGL_ICON_BUTTON_STANDARD,
|
||||||
|
/** Filled with the primary colour, for the most important action */
|
||||||
|
LVGL_ICON_BUTTON_FILLED,
|
||||||
|
/** Filled with a softer secondary colour, for other prominent actions */
|
||||||
|
LVGL_ICON_BUTTON_TONAL
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a button that shows an icon (or short text) without a filled background.
|
||||||
|
* It behaves like a regular button, but the theme styles it as an icon button.
|
||||||
|
* @param[in] parent the parent object for the new button
|
||||||
|
* @return the created button
|
||||||
|
*/
|
||||||
|
lv_obj_t* lvgl_icon_button_create(lv_obj_t* parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates an icon button with the specified look.
|
||||||
|
* @param[in] parent the parent object for the new button
|
||||||
|
* @param[in] variant the look of the button
|
||||||
|
* @return the created button
|
||||||
|
*/
|
||||||
|
lv_obj_t* lvgl_icon_button_create_variant(lv_obj_t* parent, enum LvglIconButtonVariant variant);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -1,8 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
#define LV_USE_PRIVATE_API 1 // For the lv_theme_t declaration
|
|
||||||
|
|
||||||
#include <lvgl/devices/display.h>
|
#include <lvgl/devices/display.h>
|
||||||
|
|
||||||
|
#include <lvgl/lvgl.h>
|
||||||
#include <lvgl/ppa.h>
|
#include <lvgl/ppa.h>
|
||||||
|
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
@ -12,6 +11,9 @@
|
|||||||
|
|
||||||
#include <lvgl/devices/device_context.h>
|
#include <lvgl/devices/device_context.h>
|
||||||
|
|
||||||
|
#include "../themes/lv_theme_material.h"
|
||||||
|
#include "../themes/lv_theme_material_mono.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
@ -20,37 +22,34 @@
|
|||||||
|
|
||||||
constexpr auto* TAG = "lvgl_display";
|
constexpr auto* TAG = "lvgl_display";
|
||||||
|
|
||||||
#if LV_USE_THEME_MONO
|
// The themes are generated with LSS from Modules/lvgl-module/themes/*.lss
|
||||||
|
static void lvgl_display_apply_theme(lv_display_t* disp, struct Device* device, lv_color_format_t color_format) {
|
||||||
static lv_style_t mono_button_style;
|
#if defined(LV_THEME_DEFAULT_DARK) && LV_THEME_DEFAULT_DARK
|
||||||
static bool mono_button_style_initialized = false;
|
bool is_dark = true;
|
||||||
static lv_theme_t mono_theme_extension;
|
#else
|
||||||
|
bool is_dark = false;
|
||||||
static void mono_theme_extension_apply(lv_theme_t*, lv_obj_t* obj) {
|
|
||||||
if (lv_obj_check_type(obj, &lv_button_class)) {
|
|
||||||
lv_obj_add_style(obj, &mono_button_style, LV_PART_MAIN | LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The mono theme draws a border around every button. This removes it in the default state, keeping the pressed border and focus outline.
|
|
||||||
static void lvgl_display_extend_mono_theme(lv_display_t* disp) {
|
|
||||||
if (!mono_button_style_initialized) {
|
|
||||||
lv_style_init(&mono_button_style);
|
|
||||||
lv_style_set_border_width(&mono_button_style, 0);
|
|
||||||
mono_button_style_initialized = true;
|
|
||||||
}
|
|
||||||
// LVGL prefers the default and simple themes when they are also enabled, so only the mono theme itself is extended
|
|
||||||
lv_theme_t* mono_theme = lv_display_get_theme(disp);
|
|
||||||
if (mono_theme == nullptr || mono_theme != lv_theme_mono_get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mono_theme_extension = *mono_theme;
|
|
||||||
lv_theme_set_parent(&mono_theme_extension, mono_theme);
|
|
||||||
lv_theme_set_apply_cb(&mono_theme_extension, mono_theme_extension_apply);
|
|
||||||
lv_display_set_theme(disp, &mono_theme_extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
bool is_compact = lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT;
|
||||||
|
// Slow-refresh panels (e-paper) can't show animations
|
||||||
|
bool animations_enabled = !display_has_capability(device, DISPLAY_CAPABILITY_SLOW_REFRESH);
|
||||||
|
|
||||||
|
lv_theme_t* theme;
|
||||||
|
if (color_format == LV_COLOR_FORMAT_I1 || color_format == LV_COLOR_FORMAT_L8) {
|
||||||
|
lv_theme_material_mono_config_t config;
|
||||||
|
lv_theme_material_mono_config_init(&config);
|
||||||
|
config.is_compact = is_compact;
|
||||||
|
config.animations_enabled = animations_enabled;
|
||||||
|
theme = lv_theme_material_mono_init(disp, &config);
|
||||||
|
} else {
|
||||||
|
lv_theme_material_config_t config;
|
||||||
|
lv_theme_material_config_init(&config);
|
||||||
|
config.is_dark = is_dark;
|
||||||
|
config.is_compact = is_compact;
|
||||||
|
config.animations_enabled = animations_enabled;
|
||||||
|
theme = lv_theme_material_init(disp, &config);
|
||||||
|
}
|
||||||
|
lv_display_set_theme(disp, theme);
|
||||||
|
}
|
||||||
|
|
||||||
struct LvglDisplayCtx {
|
struct LvglDisplayCtx {
|
||||||
void* buf1;
|
void* buf1;
|
||||||
@ -504,9 +503,7 @@ error_t lvgl_display_add(struct Device* device, const struct LvglDisplayConfig*
|
|||||||
return ERROR_OUT_OF_MEMORY;
|
return ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LV_USE_THEME_MONO
|
lvgl_display_apply_theme(disp, device, lv_color_format);
|
||||||
lvgl_display_extend_mono_theme(disp);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ctx->render_mode = render_mode;
|
ctx->render_mode = render_mode;
|
||||||
lv_display_set_color_format(disp, lv_color_format);
|
lv_display_set_color_format(disp, lv_color_format);
|
||||||
|
|||||||
@ -10,6 +10,8 @@
|
|||||||
#include <lvgl/devices/pointer.h>
|
#include <lvgl/devices/pointer.h>
|
||||||
|
|
||||||
#include <lvgl/widgets/sliderbox.h>
|
#include <lvgl/widgets/sliderbox.h>
|
||||||
|
#include <lvgl/widgets/card.h>
|
||||||
|
#include <lvgl/widgets/icon_button.h>
|
||||||
#include <lvgl/widgets/spinner.h>
|
#include <lvgl/widgets/spinner.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
|
||||||
@ -56,6 +58,11 @@ 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_card
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_card_create),
|
||||||
|
// lvgl_icon_button
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_icon_button_create),
|
||||||
|
DEFINE_MODULE_SYMBOL(lvgl_icon_button_create_variant),
|
||||||
// lvgl_spinner
|
// lvgl_spinner
|
||||||
DEFINE_MODULE_SYMBOL(lvgl_spinner_create),
|
DEFINE_MODULE_SYMBOL(lvgl_spinner_create),
|
||||||
// lvgl_toolbar
|
// lvgl_toolbar
|
||||||
|
|||||||
2228
Modules/lvgl-module/source/themes/lv_theme_material.c
Normal file
2228
Modules/lvgl-module/source/themes/lv_theme_material.c
Normal file
File diff suppressed because it is too large
Load Diff
59
Modules/lvgl-module/source/themes/lv_theme_material.h
Normal file
59
Modules/lvgl-module/source/themes/lv_theme_material.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/* Generated by LSS. Do not edit. */
|
||||||
|
|
||||||
|
#ifndef LV_THEME_MATERIAL_H
|
||||||
|
#define LV_THEME_MATERIAL_H
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool is_dark;
|
||||||
|
bool is_compact;
|
||||||
|
bool animations_enabled;
|
||||||
|
lv_color_t color_primary;
|
||||||
|
lv_color_t color_secondary;
|
||||||
|
lv_color_t color_error;
|
||||||
|
const lv_font_t * font_small;
|
||||||
|
const lv_font_t * font_normal;
|
||||||
|
const lv_font_t * font_large;
|
||||||
|
} lv_theme_material_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset a config to its default values
|
||||||
|
* @param config the config to initialize
|
||||||
|
*/
|
||||||
|
void lv_theme_material_config_init(lv_theme_material_config_t * config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the theme
|
||||||
|
* @param disp pointer to display (NULL for the default display)
|
||||||
|
* @param config the theme config, which is copied
|
||||||
|
* @return a pointer to reference this theme later
|
||||||
|
*/
|
||||||
|
lv_theme_t * lv_theme_material_init(lv_display_t * disp, const lv_theme_material_config_t * config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the theme is initialized
|
||||||
|
* @return true if initialized, false otherwise
|
||||||
|
*/
|
||||||
|
bool lv_theme_material_is_inited(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the theme
|
||||||
|
* @return a pointer to the theme, or NULL when not initialized
|
||||||
|
*/
|
||||||
|
lv_theme_t * lv_theme_material_get(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deinitialize the theme
|
||||||
|
*/
|
||||||
|
void lv_theme_material_deinit(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* LV_THEME_MATERIAL_H */
|
||||||
1811
Modules/lvgl-module/source/themes/lv_theme_material_mono.c
Normal file
1811
Modules/lvgl-module/source/themes/lv_theme_material_mono.c
Normal file
File diff suppressed because it is too large
Load Diff
58
Modules/lvgl-module/source/themes/lv_theme_material_mono.h
Normal file
58
Modules/lvgl-module/source/themes/lv_theme_material_mono.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* Generated by LSS. Do not edit. */
|
||||||
|
|
||||||
|
#ifndef LV_THEME_MATERIAL_MONO_H
|
||||||
|
#define LV_THEME_MATERIAL_MONO_H
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool is_dark;
|
||||||
|
bool is_compact;
|
||||||
|
bool animations_enabled;
|
||||||
|
lv_color_t color_primary;
|
||||||
|
lv_color_t color_secondary;
|
||||||
|
const lv_font_t * font_small;
|
||||||
|
const lv_font_t * font_normal;
|
||||||
|
const lv_font_t * font_large;
|
||||||
|
} lv_theme_material_mono_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset a config to its default values
|
||||||
|
* @param config the config to initialize
|
||||||
|
*/
|
||||||
|
void lv_theme_material_mono_config_init(lv_theme_material_mono_config_t * config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the theme
|
||||||
|
* @param disp pointer to display (NULL for the default display)
|
||||||
|
* @param config the theme config, which is copied
|
||||||
|
* @return a pointer to reference this theme later
|
||||||
|
*/
|
||||||
|
lv_theme_t * lv_theme_material_mono_init(lv_display_t * disp, const lv_theme_material_mono_config_t * config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the theme is initialized
|
||||||
|
* @return true if initialized, false otherwise
|
||||||
|
*/
|
||||||
|
bool lv_theme_material_mono_is_inited(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the theme
|
||||||
|
* @return a pointer to the theme, or NULL when not initialized
|
||||||
|
*/
|
||||||
|
lv_theme_t * lv_theme_material_mono_get(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deinitialize the theme
|
||||||
|
*/
|
||||||
|
void lv_theme_material_mono_deinit(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* LV_THEME_MATERIAL_MONO_H */
|
||||||
26
Modules/lvgl-module/source/widgets/card.cpp
Normal file
26
Modules/lvgl-module/source/widgets/card.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#define LV_USE_PRIVATE_API 1 // For the lv_obj_class_t declaration
|
||||||
|
|
||||||
|
#include <lvgl/widgets/card.h>
|
||||||
|
|
||||||
|
// Not static: the theme styles cards by this class
|
||||||
|
extern "C" const lv_obj_class_t lvgl_card_class = {
|
||||||
|
.base_class = &lv_obj_class,
|
||||||
|
.constructor_cb = nullptr,
|
||||||
|
.destructor_cb = nullptr,
|
||||||
|
.event_cb = nullptr,
|
||||||
|
.user_data = nullptr,
|
||||||
|
.name = "card",
|
||||||
|
.width_def = 0,
|
||||||
|
.height_def = 0,
|
||||||
|
.editable = LV_OBJ_CLASS_EDITABLE_INHERIT,
|
||||||
|
.group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT,
|
||||||
|
.instance_size = 0,
|
||||||
|
.theme_inheritable = LV_OBJ_CLASS_THEME_INHERITABLE_FALSE
|
||||||
|
};
|
||||||
|
|
||||||
|
lv_obj_t* lvgl_card_create(lv_obj_t* parent) {
|
||||||
|
lv_obj_t* obj = lv_obj_class_create_obj(&lvgl_card_class, parent);
|
||||||
|
lv_obj_class_init_obj(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
73
Modules/lvgl-module/source/widgets/icon_button.cpp
Normal file
73
Modules/lvgl-module/source/widgets/icon_button.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#define LV_USE_PRIVATE_API 1 // For the lv_obj_class_t declaration
|
||||||
|
|
||||||
|
#include <lvgl/widgets/icon_button.h>
|
||||||
|
|
||||||
|
// Not static: the theme styles icon buttons by these classes. Each variant is a separate class,
|
||||||
|
// because the theme applies its styles while the object is created.
|
||||||
|
|
||||||
|
extern "C" const lv_obj_class_t lvgl_icon_button_class = {
|
||||||
|
.base_class = &lv_button_class,
|
||||||
|
.constructor_cb = nullptr,
|
||||||
|
.destructor_cb = nullptr,
|
||||||
|
.event_cb = nullptr,
|
||||||
|
.user_data = nullptr,
|
||||||
|
.name = "icon_button",
|
||||||
|
.width_def = 0,
|
||||||
|
.height_def = 0,
|
||||||
|
.editable = LV_OBJ_CLASS_EDITABLE_INHERIT,
|
||||||
|
.group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT,
|
||||||
|
.instance_size = 0,
|
||||||
|
.theme_inheritable = LV_OBJ_CLASS_THEME_INHERITABLE_FALSE
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" const lv_obj_class_t lvgl_icon_button_filled_class = {
|
||||||
|
.base_class = &lv_button_class,
|
||||||
|
.constructor_cb = nullptr,
|
||||||
|
.destructor_cb = nullptr,
|
||||||
|
.event_cb = nullptr,
|
||||||
|
.user_data = nullptr,
|
||||||
|
.name = "icon_button_filled",
|
||||||
|
.width_def = 0,
|
||||||
|
.height_def = 0,
|
||||||
|
.editable = LV_OBJ_CLASS_EDITABLE_INHERIT,
|
||||||
|
.group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT,
|
||||||
|
.instance_size = 0,
|
||||||
|
.theme_inheritable = LV_OBJ_CLASS_THEME_INHERITABLE_FALSE
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" const lv_obj_class_t lvgl_icon_button_tonal_class = {
|
||||||
|
.base_class = &lv_button_class,
|
||||||
|
.constructor_cb = nullptr,
|
||||||
|
.destructor_cb = nullptr,
|
||||||
|
.event_cb = nullptr,
|
||||||
|
.user_data = nullptr,
|
||||||
|
.name = "icon_button_tonal",
|
||||||
|
.width_def = 0,
|
||||||
|
.height_def = 0,
|
||||||
|
.editable = LV_OBJ_CLASS_EDITABLE_INHERIT,
|
||||||
|
.group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT,
|
||||||
|
.instance_size = 0,
|
||||||
|
.theme_inheritable = LV_OBJ_CLASS_THEME_INHERITABLE_FALSE
|
||||||
|
};
|
||||||
|
|
||||||
|
static const lv_obj_class_t* get_variant_class(enum LvglIconButtonVariant variant) {
|
||||||
|
switch (variant) {
|
||||||
|
case LVGL_ICON_BUTTON_FILLED:
|
||||||
|
return &lvgl_icon_button_filled_class;
|
||||||
|
case LVGL_ICON_BUTTON_TONAL:
|
||||||
|
return &lvgl_icon_button_tonal_class;
|
||||||
|
default:
|
||||||
|
return &lvgl_icon_button_class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_t* lvgl_icon_button_create_variant(lv_obj_t* parent, enum LvglIconButtonVariant variant) {
|
||||||
|
lv_obj_t* obj = lv_obj_class_create_obj(get_variant_class(variant), parent);
|
||||||
|
lv_obj_class_init_obj(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_t* lvgl_icon_button_create(lv_obj_t* parent) {
|
||||||
|
return lvgl_icon_button_create_variant(parent, LVGL_ICON_BUTTON_STANDARD);
|
||||||
|
}
|
||||||
@ -17,7 +17,8 @@ typedef struct {
|
|||||||
|
|
||||||
static void sliderbox_constructor(const lv_obj_class_t* classPointer, lv_obj_t* obj);
|
static void sliderbox_constructor(const lv_obj_class_t* classPointer, lv_obj_t* obj);
|
||||||
|
|
||||||
static lv_obj_class_t sliderbox_class = {
|
// Not static: the theme styles sliderboxes by this class
|
||||||
|
extern "C" const lv_obj_class_t lvgl_sliderbox_class = {
|
||||||
.base_class = &lv_obj_class,
|
.base_class = &lv_obj_class,
|
||||||
.constructor_cb = &sliderbox_constructor,
|
.constructor_cb = &sliderbox_constructor,
|
||||||
.destructor_cb = nullptr,
|
.destructor_cb = nullptr,
|
||||||
@ -38,7 +39,6 @@ static void sliderbox_constructor(const lv_obj_class_t* classPointer, lv_obj_t*
|
|||||||
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);
|
||||||
lv_obj_set_style_pad_all(obj, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_all(obj, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_pad_column(obj, 4, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(obj, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_width(obj, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_bg_opa(obj, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_bg_opa(obj, 0, LV_STATE_DEFAULT);
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ lv_obj_t* lvgl_sliderbox_create(lv_obj_t* parent, int32_t min, int32_t max, int3
|
|||||||
step = 1;
|
step = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_t* obj = lv_obj_class_create_obj(&sliderbox_class, parent);
|
lv_obj_t* obj = lv_obj_class_create_obj(&lvgl_sliderbox_class, parent);
|
||||||
lv_obj_class_init_obj(obj);
|
lv_obj_class_init_obj(obj);
|
||||||
|
|
||||||
auto* sliderBox = reinterpret_cast<SliderBox*>(obj);
|
auto* sliderBox = reinterpret_cast<SliderBox*>(obj);
|
||||||
@ -165,10 +165,12 @@ lv_obj_t* lvgl_sliderbox_create(lv_obj_t* parent, int32_t min, int32_t max, int3
|
|||||||
// +4 padding wasn't enough margin for 3-digit values to never wrap; widened to
|
// +4 padding wasn't enough margin for 3-digit values to never wrap; widened to
|
||||||
// a flat per-character estimate instead of trusting raw glyph width too tightly.
|
// a flat per-character estimate instead of trusting raw glyph width too tightly.
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
|
lv_text_attributes_t text_attributes;
|
||||||
|
lv_text_attributes_init(&text_attributes);
|
||||||
lv_snprintf(buffer, sizeof(buffer), "%" LV_PRId32, min);
|
lv_snprintf(buffer, sizeof(buffer), "%" LV_PRId32, min);
|
||||||
auto width_min = lv_text_get_width(buffer, lv_strlen(buffer), lv_obj_get_style_text_font(sliderBox->valueLabel, LV_PART_MAIN), 0);
|
auto width_min = lv_text_get_width(buffer, lv_strlen(buffer), lv_obj_get_style_text_font(sliderBox->valueLabel, LV_PART_MAIN), &text_attributes);
|
||||||
lv_snprintf(buffer, sizeof(buffer), "%" LV_PRId32, max);
|
lv_snprintf(buffer, sizeof(buffer), "%" LV_PRId32, max);
|
||||||
auto width_max = lv_text_get_width(buffer, lv_strlen(buffer), lv_obj_get_style_text_font(sliderBox->valueLabel, LV_PART_MAIN), 0);
|
auto width_max = lv_text_get_width(buffer, lv_strlen(buffer), lv_obj_get_style_text_font(sliderBox->valueLabel, LV_PART_MAIN), &text_attributes);
|
||||||
auto max_width = (width_min > width_max) ? width_min : width_max;
|
auto max_width = (width_min > width_max) ? width_min : width_max;
|
||||||
lv_obj_set_width(sliderBox->valueLabel, max_width + 8);
|
lv_obj_set_width(sliderBox->valueLabel, max_width + 8);
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <lvgl/fonts.h>
|
#include <lvgl/fonts.h>
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.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>
|
||||||
@ -26,54 +27,11 @@ static const _lv_font_t* getToolbarFont(UiDensity uiDensity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t getActionIconPadding(UiDensity uiDensity) {
|
/** The size of icon buttons, which leaves room for the theme's focus indicator */
|
||||||
|
static uint32_t getActionButtonSize(UiDensity uiDensity) {
|
||||||
auto toolbar_height = getToolbarHeight(uiDensity);
|
auto toolbar_height = getToolbarHeight(uiDensity);
|
||||||
// Minimal 8 pixels total padding for selection/animation (4+4 pixels)
|
auto padding = (uiDensity != LVGL_UI_DENSITY_COMPACT) ? (uint32_t)(toolbar_height * 0.2f) : 8;
|
||||||
return (uiDensity != LVGL_UI_DENSITY_COMPACT) ? (uint32_t)(toolbar_height * 0.2f) : 8;
|
return toolbar_height - padding;
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_monochrome(lv_obj_t* obj) {
|
|
||||||
return lv_display_get_color_format(lv_obj_get_display(obj)) == LV_COLOR_FORMAT_I1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes a toolbar button transparent. Its icon or text is drawn in the accent colour when it's
|
|
||||||
* selected or pressed, instead of showing an outline.
|
|
||||||
*/
|
|
||||||
static void apply_button_style(lv_obj_t* button) {
|
|
||||||
lv_obj_set_style_bg_opa(button, LV_OPA_TRANSP, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_shadow_width(button, 0, LV_STATE_DEFAULT);
|
|
||||||
// Colours can't show the selection on monochrome displays, so those keep the theme's outline
|
|
||||||
if (is_monochrome(button)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const lv_color_t accent = lv_theme_get_color_primary(button);
|
|
||||||
const lv_state_t states[] = { LV_STATE_FOCUSED, LV_STATE_FOCUS_KEY, LV_STATE_PRESSED };
|
|
||||||
for (const lv_state_t state : states) {
|
|
||||||
lv_obj_set_style_outline_width(button, 0, state);
|
|
||||||
// Inherited by the button's label or image, including symbol images
|
|
||||||
lv_obj_set_style_text_color(button, accent, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helps with button expansion and also with vertical alignment of content,
|
|
||||||
* as the parent flex doesn't allow for vertical alignment
|
|
||||||
*/
|
|
||||||
static lv_obj_t* create_action_wrapper(lv_obj_t* parent, UiDensity ui_density) {
|
|
||||||
auto* wrapper = lv_obj_create(parent);
|
|
||||||
auto toolbar_height = getToolbarHeight(ui_density);
|
|
||||||
lv_obj_set_size(wrapper, LV_SIZE_CONTENT, toolbar_height);
|
|
||||||
|
|
||||||
auto icon_padding = getActionIconPadding(ui_density);
|
|
||||||
auto icon_padding_half = icon_padding / 2;
|
|
||||||
|
|
||||||
lv_obj_set_style_pad_all(wrapper, icon_padding_half, LV_STATE_DEFAULT); // For selection and touch animation
|
|
||||||
lv_obj_set_style_bg_opa(wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_opa(wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
return wrapper;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -88,7 +46,8 @@ typedef struct {
|
|||||||
|
|
||||||
static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
|
static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
|
||||||
|
|
||||||
static lv_obj_class_t toolbar_class = {
|
// Not static: the theme styles toolbars by this class
|
||||||
|
extern "C" const lv_obj_class_t lvgl_toolbar_class = {
|
||||||
.base_class = &lv_obj_class,
|
.base_class = &lv_obj_class,
|
||||||
.constructor_cb = &toolbar_constructor,
|
.constructor_cb = &toolbar_constructor,
|
||||||
.destructor_cb = nullptr,
|
.destructor_cb = nullptr,
|
||||||
@ -96,7 +55,7 @@ static lv_obj_class_t toolbar_class = {
|
|||||||
.user_data = nullptr,
|
.user_data = nullptr,
|
||||||
.name = nullptr,
|
.name = nullptr,
|
||||||
.width_def = LV_PCT(100),
|
.width_def = LV_PCT(100),
|
||||||
.height_def = 0,
|
.height_def = LV_SIZE_CONTENT,
|
||||||
.editable = false,
|
.editable = false,
|
||||||
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
|
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
|
||||||
.instance_size = sizeof(Toolbar),
|
.instance_size = sizeof(Toolbar),
|
||||||
@ -121,63 +80,40 @@ static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) {
|
|||||||
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
|
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static lv_obj_t* create_action_button(lv_obj_t* parent, UiDensity ui_density) {
|
||||||
|
auto button_size = getActionButtonSize(ui_density);
|
||||||
|
lv_obj_t* button = lvgl_icon_button_create(parent);
|
||||||
|
lv_obj_set_size(button, button_size, button_size);
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
lv_obj_t* lvgl_toolbar_create(lv_obj_t* parent, const char* title) {
|
lv_obj_t* lvgl_toolbar_create(lv_obj_t* parent, const char* title) {
|
||||||
auto ui_density = lvgl_get_ui_density();
|
auto ui_density = lvgl_get_ui_density();
|
||||||
auto toolbar_height = getToolbarHeight(ui_density);
|
lv_obj_t* obj = lv_obj_class_create_obj(&lvgl_toolbar_class, parent);
|
||||||
toolbar_class.height_def = toolbar_height;
|
|
||||||
lv_obj_t* obj = lv_obj_class_create_obj(&toolbar_class, parent);
|
|
||||||
lv_obj_class_init_obj(obj);
|
lv_obj_class_init_obj(obj);
|
||||||
lv_obj_set_height(obj, toolbar_height);
|
lv_obj_set_height(obj, getToolbarHeight(ui_density));
|
||||||
|
|
||||||
auto* toolbar = reinterpret_cast<Toolbar*>(obj);
|
auto* toolbar = reinterpret_cast<Toolbar*>(obj);
|
||||||
toolbar->nav_action_callback = nullptr;
|
toolbar->nav_action_callback = nullptr;
|
||||||
lv_obj_set_width(obj, LV_PCT(100));
|
|
||||||
lv_obj_set_style_pad_all(obj, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_pad_column(obj, 0, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
auto icon_padding = getActionIconPadding(ui_density);
|
toolbar->close_button = create_action_button(obj, ui_density);
|
||||||
|
|
||||||
auto* close_button_wrapper = create_action_wrapper(obj, ui_density);
|
|
||||||
|
|
||||||
toolbar->close_button = lv_button_create(close_button_wrapper);
|
|
||||||
apply_button_style(toolbar->close_button);
|
|
||||||
|
|
||||||
lv_obj_set_size(toolbar->close_button, toolbar_height - icon_padding, toolbar_height - icon_padding);
|
|
||||||
|
|
||||||
lv_obj_set_style_pad_all(toolbar->close_button, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_align(toolbar->close_button, LV_ALIGN_CENTER, 0, 0);
|
|
||||||
toolbar->close_button_image = lv_image_create(toolbar->close_button);
|
toolbar->close_button_image = lv_image_create(toolbar->close_button);
|
||||||
lv_obj_align(toolbar->close_button_image, LV_ALIGN_CENTER, 0, 0);
|
lv_obj_align(toolbar->close_button_image, LV_ALIGN_CENTER, 0, 0);
|
||||||
|
|
||||||
auto* title_wrapper = lv_obj_create(obj);
|
toolbar->title_label = lv_label_create(obj);
|
||||||
uint32_t title_left_padding = (ui_density != LVGL_UI_DENSITY_COMPACT) ? icon_padding : 2;
|
|
||||||
uint32_t title_right_padding = (ui_density != LVGL_UI_DENSITY_COMPACT) ? (icon_padding / 2) : 2;
|
|
||||||
lv_obj_set_size(title_wrapper, LV_SIZE_CONTENT, LV_PCT(100));
|
|
||||||
lv_obj_set_style_bg_opa(title_wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_pad_left(title_wrapper, title_left_padding, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_pad_right(title_wrapper, title_right_padding, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_pad_ver(title_wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(title_wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_flex_grow(title_wrapper, 1);
|
|
||||||
|
|
||||||
toolbar->title_label = lv_label_create(title_wrapper);
|
|
||||||
lv_obj_set_style_text_font(toolbar->title_label, getToolbarFont(ui_density), LV_STATE_DEFAULT);
|
lv_obj_set_style_text_font(toolbar->title_label, getToolbarFont(ui_density), LV_STATE_DEFAULT);
|
||||||
lv_label_set_text(toolbar->title_label, title);
|
lv_label_set_text(toolbar->title_label, 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_style_text_align(toolbar->title_label, LV_TEXT_ALIGN_LEFT, LV_STATE_DEFAULT);
|
lv_obj_set_flex_grow(toolbar->title_label, 1);
|
||||||
lv_obj_align(toolbar->title_label, LV_ALIGN_LEFT_MID, 0, 0);
|
|
||||||
lv_obj_set_width(toolbar->title_label, LV_PCT(100));
|
|
||||||
|
|
||||||
toolbar->action_container = lv_obj_create(obj);
|
toolbar->action_container = lv_obj_create(obj);
|
||||||
lv_obj_set_width(toolbar->action_container, LV_SIZE_CONTENT);
|
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_flow(toolbar->action_container, LV_FLEX_FLOW_ROW);
|
||||||
lv_obj_set_style_pad_all(toolbar->action_container, 0, LV_STATE_DEFAULT);
|
lv_obj_set_flex_align(toolbar->action_container, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||||
lv_obj_set_style_pad_column(toolbar->action_container, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(toolbar->action_container, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_bg_opa(toolbar->action_container, 0, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@ -215,19 +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++;
|
||||||
|
|
||||||
auto ui_density = lvgl_get_ui_density();
|
lv_obj_t* action_button = create_action_button(toolbar->action_container, lvgl_get_ui_density());
|
||||||
auto toolbar_height = getToolbarHeight(ui_density);
|
|
||||||
|
|
||||||
auto* wrapper = create_action_wrapper(toolbar->action_container, ui_density);
|
|
||||||
|
|
||||||
auto padding = getActionIconPadding(ui_density);
|
|
||||||
|
|
||||||
lv_obj_t* action_button = lv_button_create(wrapper);
|
|
||||||
lv_obj_set_size(action_button, toolbar_height - padding, toolbar_height - padding);
|
|
||||||
lv_obj_set_style_pad_all(action_button, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_align(action_button, LV_ALIGN_CENTER, 0, 0);
|
|
||||||
apply_button_style(action_button);
|
|
||||||
|
|
||||||
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) {
|
||||||
@ -255,14 +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++;
|
||||||
|
|
||||||
auto ui_density = lvgl_get_ui_density();
|
return lv_switch_create(toolbar->action_container);
|
||||||
auto* wrapper = create_action_wrapper(toolbar->action_container, ui_density);
|
|
||||||
lv_obj_set_style_pad_hor(wrapper, 4, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
lv_obj_t* widget = lv_switch_create(wrapper);
|
|
||||||
lv_obj_set_align(widget, LV_ALIGN_CENTER);
|
|
||||||
|
|
||||||
return widget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -270,11 +187,7 @@ 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++;
|
||||||
|
|
||||||
auto ui_density = lvgl_get_ui_density();
|
lv_obj_t* widget = lv_dropdown_create(toolbar->action_container);
|
||||||
auto* wrapper = create_action_wrapper(toolbar->action_container, ui_density);
|
|
||||||
lv_obj_set_style_pad_hor(wrapper, 4, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
lv_obj_t* widget = lv_dropdown_create(wrapper);
|
|
||||||
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) {
|
||||||
@ -283,7 +196,6 @@ lv_obj_t* lvgl_toolbar_add_dropdown_action(lv_obj_t* obj, const char* options, l
|
|||||||
if (text != nullptr) {
|
if (text != nullptr) {
|
||||||
lv_dropdown_set_text(widget, text);
|
lv_dropdown_set_text(widget, text);
|
||||||
}
|
}
|
||||||
lv_obj_set_align(widget, LV_ALIGN_CENTER);
|
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
@ -293,11 +205,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 ui_density = lvgl_get_ui_density();
|
auto* spinner = lvgl_spinner_create(toolbar->action_container);
|
||||||
auto* wrapper = create_action_wrapper(toolbar->action_container, ui_density);
|
|
||||||
|
|
||||||
auto* spinner = lvgl_spinner_create(wrapper);
|
|
||||||
lv_obj_set_align(spinner, LV_ALIGN_CENTER);
|
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
1624
Modules/lvgl-module/themes/material.lss
Normal file
1624
Modules/lvgl-module/themes/material.lss
Normal file
File diff suppressed because it is too large
Load Diff
1264
Modules/lvgl-module/themes/material_mono.lss
Normal file
1264
Modules/lvgl-module/themes/material_mono.lss
Normal file
File diff suppressed because it is too large
Load Diff
@ -68,7 +68,6 @@ private:
|
|||||||
static void onTileClicked(lv_event_t* e);
|
static void onTileClicked(lv_event_t* e);
|
||||||
static void onTileLongPressed(lv_event_t* e);
|
static void onTileLongPressed(lv_event_t* e);
|
||||||
static void onTileKey(lv_event_t* e);
|
static void onTileKey(lv_event_t* e);
|
||||||
static void onTileFocusChanged(lv_event_t* e);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <lvgl/devices/indev.h>
|
#include <lvgl/devices/indev.h>
|
||||||
#include <lvgl/fonts.h>
|
#include <lvgl/fonts.h>
|
||||||
|
#include <lvgl/widgets/icon_button.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -12,8 +13,6 @@ namespace tt::app {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr int32_t TILE_GAP = 6;
|
constexpr int32_t TILE_GAP = 6;
|
||||||
constexpr uint32_t ICON_COLOR_SELECTED = 0x33BBFF;
|
|
||||||
constexpr uint32_t ICON_COLOR_HIGHLIGHTED = 0xF876E9;
|
|
||||||
// Minimum label width, as a multiple of the label's font size
|
// Minimum label width, as a multiple of the label's font size
|
||||||
constexpr uint32_t MIN_LABEL_WIDTH_IN_FONT_SIZES = 6;
|
constexpr uint32_t MIN_LABEL_WIDTH_IN_FONT_SIZES = 6;
|
||||||
|
|
||||||
@ -49,18 +48,6 @@ PageLayout computePageLayout(lv_obj_t* grid) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_color_t getIconColor(bool focused, bool highlighted) {
|
|
||||||
if (focused) {
|
|
||||||
return lv_color_hex(ICON_COLOR_SELECTED);
|
|
||||||
}
|
|
||||||
return highlighted ? lv_color_hex(ICON_COLOR_HIGHLIGHTED) : lv_color_white();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accent colours can't be rendered in 1bpp, so tiles use theme colours and the theme's focus outline instead
|
|
||||||
bool isMonochrome(lv_obj_t* obj) {
|
|
||||||
return lv_display_get_color_format(lv_obj_get_display(obj)) == LV_COLOR_FORMAT_I1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AppGridItem* getTileItem(lv_event_t* e) {
|
const AppGridItem* getTileItem(lv_event_t* e) {
|
||||||
return static_cast<const AppGridItem*>(lv_obj_get_user_data(lv_event_get_target_obj(e)));
|
return static_cast<const AppGridItem*>(lv_obj_get_user_data(lv_event_get_target_obj(e)));
|
||||||
}
|
}
|
||||||
@ -110,13 +97,6 @@ void AppGrid::onTileKey(lv_event_t* e) {
|
|||||||
self->callbacks.onKey(getTileItem(e)->manifest, lv_event_get_key(e), self->callbacks.userData);
|
self->callbacks.onKey(getTileItem(e)->manifest, lv_event_get_key(e), self->callbacks.userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppGrid::onTileFocusChanged(lv_event_t* e) {
|
|
||||||
lv_obj_t* tile = lv_event_get_target_obj(e);
|
|
||||||
lv_obj_t* icon = lv_obj_get_child(tile, 0);
|
|
||||||
const bool focused = lv_event_get_code(e) == LV_EVENT_FOCUSED;
|
|
||||||
lv_obj_set_style_text_color(icon, getIconColor(focused, getTileItem(e)->highlighted), LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppGrid::requestRepopulate(bool keepSelection) {
|
void AppGrid::requestRepopulate(bool keepSelection) {
|
||||||
keepSelectionOnRepopulate = keepSelection;
|
keepSelectionOnRepopulate = keepSelection;
|
||||||
lv_async_call(onDeferredRepopulate, this);
|
lv_async_call(onDeferredRepopulate, this);
|
||||||
@ -160,27 +140,23 @@ void AppGrid::populate() {
|
|||||||
|
|
||||||
lv_obj_set_state(prevButton, LV_STATE_DISABLED, page == 0);
|
lv_obj_set_state(prevButton, LV_STATE_DISABLED, page == 0);
|
||||||
lv_obj_set_state(nextButton, LV_STATE_DISABLED, page + 1 >= page_count);
|
lv_obj_set_state(nextButton, LV_STATE_DISABLED, page + 1 >= page_count);
|
||||||
// The toolbar wraps each action button, so the wrapper is hidden to free its space too
|
lv_obj_set_flag(prevButton, LV_OBJ_FLAG_HIDDEN, page_count <= 1);
|
||||||
lv_obj_set_flag(lv_obj_get_parent(prevButton), LV_OBJ_FLAG_HIDDEN, page_count <= 1);
|
lv_obj_set_flag(nextButton, LV_OBJ_FLAG_HIDDEN, page_count <= 1);
|
||||||
lv_obj_set_flag(lv_obj_get_parent(nextButton), LV_OBJ_FLAG_HIDDEN, page_count <= 1);
|
|
||||||
|
|
||||||
lv_obj_t* focusedTile = nullptr;
|
lv_obj_t* focusedTile = nullptr;
|
||||||
const bool monochrome = isMonochrome(grid);
|
|
||||||
const uint32_t first = page * layout.pageSize;
|
const uint32_t first = page * layout.pageSize;
|
||||||
const uint32_t last = std::min(item_count, first + layout.pageSize);
|
const uint32_t last = std::min(item_count, first + layout.pageSize);
|
||||||
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];
|
||||||
|
|
||||||
lv_obj_t* tile = lv_button_create(grid);
|
// The theme styles the tile as an icon button: its focus, pressed and selected (favourite) states
|
||||||
|
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);
|
||||||
lv_obj_set_style_shadow_width(tile, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_bg_opa(tile, LV_OPA_TRANSP, LV_STATE_DEFAULT);
|
|
||||||
if (!monochrome) {
|
|
||||||
lv_obj_set_style_outline_width(tile, 0, LV_STATE_FOCUSED);
|
|
||||||
lv_obj_set_style_outline_width(tile, 0, LV_STATE_FOCUS_KEY);
|
|
||||||
}
|
|
||||||
lv_obj_set_flex_flow(tile, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(tile, LV_FLEX_FLOW_COLUMN);
|
||||||
lv_obj_set_flex_align(tile, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
lv_obj_set_flex_align(tile, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||||
lv_obj_set_user_data(tile, const_cast<AppGridItem*>(&item));
|
lv_obj_set_user_data(tile, const_cast<AppGridItem*>(&item));
|
||||||
@ -189,9 +165,6 @@ void AppGrid::populate() {
|
|||||||
lv_obj_set_style_text_font(icon, lvgl_get_shared_icon_large_font(), LV_STATE_DEFAULT);
|
lv_obj_set_style_text_font(icon, lvgl_get_shared_icon_large_font(), LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_text_align(icon, LV_TEXT_ALIGN_CENTER, LV_STATE_DEFAULT);
|
lv_obj_set_style_text_align(icon, LV_TEXT_ALIGN_CENTER, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_size(icon, layout.iconSize, layout.iconSize);
|
lv_obj_set_size(icon, layout.iconSize, layout.iconSize);
|
||||||
if (!monochrome) {
|
|
||||||
lv_obj_set_style_text_color(icon, getIconColor(false, item.highlighted), LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
lv_label_set_text(icon, item.icon);
|
lv_label_set_text(icon, item.icon);
|
||||||
|
|
||||||
lv_obj_t* label = lv_label_create(tile);
|
lv_obj_t* label = lv_label_create(tile);
|
||||||
@ -209,10 +182,6 @@ void AppGrid::populate() {
|
|||||||
if (callbacks.onKey != nullptr) {
|
if (callbacks.onKey != nullptr) {
|
||||||
lv_obj_add_event_cb(tile, onTileKey, LV_EVENT_KEY, this);
|
lv_obj_add_event_cb(tile, onTileKey, LV_EVENT_KEY, this);
|
||||||
}
|
}
|
||||||
if (!monochrome) {
|
|
||||||
lv_obj_add_event_cb(tile, onTileFocusChanged, LV_EVENT_FOCUSED, nullptr);
|
|
||||||
lv_obj_add_event_cb(tile, onTileFocusChanged, LV_EVENT_DEFOCUSED, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!focusedAppId.empty() && focusedAppId == item.manifest.id) {
|
if (!focusedAppId.empty() && focusedAppId == item.manifest.id) {
|
||||||
focusedTile = tile;
|
focusedTile = tile;
|
||||||
@ -247,7 +216,7 @@ void AppGrid::createWidgets(lv_obj_t* parent, lv_obj_t* toolbar) {
|
|||||||
|
|
||||||
populate();
|
populate();
|
||||||
|
|
||||||
const bool next_visible = !lv_obj_has_flag(lv_obj_get_parent(nextButton), LV_OBJ_FLAG_HIDDEN);
|
const bool next_visible = !lv_obj_has_flag(nextButton, LV_OBJ_FLAG_HIDDEN);
|
||||||
if (next_visible && !lvgl_indev_exists(LV_INDEV_TYPE_POINTER)) {
|
if (next_visible && !lvgl_indev_exists(LV_INDEV_TYPE_POINTER)) {
|
||||||
lv_group_focus_obj(nextButton);
|
lv_group_focus_obj(nextButton);
|
||||||
lv_obj_add_state(nextButton, LV_STATE_FOCUS_KEY);
|
lv_obj_add_state(nextButton, LV_STATE_FOCUS_KEY);
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include <tactility/check.h>
|
#include <tactility/check.h>
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
#include <lvgl/widgets/card.h>
|
||||||
#include <lvgl/widgets/sliderbox.h>
|
#include <lvgl/widgets/sliderbox.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
|
||||||
@ -77,23 +78,26 @@ void onOutputVolumeSlider(lv_event_t* event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_t* createSection(lv_obj_t* parent, const char* title) {
|
lv_obj_t* createSection(lv_obj_t* parent, const char* title) {
|
||||||
auto* wrapper = lv_obj_create(parent);
|
auto* title_label = lv_label_create(parent);
|
||||||
lv_obj_set_size(wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
|
||||||
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
|
|
||||||
lv_obj_set_style_pad_hor(wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
auto* title_label = lv_label_create(wrapper);
|
|
||||||
lv_label_set_text(title_label, title);
|
lv_label_set_text(title_label, title);
|
||||||
|
|
||||||
return wrapper;
|
auto* card = lvgl_card_create(parent);
|
||||||
|
lv_obj_set_size(card, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
|
lv_obj_set_flex_flow(card, LV_FLEX_FLOW_COLUMN);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
// An unstyled container, so the rows don't paint over the card
|
||||||
|
lv_obj_t* createRow(lv_obj_t* parent) {
|
||||||
|
auto* row = lv_obj_create(parent);
|
||||||
|
lv_obj_remove_style_all(row);
|
||||||
|
lv_obj_set_size(row, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_t* createSwitchRow(lv_obj_t* parent, const char* label, lv_event_cb_t cb, void* userData) {
|
lv_obj_t* createSwitchRow(lv_obj_t* parent, const char* label, lv_event_cb_t cb, void* userData) {
|
||||||
auto* row = lv_obj_create(parent);
|
auto* row = createRow(parent);
|
||||||
lv_obj_set_size(row, LV_PCT(100), LV_SIZE_CONTENT);
|
|
||||||
lv_obj_set_style_pad_all(row, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(row, 0, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
auto* row_label = lv_label_create(row);
|
auto* row_label = lv_label_create(row);
|
||||||
lv_label_set_text(row_label, label);
|
lv_label_set_text(row_label, label);
|
||||||
@ -107,10 +111,7 @@ lv_obj_t* createSwitchRow(lv_obj_t* parent, const char* label, lv_event_cb_t cb,
|
|||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_t* createSliderRow(lv_obj_t* parent, const char* label, int32_t initialValue, lv_event_cb_t cb, void* userData) {
|
lv_obj_t* createSliderRow(lv_obj_t* parent, const char* label, int32_t initialValue, lv_event_cb_t cb, void* userData) {
|
||||||
auto* row = lv_obj_create(parent);
|
auto* row = createRow(parent);
|
||||||
lv_obj_set_size(row, LV_PCT(100), LV_SIZE_CONTENT);
|
|
||||||
lv_obj_set_style_pad_all(row, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(row, 0, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
auto* row_label = lv_label_create(row);
|
auto* row_label = lv_label_create(row);
|
||||||
lv_label_set_text(row_label, label);
|
lv_label_set_text(row_label, label);
|
||||||
|
|||||||
@ -163,11 +163,17 @@ void View::updateScanning() {
|
|||||||
void View::updatePeerList() {
|
void View::updatePeerList() {
|
||||||
lv_obj_clean(peers_list);
|
lv_obj_clean(peers_list);
|
||||||
|
|
||||||
// Enable on boot row
|
// Enable on boot
|
||||||
auto* enable_on_boot_wrapper = lv_obj_create(peers_list);
|
|
||||||
|
// An unstyled row insets the wrapper like the list items' text: margins would make a full-width item overflow the list
|
||||||
|
auto* enable_on_boot_row = lv_obj_create(peers_list);
|
||||||
|
lv_obj_remove_style_all(enable_on_boot_row);
|
||||||
|
lv_obj_set_size(enable_on_boot_row, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
|
const int32_t enable_on_boot_inset = (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) ? 2 : LV_DPX(16);
|
||||||
|
lv_obj_set_style_pad_hor(enable_on_boot_row, enable_on_boot_inset, LV_STATE_DEFAULT);
|
||||||
|
|
||||||
|
auto* enable_on_boot_wrapper = lv_obj_create(enable_on_boot_row);
|
||||||
lv_obj_set_size(enable_on_boot_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(enable_on_boot_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_pad_all(enable_on_boot_wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(enable_on_boot_wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
auto* enable_label = lv_label_create(enable_on_boot_wrapper);
|
auto* enable_label = lv_label_create(enable_on_boot_wrapper);
|
||||||
lv_label_set_text(enable_label, "Enable on boot");
|
lv_label_set_text(enable_label, "Enable on boot");
|
||||||
@ -178,12 +184,6 @@ void View::updatePeerList() {
|
|||||||
lv_obj_add_event_cb(enable_on_boot_switch, onEnableOnBootSwitchChanged, LV_EVENT_VALUE_CHANGED, nullptr);
|
lv_obj_add_event_cb(enable_on_boot_switch, onEnableOnBootSwitchChanged, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||||
lv_obj_add_event_cb(enable_on_boot_wrapper, onEnableOnBootParentClicked, LV_EVENT_SHORT_CLICKED, enable_on_boot_switch);
|
lv_obj_add_event_cb(enable_on_boot_wrapper, onEnableOnBootParentClicked, LV_EVENT_SHORT_CLICKED, enable_on_boot_switch);
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_ver(enable_on_boot_wrapper, 2, LV_STATE_DEFAULT);
|
|
||||||
} else {
|
|
||||||
lv_obj_set_style_pad_ver(enable_on_boot_wrapper, 8, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateEnableOnBootToggle();
|
updateEnableOnBootToggle();
|
||||||
|
|
||||||
using enum bluetooth::RadioState;
|
using enum bluetooth::RadioState;
|
||||||
|
|||||||
@ -177,7 +177,6 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
auto* ctx = static_cast<Context*>(userData);
|
auto* ctx = static_cast<Context*>(userData);
|
||||||
|
|
||||||
ctx->displaySettings = settings::display::loadOrGetDefault();
|
ctx->displaySettings = settings::display::loadOrGetDefault();
|
||||||
auto ui_density = lvgl_get_ui_density();
|
|
||||||
bool has_imu = device_exists_of_type(&IMU_TYPE);
|
bool has_imu = device_exists_of_type(&IMU_TYPE);
|
||||||
|
|
||||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||||
@ -205,9 +204,6 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
lv_obj_set_size(brightness_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(brightness_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_pad_hor(brightness_wrapper, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_hor(brightness_wrapper, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_border_width(brightness_wrapper, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_width(brightness_wrapper, 0, LV_STATE_DEFAULT);
|
||||||
if (ui_density != LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_ver(brightness_wrapper, 4, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* brightness_label = lv_label_create(brightness_wrapper);
|
auto* brightness_label = lv_label_create(brightness_wrapper);
|
||||||
lv_label_set_text(brightness_label, "Brightness");
|
lv_label_set_text(brightness_label, "Brightness");
|
||||||
|
|||||||
@ -552,15 +552,15 @@ void View::update(size_t start_index) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (is_root) {
|
if (is_root) {
|
||||||
lv_obj_add_flag(lv_obj_get_parent(navigate_up_button), LV_OBJ_FLAG_HIDDEN);
|
lv_obj_add_flag(navigate_up_button, LV_OBJ_FLAG_HIDDEN);
|
||||||
} else {
|
} else {
|
||||||
lv_obj_remove_flag(lv_obj_get_parent(navigate_up_button), LV_OBJ_FLAG_HIDDEN);
|
lv_obj_remove_flag(navigate_up_button, LV_OBJ_FLAG_HIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->hasClipboard() && !is_root) {
|
if (state->hasClipboard() && !is_root) {
|
||||||
lv_obj_remove_flag(lv_obj_get_parent(paste_button), LV_OBJ_FLAG_HIDDEN);
|
lv_obj_remove_flag(paste_button, LV_OBJ_FLAG_HIDDEN);
|
||||||
} else {
|
} else {
|
||||||
lv_obj_add_flag(lv_obj_get_parent(paste_button), LV_OBJ_FLAG_HIDDEN);
|
lv_obj_add_flag(paste_button, LV_OBJ_FLAG_HIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
@ -579,7 +579,7 @@ void View::init(uint32_t appInstanceId, lv_obj_t* parent) {
|
|||||||
new_file_button = lvgl_toolbar_add_image_button_action(toolbar, LV_SYMBOL_FILE, &onNewFilePressedCallback, this);
|
new_file_button = lvgl_toolbar_add_image_button_action(toolbar, LV_SYMBOL_FILE, &onNewFilePressedCallback, this);
|
||||||
new_folder_button = lvgl_toolbar_add_image_button_action(toolbar, LV_SYMBOL_DIRECTORY, &onNewFolderPressedCallback, this);
|
new_folder_button = lvgl_toolbar_add_image_button_action(toolbar, LV_SYMBOL_DIRECTORY, &onNewFolderPressedCallback, this);
|
||||||
paste_button = lvgl_toolbar_add_image_button_action(toolbar, LV_SYMBOL_PASTE, &onPastePressedCallback, this);
|
paste_button = lvgl_toolbar_add_image_button_action(toolbar, LV_SYMBOL_PASTE, &onPastePressedCallback, this);
|
||||||
lv_obj_add_flag(lv_obj_get_parent(paste_button), LV_OBJ_FLAG_HIDDEN);
|
lv_obj_add_flag(paste_button, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
|
||||||
auto* wrapper = lv_obj_create(parent);
|
auto* wrapper = lv_obj_create(parent);
|
||||||
lv_obj_set_width(wrapper, LV_PCT(100));
|
lv_obj_set_width(wrapper, LV_PCT(100));
|
||||||
|
|||||||
@ -239,23 +239,16 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||||
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
|
||||||
|
|
||||||
uint8_t margin = (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) ? 2 : 8;
|
|
||||||
|
|
||||||
auto* toolbar = lvgl_toolbar_create(parent, "GPS");
|
auto* toolbar = lvgl_toolbar_create(parent, "GPS");
|
||||||
// The global toolbar nav callback only knows how to stop old-model apps.
|
// The global toolbar nav callback only knows how to stop old-model apps.
|
||||||
lvgl_toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, onBackPressed, ctx);
|
lvgl_toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, onBackPressed, ctx);
|
||||||
lvgl_toolbar_add_text_button_action(toolbar, LV_SYMBOL_PLUS, onAddGpsPressed, ctx);
|
lvgl_toolbar_add_text_button_action(toolbar, LV_SYMBOL_PLUS, onAddGpsPressed, ctx);
|
||||||
lv_obj_set_style_margin_bottom(toolbar, margin, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
ctx->deviceListWrapper = lv_obj_create(parent);
|
ctx->deviceListWrapper = lv_obj_create(parent);
|
||||||
lv_obj_set_size(ctx->deviceListWrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(ctx->deviceListWrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
lv_obj_set_flex_flow(ctx->deviceListWrapper, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(ctx->deviceListWrapper, LV_FLEX_FLOW_COLUMN);
|
||||||
lv_obj_set_flex_grow(ctx->deviceListWrapper, 1);
|
lv_obj_set_flex_grow(ctx->deviceListWrapper, 1);
|
||||||
lv_obj_set_style_border_width(ctx->deviceListWrapper, 0, 0);
|
lv_obj_set_style_border_width(ctx->deviceListWrapper, 0, 0);
|
||||||
lv_obj_set_style_pad_hor(ctx->deviceListWrapper, margin, 0);
|
|
||||||
lv_obj_set_style_pad_top(ctx->deviceListWrapper, 0, 0);
|
|
||||||
lv_obj_set_style_pad_bottom(ctx->deviceListWrapper, margin, 0);
|
|
||||||
lv_obj_set_style_pad_row(ctx->deviceListWrapper, margin, 0);
|
|
||||||
|
|
||||||
rebuildDeviceList(ctx);
|
rebuildDeviceList(ctx);
|
||||||
updateDeviceStates(ctx);
|
updateDeviceStates(ctx);
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include <lvgl/icons/launcher.h>
|
#include <lvgl/icons/launcher.h>
|
||||||
#include <lvgl/fonts.h>
|
#include <lvgl/fonts.h>
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
#include <lvgl/widgets/icon_button.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
@ -32,19 +33,30 @@ constexpr auto* TAG = "Launcher";
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
uint32_t getButtonPadding(UiDensity density, uint32_t buttonSize) {
|
|
||||||
if (density == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return buttonSize / 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t computeButtonMargin(int32_t available_span, int32_t total_button_size) {
|
int32_t computeButtonMargin(int32_t available_span, int32_t total_button_size) {
|
||||||
const int32_t usable = std::max<int32_t>(0, available_span - (3 * total_button_size));
|
const int32_t usable = std::max<int32_t>(0, available_span - (3 * total_button_size));
|
||||||
return std::min<int32_t>(usable / 16, total_button_size / 2);
|
return std::min<int32_t>(usable / 16, total_button_size / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spreads the buttons along the display's longest side. The button padding comes from the theme.
|
||||||
|
void applyButtonMargins(lv_obj_t* buttonsWrapper, bool isLandscape) {
|
||||||
|
const uint32_t child_count = lv_obj_get_child_count(buttonsWrapper);
|
||||||
|
if (child_count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto* first_button = lv_obj_get_child(buttonsWrapper, 0);
|
||||||
|
const auto total_button_size = static_cast<int32_t>(lvgl_get_launcher_icon_font_height()) +
|
||||||
|
lv_obj_get_style_pad_left(first_button, LV_PART_MAIN) + lv_obj_get_style_pad_right(first_button, LV_PART_MAIN);
|
||||||
|
const auto* display = lv_obj_get_display(buttonsWrapper);
|
||||||
|
const auto span = isLandscape ? lv_display_get_horizontal_resolution(display) : lv_display_get_vertical_resolution(display);
|
||||||
|
const int32_t margin = computeButtonMargin(span, total_button_size);
|
||||||
|
for (uint32_t i = 0; i < child_count; i++) {
|
||||||
|
auto* button = lv_obj_get_child(buttonsWrapper, i);
|
||||||
|
lv_obj_set_style_margin_hor(button, isLandscape ? margin : 0, LV_STATE_DEFAULT);
|
||||||
|
lv_obj_set_style_margin_ver(button, isLandscape ? 0 : margin, LV_STATE_DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onAppPressed(lv_event_t* e) {
|
void onAppPressed(lv_event_t* e) {
|
||||||
auto* appId = static_cast<const char*>(lv_event_get_user_data(e));
|
auto* appId = static_cast<const char*>(lv_event_get_user_data(e));
|
||||||
uint32_t instance_id = 0;
|
uint32_t instance_id = 0;
|
||||||
@ -54,28 +66,14 @@ void onAppPressed(lv_event_t* e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_t* createAppButton(lv_obj_t* parent, UiDensity uiDensity, const char* imageFile, const char* appId, int32_t itemMargin, bool isLandscape) {
|
lv_obj_t* createAppButton(lv_obj_t* parent, LvglIconButtonVariant variant, const char* imageFile, const char* appId) {
|
||||||
const auto button_size = lvgl_get_launcher_icon_font_height();
|
const auto button_size = lvgl_get_launcher_icon_font_height();
|
||||||
const auto button_padding = getButtonPadding(uiDensity, button_size);
|
auto* apps_button = lvgl_icon_button_create_variant(parent, variant);
|
||||||
auto* apps_button = lv_button_create(parent);
|
|
||||||
|
|
||||||
lv_obj_set_style_pad_all(apps_button, static_cast<int32_t>(button_padding), LV_STATE_DEFAULT);
|
|
||||||
if (isLandscape) {
|
|
||||||
lv_obj_set_style_margin_hor(apps_button, itemMargin, LV_STATE_DEFAULT);
|
|
||||||
} else {
|
|
||||||
lv_obj_set_style_margin_ver(apps_button, itemMargin, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_obj_set_style_shadow_width(apps_button, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_bg_opa(apps_button, 0, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
// create the image first
|
// create the image first
|
||||||
auto* button_image = lv_image_create(apps_button);
|
auto* button_image = lv_image_create(apps_button);
|
||||||
lv_obj_set_style_text_font(button_image, lvgl_get_launcher_icon_font(), LV_STATE_DEFAULT);
|
lv_obj_set_style_text_font(button_image, lvgl_get_launcher_icon_font(), LV_STATE_DEFAULT);
|
||||||
lv_image_set_src(button_image, imageFile);
|
lv_image_set_src(button_image, imageFile);
|
||||||
lv_obj_set_style_text_color(button_image, lv_theme_get_color_primary(button_image), LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_image_recolor(button_image, lv_theme_get_color_primary(parent), LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_image_recolor_opa(button_image, LV_OPA_COVER, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
// Ensure it's square (Material Symbols are slightly wider than tall)
|
// Ensure it's square (Material Symbols are slightly wider than tall)
|
||||||
lv_obj_set_size(button_image, button_size, button_size);
|
lv_obj_set_size(button_image, button_size, button_size);
|
||||||
@ -118,10 +116,6 @@ void onButtonsWrapperResized(lv_event_t* e) {
|
|||||||
auto* buttons_wrapper = static_cast<lv_obj_t*>(lv_event_get_user_data(e));
|
auto* buttons_wrapper = static_cast<lv_obj_t*>(lv_event_get_user_data(e));
|
||||||
const auto* display = lv_obj_get_display(buttons_wrapper);
|
const auto* display = lv_obj_get_display(buttons_wrapper);
|
||||||
|
|
||||||
const auto button_size = lvgl_get_launcher_icon_font_height();
|
|
||||||
const auto button_padding = getButtonPadding(lvgl_get_ui_density(), button_size);
|
|
||||||
const auto total_button_size = button_size + (button_padding * 2);
|
|
||||||
|
|
||||||
const auto horizontal_px = lv_display_get_horizontal_resolution(display);
|
const auto horizontal_px = lv_display_get_horizontal_resolution(display);
|
||||||
const auto vertical_px = lv_display_get_vertical_resolution(display);
|
const auto vertical_px = lv_display_get_vertical_resolution(display);
|
||||||
const bool is_landscape_display = horizontal_px >= vertical_px;
|
const bool is_landscape_display = horizontal_px >= vertical_px;
|
||||||
@ -132,27 +126,12 @@ void onButtonsWrapperResized(lv_event_t* e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_set_flex_flow(buttons_wrapper, is_landscape_display ? LV_FLEX_FLOW_ROW : LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(buttons_wrapper, is_landscape_display ? LV_FLEX_FLOW_ROW : LV_FLEX_FLOW_COLUMN);
|
||||||
|
applyButtonMargins(buttons_wrapper, is_landscape_display);
|
||||||
const int32_t margin = is_landscape_display
|
|
||||||
? computeButtonMargin(horizontal_px, total_button_size)
|
|
||||||
: computeButtonMargin(vertical_px, total_button_size);
|
|
||||||
|
|
||||||
const uint32_t child_count = lv_obj_get_child_count(buttons_wrapper);
|
|
||||||
for (uint32_t i = 0; i < child_count; i++) {
|
|
||||||
auto* button = lv_obj_get_child(buttons_wrapper, i);
|
|
||||||
lv_obj_set_style_margin_hor(button, is_landscape_display ? margin : 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_margin_ver(button, is_landscape_display ? 0 : margin, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void createWidgets(lv_obj_t* parent, void*) {
|
void createWidgets(lv_obj_t* parent, void*) {
|
||||||
auto* buttons_wrapper = lv_obj_create(parent);
|
auto* buttons_wrapper = lv_obj_create(parent);
|
||||||
|
|
||||||
auto ui_density = lvgl_get_ui_density();
|
|
||||||
const auto button_size = lvgl_get_launcher_icon_font_height();
|
|
||||||
const auto button_padding = getButtonPadding(ui_density, button_size);
|
|
||||||
const auto total_button_size = button_size + (button_padding * 2);
|
|
||||||
|
|
||||||
lv_obj_align(buttons_wrapper, LV_ALIGN_CENTER, 0, 0);
|
lv_obj_align(buttons_wrapper, LV_ALIGN_CENTER, 0, 0);
|
||||||
lv_obj_set_size(buttons_wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
lv_obj_set_size(buttons_wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_border_width(buttons_wrapper, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_width(buttons_wrapper, 0, LV_STATE_DEFAULT);
|
||||||
@ -171,13 +150,11 @@ void createWidgets(lv_obj_t* parent, void*) {
|
|||||||
lv_obj_set_flex_flow(buttons_wrapper, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(buttons_wrapper, LV_FLEX_FLOW_COLUMN);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int32_t margin = is_landscape_display
|
// The app list is the main action, so it stands out more than the others
|
||||||
? computeButtonMargin(lv_display_get_horizontal_resolution(display), total_button_size)
|
auto* app_list_button = createAppButton(buttons_wrapper, LVGL_ICON_BUTTON_FILLED, LVGL_ICON_LAUNCHER_APPS, "tactility.applist");
|
||||||
: computeButtonMargin(lv_display_get_vertical_resolution(display), total_button_size);
|
createAppButton(buttons_wrapper, LVGL_ICON_BUTTON_FILLED, LVGL_ICON_LAUNCHER_FOLDER, "tactility.files");
|
||||||
|
createAppButton(buttons_wrapper, LVGL_ICON_BUTTON_FILLED, LVGL_ICON_LAUNCHER_SETTINGS, "tactility.settings");
|
||||||
auto* app_list_button = createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_APPS, "tactility.applist", margin, is_landscape_display);
|
applyButtonMargins(buttons_wrapper, is_landscape_display);
|
||||||
createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_FOLDER, "tactility.files", margin, is_landscape_display);
|
|
||||||
createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_SETTINGS, "tactility.settings", margin, is_landscape_display);
|
|
||||||
|
|
||||||
// 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
|
||||||
@ -188,16 +165,13 @@ void createWidgets(lv_obj_t* parent, void*) {
|
|||||||
// Some devices (e.g. T-Lora Pager) have no other way to power off, so the
|
// Some devices (e.g. T-Lora Pager) have no other way to power off, so the
|
||||||
// button stays in the launcher; the confirmation flow lives in the PowerOff app.
|
// button stays in the launcher; the confirmation flow lives in the PowerOff app.
|
||||||
if (shouldShowPowerButton()) {
|
if (shouldShowPowerButton()) {
|
||||||
auto* power_button = lv_button_create(parent);
|
auto* power_button = lvgl_icon_button_create(parent);
|
||||||
lv_obj_set_style_pad_all(power_button, 8, 0);
|
lv_obj_set_style_pad_all(power_button, 8, 0);
|
||||||
lv_obj_align(power_button, LV_ALIGN_BOTTOM_MID, 0, -10);
|
lv_obj_align(power_button, LV_ALIGN_BOTTOM_MID, 0, -10);
|
||||||
lv_obj_add_event_cb(power_button, onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)"tactility.poweroff");
|
lv_obj_add_event_cb(power_button, onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)"tactility.poweroff");
|
||||||
lv_obj_set_style_shadow_width(power_button, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_bg_opa(power_button, 0, LV_PART_MAIN);
|
|
||||||
|
|
||||||
auto* power_label = lv_label_create(power_button);
|
auto* power_label = lv_label_create(power_button);
|
||||||
lv_label_set_text(power_label, LV_SYMBOL_POWER);
|
lv_label_set_text(power_label, LV_SYMBOL_POWER);
|
||||||
lv_obj_set_style_text_color(power_label, lv_theme_get_color_primary(parent), LV_STATE_DEFAULT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include <tactility/time.h>
|
#include <tactility/time.h>
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
#include <lvgl/widgets/card.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -170,10 +171,14 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
entry.device = device;
|
entry.device = device;
|
||||||
|
|
||||||
lv_obj_t* header = lv_label_create(wrapper);
|
lv_obj_t* header = lv_label_create(wrapper);
|
||||||
lv_label_set_text_fmt(header, "%s:", device->name);
|
lv_label_set_text(header, device->name);
|
||||||
|
|
||||||
|
lv_obj_t* card = lvgl_card_create(wrapper);
|
||||||
|
lv_obj_set_size(card, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
|
lv_obj_set_flex_flow(card, LV_FLEX_FLOW_COLUMN);
|
||||||
|
|
||||||
if (power_supply_supports_charge_control(device)) {
|
if (power_supply_supports_charge_control(device)) {
|
||||||
lv_obj_t* switch_container = lv_obj_create(wrapper);
|
lv_obj_t* switch_container = lv_obj_create(card);
|
||||||
lv_obj_set_width(switch_container, LV_PCT(100));
|
lv_obj_set_width(switch_container, LV_PCT(100));
|
||||||
lv_obj_set_height(switch_container, LV_SIZE_CONTENT);
|
lv_obj_set_height(switch_container, LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_pad_all(switch_container, 0, 0);
|
lv_obj_set_style_pad_all(switch_container, 0, 0);
|
||||||
@ -193,7 +198,7 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (power_supply_supports_quick_charge(device)) {
|
if (power_supply_supports_quick_charge(device)) {
|
||||||
lv_obj_t* qc_container = lv_obj_create(wrapper);
|
lv_obj_t* qc_container = lv_obj_create(card);
|
||||||
lv_obj_set_width(qc_container, LV_PCT(100));
|
lv_obj_set_width(qc_container, LV_PCT(100));
|
||||||
lv_obj_set_height(qc_container, LV_SIZE_CONTENT);
|
lv_obj_set_height(qc_container, LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_pad_all(qc_container, 0, 0);
|
lv_obj_set_style_pad_all(qc_container, 0, 0);
|
||||||
@ -215,8 +220,7 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
PowerSupplyPropertyValue value;
|
PowerSupplyPropertyValue value;
|
||||||
for (auto property : DISPLAYED_PROPERTIES) {
|
for (auto property : DISPLAYED_PROPERTIES) {
|
||||||
if (power_supply_get_property(device, property, &value) == ERROR_NONE) {
|
if (power_supply_get_property(device, property, &value) == ERROR_NONE) {
|
||||||
lv_obj_t* label = lv_label_create(wrapper);
|
lv_obj_t* label = lv_label_create(card);
|
||||||
lv_obj_set_style_margin_left(label, 24, LV_STATE_DEFAULT);
|
|
||||||
setPropertyLabelText(label, property, value);
|
setPropertyLabelText(label, property, value);
|
||||||
entry.propertyWidgets.push_back({ property, label });
|
entry.propertyWidgets.push_back({ property, label });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -145,12 +145,6 @@ MemoryBarWidgets createMemoryBar(lv_obj_t* parent, const char* label) {
|
|||||||
lv_obj_set_width(bottom_label, LV_PCT(100));
|
lv_obj_set_width(bottom_label, LV_PCT(100));
|
||||||
lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0);
|
lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0);
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_bottom(bottom_label, 2, LV_STATE_DEFAULT);
|
|
||||||
} else {
|
|
||||||
lv_obj_set_style_pad_bottom(bottom_label, 12, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {bar, bottom_label};
|
return {bar, bottom_label};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -149,7 +149,6 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
auto* ctx = static_cast<Context*>(userData);
|
auto* ctx = static_cast<Context*>(userData);
|
||||||
|
|
||||||
ctx->tbSettings = settings::trackball::loadOrGetDefault();
|
ctx->tbSettings = settings::trackball::loadOrGetDefault();
|
||||||
auto ui_density = lvgl_get_ui_density();
|
|
||||||
ctx->updated = false;
|
ctx->updated = false;
|
||||||
ctx->trackballIndev = findFirstTrackballIndev();
|
ctx->trackballIndev = findFirstTrackballIndev();
|
||||||
|
|
||||||
@ -211,9 +210,6 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
lv_obj_set_size(enc_sens_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(enc_sens_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_pad_hor(enc_sens_wrapper, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_hor(enc_sens_wrapper, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_border_width(enc_sens_wrapper, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_width(enc_sens_wrapper, 0, LV_STATE_DEFAULT);
|
||||||
if (ui_density != LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_ver(enc_sens_wrapper, 4, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* enc_sens_label = lv_label_create(enc_sens_wrapper);
|
auto* enc_sens_label = lv_label_create(enc_sens_wrapper);
|
||||||
lv_label_set_text(enc_sens_label, "Encoder Speed");
|
lv_label_set_text(enc_sens_label, "Encoder Speed");
|
||||||
@ -235,9 +231,6 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
lv_obj_set_size(ptr_sens_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(ptr_sens_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_pad_hor(ptr_sens_wrapper, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_hor(ptr_sens_wrapper, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_border_width(ptr_sens_wrapper, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_width(ptr_sens_wrapper, 0, LV_STATE_DEFAULT);
|
||||||
if (ui_density != LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_ver(ptr_sens_wrapper, 4, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* ptr_sens_label = lv_label_create(ptr_sens_wrapper);
|
auto* ptr_sens_label = lv_label_create(ptr_sens_wrapper);
|
||||||
lv_label_set_text(ptr_sens_label, "Pointer Speed");
|
lv_label_set_text(ptr_sens_label, "Pointer Speed");
|
||||||
|
|||||||
@ -142,10 +142,15 @@ void View::updateNetworkList() {
|
|||||||
|
|
||||||
// Enable on boot
|
// Enable on boot
|
||||||
|
|
||||||
auto* enable_on_boot_wrapper = lv_obj_create(networks_list);
|
// An unstyled row insets the wrapper like the list items' text: margins would make a full-width item overflow the list
|
||||||
|
auto* enable_on_boot_row = lv_obj_create(networks_list);
|
||||||
|
lv_obj_remove_style_all(enable_on_boot_row);
|
||||||
|
lv_obj_set_size(enable_on_boot_row, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
|
const int32_t enable_on_boot_inset = (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) ? 2 : LV_DPX(16);
|
||||||
|
lv_obj_set_style_pad_hor(enable_on_boot_row, enable_on_boot_inset, LV_STATE_DEFAULT);
|
||||||
|
|
||||||
|
auto* enable_on_boot_wrapper = lv_obj_create(enable_on_boot_row);
|
||||||
lv_obj_set_size(enable_on_boot_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(enable_on_boot_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_pad_all(enable_on_boot_wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(enable_on_boot_wrapper, 0, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
auto* enable_label = lv_label_create(enable_on_boot_wrapper);
|
auto* enable_label = lv_label_create(enable_on_boot_wrapper);
|
||||||
lv_label_set_text(enable_label, "Enable on boot");
|
lv_label_set_text(enable_label, "Enable on boot");
|
||||||
@ -156,12 +161,6 @@ void View::updateNetworkList() {
|
|||||||
lv_obj_add_event_cb(enable_on_boot_switch, onEnableOnBootSwitchChanged, LV_EVENT_VALUE_CHANGED, bindings);
|
lv_obj_add_event_cb(enable_on_boot_switch, onEnableOnBootSwitchChanged, LV_EVENT_VALUE_CHANGED, bindings);
|
||||||
lv_obj_add_event_cb(enable_on_boot_wrapper, onEnableOnBootParentClicked, LV_EVENT_SHORT_CLICKED, enable_on_boot_switch);
|
lv_obj_add_event_cb(enable_on_boot_wrapper, onEnableOnBootParentClicked, LV_EVENT_SHORT_CLICKED, enable_on_boot_switch);
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_ver(enable_on_boot_wrapper, 2, LV_STATE_DEFAULT);
|
|
||||||
} else {
|
|
||||||
lv_obj_set_style_pad_ver(enable_on_boot_wrapper, 8, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateEnableOnBootToggle();
|
updateEnableOnBootToggle();
|
||||||
|
|
||||||
switch (state->getRadioState()) {
|
switch (state->getRadioState()) {
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
#include <Tactility/Tactility.h>
|
#include <Tactility/Tactility.h>
|
||||||
|
|
||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
#include <app/scheduler.h>
|
#include <app/scheduler.h>
|
||||||
#include <app/start.h>
|
#include <app/start.h>
|
||||||
|
|||||||
@ -74,7 +74,7 @@ static lv_obj_t* windowManagerScreenInit(lv_obj_t* root) {
|
|||||||
lv_obj_set_flex_flow(vertical_container, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(vertical_container, LV_FLEX_FLOW_COLUMN);
|
||||||
lv_obj_set_style_pad_all(vertical_container, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_all(vertical_container, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_pad_gap(vertical_container, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_gap(vertical_container, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_bg_color(vertical_container, lv_color_black(), LV_STATE_DEFAULT);
|
lv_obj_set_style_bg_opa(vertical_container, LV_OPA_TRANSP, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_border_width(vertical_container, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_width(vertical_container, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_radius(vertical_container, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_radius(vertical_container, 0, LV_STATE_DEFAULT);
|
||||||
lv_obj_remove_flag(vertical_container, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_remove_flag(vertical_container, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|||||||
@ -168,21 +168,12 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
|||||||
|
|
||||||
auto* statusbar = reinterpret_cast<Statusbar*>(obj);
|
auto* statusbar = reinterpret_cast<Statusbar*>(obj);
|
||||||
|
|
||||||
const auto fg_color = STATUSBAR_COLORS_INVERTED ? lv_color_black() : lv_color_white();
|
// The statusbar has no theme styles: it shows the themed container background behind it and inherits its text colour
|
||||||
const auto bg_color = STATUSBAR_COLORS_INVERTED ? lv_color_white() : lv_color_black();
|
|
||||||
|
|
||||||
lv_obj_set_style_bg_color(obj, bg_color, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, LV_STATE_DEFAULT);
|
|
||||||
|
|
||||||
// Inverted statusbar creates a separator between statusbar and the rest of the UI below it
|
|
||||||
if (STATUSBAR_COLORS_INVERTED) {
|
if (STATUSBAR_COLORS_INVERTED) {
|
||||||
// Show border as a horizontal line on the bottom
|
// A horizontal line separates the statusbar from the rest of the UI below it
|
||||||
lv_obj_set_style_border_color(obj, fg_color, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_color(obj, lv_obj_get_style_text_color(obj, LV_PART_MAIN), LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_border_side(obj, LV_BORDER_SIDE_BOTTOM, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_side(obj, LV_BORDER_SIDE_BOTTOM, LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_border_width(obj, 1, LV_STATE_DEFAULT);
|
lv_obj_set_style_border_width(obj, 1, LV_STATE_DEFAULT);
|
||||||
} else {
|
|
||||||
// No border
|
|
||||||
lv_obj_set_style_border_color(obj, bg_color, LV_STATE_DEFAULT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_set_width(obj, LV_PCT(100));
|
lv_obj_set_width(obj, LV_PCT(100));
|
||||||
@ -197,7 +188,6 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
|||||||
lv_obj_set_style_pad_column(obj, icon_padding, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_column(obj, icon_padding, LV_STATE_DEFAULT);
|
||||||
|
|
||||||
statusbar->time = lv_label_create(obj);
|
statusbar->time = lv_label_create(obj);
|
||||||
lv_obj_set_style_text_color(statusbar->time, fg_color, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_margin_left(statusbar->time, 4, LV_STATE_DEFAULT);
|
lv_obj_set_style_margin_left(statusbar->time, 4, LV_STATE_DEFAULT);
|
||||||
update_time(statusbar);
|
update_time(statusbar);
|
||||||
|
|
||||||
@ -213,7 +203,6 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
|||||||
auto* image = lv_image_create(obj);
|
auto* image = lv_image_create(obj);
|
||||||
lv_obj_set_size(image, icon_size, icon_size); // regular padding doesn't work
|
lv_obj_set_size(image, icon_size, icon_size); // regular padding doesn't work
|
||||||
lv_obj_set_style_text_font(image, lvgl_get_statusbar_icon_font(), LV_STATE_DEFAULT);
|
lv_obj_set_style_text_font(image, lvgl_get_statusbar_icon_font(), LV_STATE_DEFAULT);
|
||||||
lv_obj_set_style_text_color(image, fg_color, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_pad_all(image, 0, LV_STATE_DEFAULT);
|
lv_obj_set_style_pad_all(image, 0, LV_STATE_DEFAULT);
|
||||||
statusbar->icons[i] = image;
|
statusbar->icons[i] = image;
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
#ifdef ESP_PLATFORM
|
|
||||||
|
|
||||||
#include <lvgl.h>
|
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
extern lv_obj_t* __real_lv_button_create(lv_obj_t* parent);
|
|
||||||
|
|
||||||
lv_obj_t* __wrap_lv_button_create(lv_obj_t* parent) {
|
|
||||||
auto button = __real_lv_button_create(parent);
|
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_all(button, 3, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_radius(button, 3, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ESP_PLATFORM
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
#ifdef ESP_PLATFORM
|
|
||||||
|
|
||||||
#include <lvgl.h>
|
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
extern lv_obj_t* __real_lv_dropdown_create(lv_obj_t* parent);
|
|
||||||
|
|
||||||
lv_obj_t* __wrap_lv_dropdown_create(lv_obj_t* parent) {
|
|
||||||
auto dropdown = __real_lv_dropdown_create(parent);
|
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_all(dropdown, 2, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_obj_set_style_border_width(dropdown, 1, LV_PART_MAIN);
|
|
||||||
if (lv_display_get_color_format(lv_obj_get_display(parent)) == LV_COLOR_FORMAT_L8) {
|
|
||||||
lv_obj_set_style_border_color(dropdown, lv_theme_get_color_secondary(parent), LV_PART_MAIN);
|
|
||||||
} else {
|
|
||||||
lv_obj_set_style_border_color(dropdown, lv_color_hex(0xFAFAFA), LV_PART_MAIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dropdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ESP_PLATFORM
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
#ifdef ESP_PLATFORM
|
|
||||||
|
|
||||||
#include <lvgl.h>
|
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
extern lv_obj_t* __real_lv_list_create(lv_obj_t* parent);
|
|
||||||
extern lv_obj_t* __real_lv_list_add_button(lv_obj_t* list, const void* icon, const char* txt);
|
|
||||||
|
|
||||||
lv_obj_t* __wrap_lv_list_create(lv_obj_t* parent) {
|
|
||||||
auto* list = __real_lv_list_create(parent);
|
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_row(list, 2, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_pad_column(list, 2, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_pad_all(list, 2, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_obj_t* __wrap_lv_list_add_button(lv_obj_t* list, const void* icon, const char* txt) {
|
|
||||||
auto* button = __real_lv_list_add_button(list, icon, txt);
|
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_ver(button, 2, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ESP_PLATFORM
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
#ifdef ESP_PLATFORM
|
|
||||||
|
|
||||||
#include <lvgl.h>
|
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
extern void __real_lv_obj_set_flex_flow(lv_obj_t* obj, lv_flex_flow_t flow);
|
|
||||||
extern lv_obj_t* __real_lv_obj_create(lv_obj_t* parent);
|
|
||||||
|
|
||||||
void __wrap_lv_obj_set_flex_flow(lv_obj_t* obj, lv_flex_flow_t flow) {
|
|
||||||
__real_lv_obj_set_flex_flow(obj, flow);
|
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_gap(obj, 4, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_obj_t* __wrap_lv_obj_create(lv_obj_t* parent) {
|
|
||||||
auto obj = __real_lv_obj_create(parent);
|
|
||||||
// Screens and display layers (a null parent, also used by LVGL's own display setup) must stay borderless and unpadded
|
|
||||||
if (parent != nullptr && lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_all(obj, 2, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_pad_gap(obj, 2, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_radius(obj, 3, LV_STATE_DEFAULT);
|
|
||||||
lv_obj_set_style_border_width(obj, 1, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ESP_PLATFORM
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
#ifdef ESP_PLATFORM
|
|
||||||
|
|
||||||
#include <lvgl.h>
|
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
extern lv_obj_t* __real_lv_switch_create(lv_obj_t* parent);
|
|
||||||
|
|
||||||
lv_obj_t* __wrap_lv_switch_create(lv_obj_t* parent) {
|
|
||||||
auto widget = __real_lv_switch_create(parent);
|
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_size(widget, 25, 15, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lv_display_get_color_format(lv_obj_get_display(parent)) == LV_COLOR_FORMAT_L8) {
|
|
||||||
lv_obj_set_style_bg_color(widget, lv_theme_get_color_secondary(parent), LV_PART_MAIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
return widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ESP_PLATFORM
|
|
||||||
@ -10,20 +10,11 @@ extern lv_obj_t* __real_lv_textarea_create(lv_obj_t* parent);
|
|||||||
lv_obj_t* __wrap_lv_textarea_create(lv_obj_t* parent) {
|
lv_obj_t* __wrap_lv_textarea_create(lv_obj_t* parent) {
|
||||||
auto textarea = __real_lv_textarea_create(parent);
|
auto textarea = __real_lv_textarea_create(parent);
|
||||||
|
|
||||||
if (lvgl_get_ui_density() == LVGL_UI_DENSITY_COMPACT) {
|
|
||||||
lv_obj_set_style_pad_all(textarea, 2, LV_STATE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* software_keyboard = lvgl_software_keyboard_get_last();
|
auto* software_keyboard = lvgl_software_keyboard_get_last();
|
||||||
if (software_keyboard != nullptr) {
|
if (software_keyboard != nullptr) {
|
||||||
lvgl_keyboard_add_textarea(software_keyboard, textarea);
|
lvgl_keyboard_add_textarea(software_keyboard, textarea);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lv_display_get_color_format(lv_obj_get_display(parent)) == LV_COLOR_FORMAT_L8) {
|
|
||||||
lv_obj_set_style_border_width(textarea, 1, LV_PART_MAIN);
|
|
||||||
lv_obj_set_style_border_color(textarea, lv_theme_get_color_secondary(parent), LV_PART_MAIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
return textarea;
|
return textarea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -81,7 +81,7 @@ dependencies:
|
|||||||
rules:
|
rules:
|
||||||
- if: "target in [esp32s3, esp32p4]"
|
- if: "target in [esp32s3, esp32p4]"
|
||||||
espressif/esp_lvgl_port: "2.7.2"
|
espressif/esp_lvgl_port: "2.7.2"
|
||||||
lvgl/lvgl: "9.3.0"
|
lvgl/lvgl: "9.4.0"
|
||||||
epdiy:
|
epdiy:
|
||||||
git: https://github.com/vroland/epdiy.git
|
git: https://github.com/vroland/epdiy.git
|
||||||
version: 2.1.3
|
version: 2.1.3
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user