diff --git a/CMakeLists.txt b/CMakeLists.txt index daec1f6c4..0a43f0afb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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=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) else () diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 89017ee85..946320791 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -7,7 +7,9 @@ ## 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. - NimBLE looses pairing key after reboot - CrashDiagnostics shouldn't show a QR when there's no callstack diff --git a/Modules/lvgl-module/include/lvgl/widgets/card.h b/Modules/lvgl-module/include/lvgl/widgets/card.h new file mode 100644 index 000000000..8b15f1617 --- /dev/null +++ b/Modules/lvgl-module/include/lvgl/widgets/card.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +#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 diff --git a/Modules/lvgl-module/include/lvgl/widgets/icon_button.h b/Modules/lvgl-module/include/lvgl/widgets/icon_button.h new file mode 100644 index 000000000..43d1c13b1 --- /dev/null +++ b/Modules/lvgl-module/include/lvgl/widgets/icon_button.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +#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 diff --git a/Modules/lvgl-module/source/devices/display.cpp b/Modules/lvgl-module/source/devices/display.cpp index 1c88ef8d2..4a7326810 100644 --- a/Modules/lvgl-module/source/devices/display.cpp +++ b/Modules/lvgl-module/source/devices/display.cpp @@ -1,8 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 -#define LV_USE_PRIVATE_API 1 // For the lv_theme_t declaration - #include +#include #include #include @@ -12,6 +11,9 @@ #include +#include "../themes/lv_theme_material.h" +#include "../themes/lv_theme_material_mono.h" + #include #ifdef ESP_PLATFORM @@ -20,37 +22,34 @@ constexpr auto* TAG = "lvgl_display"; -#if LV_USE_THEME_MONO - -static lv_style_t mono_button_style; -static bool mono_button_style_initialized = false; -static lv_theme_t mono_theme_extension; - -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); -} - +// 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) { +#if defined(LV_THEME_DEFAULT_DARK) && LV_THEME_DEFAULT_DARK + bool is_dark = true; +#else + bool is_dark = false; #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 { void* buf1; @@ -504,9 +503,7 @@ error_t lvgl_display_add(struct Device* device, const struct LvglDisplayConfig* return ERROR_OUT_OF_MEMORY; } -#if LV_USE_THEME_MONO - lvgl_display_extend_mono_theme(disp); -#endif + lvgl_display_apply_theme(disp, device, lv_color_format); ctx->render_mode = render_mode; lv_display_set_color_format(disp, lv_color_format); diff --git a/Modules/lvgl-module/source/symbols.c b/Modules/lvgl-module/source/symbols.c index e9e5b8199..80df6469c 100644 --- a/Modules/lvgl-module/source/symbols.c +++ b/Modules/lvgl-module/source/symbols.c @@ -10,6 +10,8 @@ #include #include +#include +#include #include #include @@ -56,6 +58,11 @@ const struct ModuleSymbol lvgl_module_symbols[] = { DEFINE_MODULE_SYMBOL(lvgl_pointer_add), DEFINE_MODULE_SYMBOL(lvgl_pointer_get_slot_index), 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 DEFINE_MODULE_SYMBOL(lvgl_spinner_create), // lvgl_toolbar diff --git a/Modules/lvgl-module/source/themes/lv_theme_material.c b/Modules/lvgl-module/source/themes/lv_theme_material.c new file mode 100644 index 000000000..47e34fdd5 --- /dev/null +++ b/Modules/lvgl-module/source/themes/lv_theme_material.c @@ -0,0 +1,2228 @@ +/* Generated by LSS. Do not edit. */ + +#define LV_USE_PRIVATE_API 1 /* For lv_theme_t */ + +#include "lv_theme_material.h" + +/********************* + * DEFINES + *********************/ + +extern const lv_obj_class_t lvgl_toolbar_class; +extern const lv_obj_class_t lvgl_icon_button_class; +extern const lv_obj_class_t lvgl_icon_button_filled_class; +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_card_class; + +#define NEUTRAL_4 lv_color_hex(0x0E0E0E) +#define NEUTRAL_6 lv_color_hex(0x131313) +#define NEUTRAL_10 lv_color_hex(0x1C1C1C) +#define NEUTRAL_12 lv_color_hex(0x1F1F1F) +#define NEUTRAL_17 lv_color_hex(0x2B2B2B) +#define NEUTRAL_20 lv_color_hex(0x303030) +#define NEUTRAL_22 lv_color_hex(0x343434) +#define NEUTRAL_30 lv_color_hex(0x474747) +#define NEUTRAL_50 lv_color_hex(0x777777) +#define NEUTRAL_60 lv_color_hex(0x919191) +#define NEUTRAL_80 lv_color_hex(0xC6C6C6) +#define NEUTRAL_90 lv_color_hex(0xE2E2E2) +#define NEUTRAL_92 lv_color_hex(0xE8E8E8) +#define NEUTRAL_94 lv_color_hex(0xEEEEEE) +#define NEUTRAL_95 lv_color_hex(0xF0F0F0) +#define NEUTRAL_96 lv_color_hex(0xF3F3F3) +#define NEUTRAL_98 lv_color_hex(0xFAFAFA) +#define NEUTRAL_100 lv_color_hex(0xFFFFFF) +#define TINT_NEUTRAL (10) +#define TINT_NEUTRAL_VARIANT (20) +#define CORNER_NONE 0 +#define CORNER_EXTRA_SMALL (LV_DPX_CALC(theme->disp_dpi, 4)) +#define CORNER_SMALL (LV_DPX_CALC(theme->disp_dpi, 8)) +#define CORNER_MEDIUM (LV_DPX_CALC(theme->disp_dpi, 12)) +#define CORNER_LARGE (LV_DPX_CALC(theme->disp_dpi, 16)) +#define CORNER_EXTRA_LARGE (LV_DPX_CALC(theme->disp_dpi, 28)) +#define CORNER_FULL LV_RADIUS_CIRCLE +#define CORNER_COMPACT 3 +#define SPACE_1 (LV_DPX_CALC(theme->disp_dpi, 4)) +#define SPACE_2 (LV_DPX_CALC(theme->disp_dpi, 8)) +#define SPACE_3 (LV_DPX_CALC(theme->disp_dpi, 12)) +#define SPACE_4 (LV_DPX_CALC(theme->disp_dpi, 16)) +#define SPACE_6 (LV_DPX_CALC(theme->disp_dpi, 24)) +#define OPACITY_STATE_HOVER (20) +#define OPACITY_STATE_FOCUS (LV_OPA_10) +#define OPACITY_STATE_PRESSED (LV_OPA_10) +#define OPACITY_DISABLED_CONTENT (97) +#define OPACITY_DISABLED_CONTAINER (31) +#define OPACITY_SCRIM (82) +#define DURATION_SHORT 100 +#define DURATION_MEDIUM 250 +#define DURATION_LONG 500 +#define COLOR_PRIMARY (theme->config.is_dark ? lv_color_mix(theme->config.color_primary, lv_color_white(), 140) : theme->config.color_primary) +#define COLOR_ON_PRIMARY (theme->config.is_dark ? lv_color_mix(theme->config.color_primary, lv_color_black(), LV_OPA_50) : lv_color_white()) +#define COLOR_PRIMARY_CONTAINER (theme->config.is_dark ? lv_color_mix(theme->config.color_primary, lv_color_black(), LV_OPA_70) : lv_color_mix(theme->config.color_primary, lv_color_white(), 64)) +#define COLOR_ON_PRIMARY_CONTAINER (theme->config.is_dark ? lv_color_mix(theme->config.color_primary, lv_color_white(), LV_OPA_20) : lv_color_mix(theme->config.color_primary, lv_color_black(), LV_OPA_60)) +#define COLOR_SECONDARY (theme->config.is_dark ? lv_color_mix(theme->config.color_secondary, lv_color_white(), 140) : theme->config.color_secondary) +#define COLOR_ON_SECONDARY (theme->config.is_dark ? lv_color_mix(theme->config.color_secondary, lv_color_black(), LV_OPA_50) : lv_color_white()) +#define COLOR_SECONDARY_CONTAINER (theme->config.is_dark ? lv_color_mix(theme->config.color_secondary, lv_color_black(), LV_OPA_70) : lv_color_mix(theme->config.color_secondary, lv_color_white(), 64)) +#define COLOR_ON_SECONDARY_CONTAINER (theme->config.is_dark ? lv_color_mix(theme->config.color_secondary, lv_color_white(), LV_OPA_20) : lv_color_mix(theme->config.color_secondary, lv_color_black(), LV_OPA_60)) +#define COLOR_ERROR (theme->config.is_dark ? lv_color_mix(theme->config.color_error, lv_color_white(), 140) : theme->config.color_error) +#define COLOR_ON_ERROR (theme->config.is_dark ? lv_color_mix(theme->config.color_error, lv_color_black(), LV_OPA_50) : lv_color_white()) +#define COLOR_SURFACE (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_6 : NEUTRAL_98, TINT_NEUTRAL)) +#define COLOR_ON_SURFACE (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_90 : NEUTRAL_10, TINT_NEUTRAL)) +#define COLOR_ON_SURFACE_VARIANT (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_80 : NEUTRAL_30, TINT_NEUTRAL_VARIANT)) +#define COLOR_SURFACE_CONTAINER_LOWEST (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_4 : NEUTRAL_100, TINT_NEUTRAL)) +#define COLOR_SURFACE_CONTAINER_LOW (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_10 : NEUTRAL_96, TINT_NEUTRAL)) +#define COLOR_SURFACE_CONTAINER (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_12 : NEUTRAL_94, TINT_NEUTRAL)) +#define COLOR_SURFACE_CONTAINER_HIGH (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_17 : NEUTRAL_92, TINT_NEUTRAL)) +#define COLOR_SURFACE_CONTAINER_HIGHEST (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_22 : NEUTRAL_90, TINT_NEUTRAL)) +#define COLOR_OUTLINE (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_60 : NEUTRAL_50, TINT_NEUTRAL_VARIANT)) +#define COLOR_OUTLINE_VARIANT (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_30 : NEUTRAL_80, TINT_NEUTRAL_VARIANT)) +#define COLOR_INVERSE_SURFACE (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_90 : NEUTRAL_20, TINT_NEUTRAL)) +#define COLOR_INVERSE_ON_SURFACE (lv_color_mix(theme->config.color_primary, theme->config.is_dark ? NEUTRAL_20 : NEUTRAL_95, TINT_NEUTRAL)) +#define COLOR_SCRIM lv_color_black() +#define SHAPE_CONTROL (theme->config.is_compact ? CORNER_COMPACT : CORNER_FULL) +#define SHAPE_CONTAINER (theme->config.is_compact ? CORNER_COMPACT : CORNER_MEDIUM) +#define SHAPE_FIELD (theme->config.is_compact ? CORNER_COMPACT : CORNER_EXTRA_SMALL) +#define SHAPE_KEY (theme->config.is_compact ? CORNER_COMPACT : CORNER_SMALL) +#define SHAPE_DIALOG (theme->config.is_compact ? CORNER_COMPACT : CORNER_EXTRA_LARGE) +#define SHAPE_MENU (theme->config.is_compact ? CORNER_COMPACT : CORNER_EXTRA_SMALL) +#define DENSITY_CONTAINER_PAD (theme->config.is_compact ? 2 : SPACE_4) +#define DENSITY_CONTAINER_GAP (theme->config.is_compact ? 2 : SPACE_3) +#define DENSITY_CONTAINER_BORDER (theme->config.is_compact ? 1 : 0) +#define DENSITY_CONTROL_PAD_HOR (theme->config.is_compact ? 3 : SPACE_6) +#define DENSITY_CONTROL_PAD_VER (theme->config.is_compact ? 3 : LV_DPX_CALC(theme->disp_dpi, 10)) +#define DENSITY_CONTROL_GAP (theme->config.is_compact ? 2 : SPACE_2) +#define DENSITY_FIELD_PAD_HOR (theme->config.is_compact ? 2 : SPACE_4) +#define DENSITY_FIELD_PAD_VER (theme->config.is_compact ? 2 : SPACE_2) +#define DENSITY_LIST_PAD (theme->config.is_compact ? 2 : SPACE_2) +#define DENSITY_LIST_ITEM_PAD_HOR (theme->config.is_compact ? 2 : SPACE_4) +#define DENSITY_LIST_ITEM_PAD_VER (theme->config.is_compact ? 2 : SPACE_3) +#define DENSITY_DIALOG_PAD (theme->config.is_compact ? 2 : SPACE_6) +#define DENSITY_KEY_GAP (theme->config.is_compact ? 2 : SPACE_1) +#define DENSITY_BORDER (LV_DPX_CALC(theme->disp_dpi, 1)) +#define FOCUS_COLOR COLOR_SECONDARY +#define FOCUS_WIDTH (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 3)) +#define FOCUS_OFFSET (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 2)) +#define FOCUS_INSET_WIDTH (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 2)) +#define EDIT_COLOR COLOR_PRIMARY +#define STATE_PRESSED_ON_PRIMARY (lv_color_mix(COLOR_ON_PRIMARY, COLOR_PRIMARY, OPACITY_STATE_PRESSED)) +#define STATE_PRESSED_ON_SECONDARY (lv_color_mix(COLOR_ON_SECONDARY, COLOR_SECONDARY, OPACITY_STATE_PRESSED)) +#define STATE_LAYER_COLOR COLOR_ON_SURFACE +#define MOTION_DURATION_SHORT (theme->config.animations_enabled ? DURATION_SHORT : 0) +#define MOTION_DURATION_MEDIUM (theme->config.animations_enabled ? DURATION_MEDIUM : 0) +#define MOTION_DURATION_LONG (theme->config.animations_enabled ? DURATION_LONG : 0) +#define SCREEN_COLOR COLOR_SURFACE +#define SCREEN_TEXT_COLOR COLOR_ON_SURFACE +#define SCREEN_GAP DENSITY_CONTAINER_GAP +#define CONTAINER_COLOR COLOR_SURFACE_CONTAINER_LOW +#define CONTAINER_TEXT_COLOR COLOR_ON_SURFACE +#define CONTAINER_BORDER_COLOR COLOR_OUTLINE_VARIANT +#define CONTAINER_BORDER_WIDTH DENSITY_CONTAINER_BORDER +#define CONTAINER_SHAPE SHAPE_CONTAINER +#define CONTAINER_PAD DENSITY_CONTAINER_PAD +#define CONTAINER_GAP DENSITY_CONTAINER_GAP +#define CARD_COLOR COLOR_SURFACE_CONTAINER_HIGH +#define CARD_TEXT_COLOR COLOR_ON_SURFACE +#define CARD_BORDER_COLOR COLOR_OUTLINE_VARIANT +#define CARD_BORDER_WIDTH DENSITY_CONTAINER_BORDER +#define CARD_SHAPE SHAPE_CONTAINER +#define CARD_PAD DENSITY_CONTAINER_PAD +#define CARD_GAP DENSITY_CONTAINER_GAP +#define SCROLLBAR_COLOR COLOR_OUTLINE +#define SCROLLBAR_OPACITY (LV_OPA_40) +#define SCROLLBAR_SCROLLED_OPACITY (LV_OPA_70) +#define SCROLLBAR_WIDTH (theme->config.is_compact ? 2 : SPACE_1) +#define SCROLLBAR_PAD (theme->config.is_compact ? 1 : SPACE_1) +#define BUTTON_CONTAINER_COLOR COLOR_PRIMARY +#define BUTTON_LABEL_COLOR COLOR_ON_PRIMARY +#define BUTTON_PRESSED_CONTAINER_COLOR STATE_PRESSED_ON_PRIMARY +#define BUTTON_CHECKED_CONTAINER_COLOR COLOR_SECONDARY +#define BUTTON_CHECKED_LABEL_COLOR COLOR_ON_SECONDARY +#define BUTTON_CHECKED_PRESSED_CONTAINER_COLOR STATE_PRESSED_ON_SECONDARY +#define BUTTON_DISABLED_CONTAINER_COLOR COLOR_ON_SURFACE +#define BUTTON_DISABLED_CONTAINER_OPACITY OPACITY_DISABLED_CONTAINER +#define BUTTON_DISABLED_LABEL_COLOR COLOR_ON_SURFACE +#define BUTTON_DISABLED_LABEL_OPACITY OPACITY_DISABLED_CONTENT +#define BUTTON_SHAPE SHAPE_CONTROL +#define BUTTON_PAD_HOR DENSITY_CONTROL_PAD_HOR +#define BUTTON_PAD_VER DENSITY_CONTROL_PAD_VER +#define BUTTON_GAP DENSITY_CONTROL_GAP +#define TEXT_BUTTON_LABEL_COLOR COLOR_PRIMARY +#define TEXT_BUTTON_PRESSED_CONTAINER_COLOR COLOR_PRIMARY +#define TEXT_BUTTON_PAD_HOR (theme->config.is_compact ? 3 : SPACE_3) +#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_FILLED_COLOR COLOR_PRIMARY +#define ICON_BUTTON_FILLED_ICON_COLOR COLOR_ON_PRIMARY +#define ICON_BUTTON_FILLED_PRESSED_COLOR STATE_PRESSED_ON_PRIMARY +#define ICON_BUTTON_TONAL_COLOR COLOR_SECONDARY_CONTAINER +#define ICON_BUTTON_TONAL_ICON_COLOR COLOR_ON_SECONDARY_CONTAINER +#define ICON_BUTTON_TONAL_PRESSED_COLOR (lv_color_mix(COLOR_ON_SECONDARY_CONTAINER, COLOR_SECONDARY_CONTAINER, OPACITY_STATE_PRESSED)) +#define APP_BAR_COLOR (lv_color_mix(theme->config.color_primary, NEUTRAL_22, TINT_NEUTRAL)) +#define APP_BAR_CONTENT_COLOR lv_color_white() +#define APP_BAR_PRESSED_OPACITY (LV_OPA_20) +#define APP_BAR_PAD_HOR (theme->config.is_compact ? 0 : SPACE_1) +#define APP_BAR_GAP (theme->config.is_compact ? 2 : SPACE_1) +#define APP_BAR_FOCUS_WIDTH FOCUS_INSET_WIDTH +#define FOCUS_RING_COLOR FOCUS_COLOR +#define FOCUS_RING_WIDTH FOCUS_WIDTH +#define FOCUS_RING_OFFSET FOCUS_OFFSET +#define EDIT_RING_COLOR EDIT_COLOR +#define SWITCH_WIDTH (theme->config.is_compact ? 25 : LV_DPX_CALC(theme->disp_dpi, 52)) +#define SWITCH_HEIGHT (theme->config.is_compact ? 15 : LV_DPX_CALC(theme->disp_dpi, 32)) +#define SWITCH_TRACK_COLOR COLOR_SURFACE_CONTAINER_HIGHEST +#define SWITCH_TRACK_BORDER_COLOR COLOR_OUTLINE +#define SWITCH_TRACK_BORDER_WIDTH (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 2)) +#define SWITCH_TRACK_SELECTED_COLOR COLOR_PRIMARY +#define SWITCH_HANDLE_COLOR COLOR_OUTLINE +#define SWITCH_HANDLE_SELECTED_COLOR COLOR_ON_PRIMARY +#define SWITCH_HANDLE_PAD (theme->config.is_compact ? -3 : -LV_DPX_CALC(theme->disp_dpi, 8)) +#define SWITCH_HANDLE_SELECTED_PAD (theme->config.is_compact ? -2 : -LV_DPX_CALC(theme->disp_dpi, 4)) +#define SWITCH_HANDLE_PRESSED_PAD (theme->config.is_compact ? -1 : -LV_DPX_CALC(theme->disp_dpi, 2)) +#define CHECKBOX_GAP (theme->config.is_compact ? 3 : SPACE_3) +#define CHECKBOX_BOX_COLOR COLOR_SURFACE +#define CHECKBOX_BOX_OPACITY LV_OPA_TRANSP +#define CHECKBOX_BOX_BORDER_COLOR COLOR_ON_SURFACE_VARIANT +#define CHECKBOX_BOX_BORDER_WIDTH (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 2)) +#define CHECKBOX_BOX_SHAPE (theme->config.is_compact ? 2 : LV_DPX_CALC(theme->disp_dpi, 2)) +#define CHECKBOX_BOX_PAD (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 2)) +#define CHECKBOX_BOX_SELECTED_COLOR COLOR_PRIMARY +#define CHECKBOX_ICON_COLOR COLOR_ON_PRIMARY +#define SLIDER_TRACK_COLOR COLOR_SECONDARY_CONTAINER +#define SLIDER_ACTIVE_TRACK_COLOR COLOR_PRIMARY +#define SLIDER_HANDLE_COLOR COLOR_PRIMARY +#define SLIDER_TRACK_HEIGHT (LV_DPX_CALC(theme->disp_dpi, 8)) +#define SLIDER_HANDLE_PAD ((theme->config.is_compact ? 2 : LV_DPX_CALC(theme->disp_dpi, 6)) + LV_DPX_CALC(theme->disp_dpi, 4)) +#define SLIDERBOX_GAP (theme->config.is_compact ? 4 : ((SLIDER_TRACK_HEIGHT / 2) + SLIDER_HANDLE_PAD) + SPACE_2) +#define PROGRESS_TRACK_COLOR COLOR_SECONDARY_CONTAINER +#define PROGRESS_INDICATOR_COLOR COLOR_PRIMARY +#define ARC_TRACK_COLOR COLOR_SECONDARY_CONTAINER +#define ARC_INDICATOR_COLOR COLOR_PRIMARY +#define ARC_WIDTH (theme->config.is_compact ? 3 : SPACE_1) +#define ARC_HANDLE_COLOR COLOR_PRIMARY +#define ARC_HANDLE_PAD (theme->config.is_compact ? 2 : SPACE_1) +#define FIELD_COLOR COLOR_SURFACE +#define FIELD_OPACITY LV_OPA_TRANSP +#define FIELD_TEXT_COLOR COLOR_ON_SURFACE +#define FIELD_BORDER_COLOR COLOR_OUTLINE +#define FIELD_BORDER_WIDTH DENSITY_BORDER +#define FIELD_FOCUSED_BORDER_COLOR COLOR_PRIMARY +#define FIELD_FOCUSED_BORDER_WIDTH (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 2)) +#define FIELD_SHAPE SHAPE_FIELD +#define FIELD_PAD_HOR DENSITY_FIELD_PAD_HOR +#define FIELD_PAD_VER DENSITY_FIELD_PAD_VER +#define FIELD_PLACEHOLDER_COLOR COLOR_ON_SURFACE_VARIANT +#define FIELD_CURSOR_COLOR COLOR_PRIMARY +#define FIELD_CURSOR_WIDTH (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 2)) +#define FIELD_SELECTION_COLOR COLOR_PRIMARY_CONTAINER +#define FIELD_SELECTION_TEXT_COLOR COLOR_ON_PRIMARY_CONTAINER +#define SPINBOX_CURSOR_COLOR COLOR_PRIMARY +#define SPINBOX_CURSOR_TEXT_COLOR COLOR_ON_PRIMARY +#define MENU_COLOR COLOR_SURFACE_CONTAINER +#define MENU_TEXT_COLOR COLOR_ON_SURFACE +#define MENU_SHAPE SHAPE_MENU +#define MENU_PAD (theme->config.is_compact ? 1 : SPACE_2) +#define MENU_MAX_HEIGHT (LV_DPI_DEF * 2) +#define MENU_ITEM_SELECTED_COLOR COLOR_SECONDARY_CONTAINER +#define MENU_ITEM_SELECTED_TEXT_COLOR COLOR_ON_SECONDARY_CONTAINER +#define MENU_ITEM_LINE_SPACE (theme->config.is_compact ? 4 : SPACE_4) +#define ROLLER_COLOR COLOR_SURFACE_CONTAINER +#define ROLLER_TEXT_COLOR COLOR_ON_SURFACE_VARIANT +#define ROLLER_SELECTED_COLOR COLOR_SECONDARY_CONTAINER +#define ROLLER_SELECTED_TEXT_COLOR COLOR_ON_SECONDARY_CONTAINER +#define ROLLER_LINE_SPACE (theme->config.is_compact ? 4 : SPACE_4) +#define KEYS_COLOR COLOR_SURFACE_CONTAINER +#define KEYS_PAD DENSITY_KEY_GAP +#define KEYS_GAP DENSITY_KEY_GAP +#define KEY_COLOR COLOR_SURFACE_CONTAINER_HIGHEST +#define KEY_TEXT_COLOR COLOR_ON_SURFACE +#define KEY_SHAPE SHAPE_KEY +#define KEY_CHECKED_COLOR COLOR_SECONDARY_CONTAINER +#define KEY_CHECKED_TEXT_COLOR COLOR_ON_SECONDARY_CONTAINER +#define KEY_SELECTED_COLOR COLOR_PRIMARY +#define KEY_SELECTED_TEXT_COLOR COLOR_ON_PRIMARY +#define LIST_COLOR COLOR_SURFACE +#define LIST_PAD DENSITY_LIST_PAD +#define LIST_ITEM_COLOR COLOR_SURFACE +#define LIST_ITEM_OPACITY LV_OPA_TRANSP +#define LIST_ITEM_TEXT_COLOR COLOR_ON_SURFACE +#define LIST_ITEM_PAD_HOR DENSITY_LIST_ITEM_PAD_HOR +#define LIST_ITEM_PAD_VER DENSITY_LIST_ITEM_PAD_VER +#define LIST_ITEM_GAP (theme->config.is_compact ? 3 : SPACE_4) +#define LIST_ITEM_FOCUSED_COLOR COLOR_SECONDARY_CONTAINER +#define LIST_ITEM_FOCUSED_TEXT_COLOR COLOR_ON_SECONDARY_CONTAINER +#define LIST_DIVIDER_COLOR COLOR_OUTLINE_VARIANT +#define LIST_DIVIDER_WIDTH DENSITY_BORDER +#define LIST_SUBHEADER_TEXT_COLOR COLOR_ON_SURFACE_VARIANT +#define TABS_DIVIDER_COLOR COLOR_OUTLINE_VARIANT +#define TAB_LABEL_COLOR COLOR_ON_SURFACE_VARIANT +#define TAB_SELECTED_LABEL_COLOR COLOR_PRIMARY +#define TAB_INDICATOR_COLOR COLOR_PRIMARY +#define TAB_INDICATOR_WIDTH (theme->config.is_compact ? 2 : LV_DPX_CALC(theme->disp_dpi, 3)) +#define TAB_PAGE_PAD DENSITY_CONTAINER_PAD +#define DIALOG_COLOR COLOR_SURFACE_CONTAINER_HIGH +#define DIALOG_TEXT_COLOR COLOR_ON_SURFACE_VARIANT +#define DIALOG_TITLE_COLOR COLOR_ON_SURFACE +#define DIALOG_SHAPE SHAPE_DIALOG +#define DIALOG_PAD DENSITY_DIALOG_PAD +#define DIALOG_GAP (theme->config.is_compact ? 2 : SPACE_2) +#define DIALOG_SCRIM_COLOR COLOR_SCRIM +#define DIALOG_SCRIM_OPACITY OPACITY_SCRIM +#define SETTINGS_COLOR COLOR_SURFACE +#define SETTINGS_SECTION_COLOR COLOR_SURFACE_CONTAINER_LOW +#define SETTINGS_SECTION_SHAPE SHAPE_CONTAINER +#define SETTINGS_ITEM_PAD_HOR DENSITY_LIST_ITEM_PAD_HOR +#define SETTINGS_ITEM_PAD_VER DENSITY_LIST_ITEM_PAD_VER +#define SETTINGS_ITEM_GAP DENSITY_CONTAINER_GAP +#define SETTINGS_DIVIDER_COLOR COLOR_OUTLINE_VARIANT +#define SETTINGS_ITEM_SELECTED_COLOR COLOR_SECONDARY_CONTAINER +#define SETTINGS_ITEM_SELECTED_TEXT_COLOR COLOR_ON_SECONDARY_CONTAINER +#define WINDOW_HEADER_COLOR COLOR_SURFACE_CONTAINER +#define WINDOW_HEADER_PAD (theme->config.is_compact ? 2 : SPACE_2) +#define WINDOW_CONTENT_PAD DENSITY_CONTAINER_PAD +#define TABLE_COLOR COLOR_SURFACE +#define TABLE_CELL_TEXT_COLOR COLOR_ON_SURFACE +#define TABLE_CELL_PAD (theme->config.is_compact ? 2 : SPACE_3) +#define TABLE_DIVIDER_COLOR COLOR_OUTLINE_VARIANT +#define CHART_COLOR COLOR_SURFACE_CONTAINER_LOW +#define CHART_GRID_COLOR COLOR_OUTLINE_VARIANT +#define CHART_SERIES_WIDTH (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 2)) +#define CHART_POINT_SIZE (theme->config.is_compact ? 3 : LV_DPX_CALC(theme->disp_dpi, 6)) +#define CHART_INDICATOR_COLOR COLOR_PRIMARY +#define CALENDAR_COLOR COLOR_SURFACE_CONTAINER_HIGH +#define CALENDAR_SHAPE (theme->config.is_compact ? CORNER_COMPACT : CORNER_LARGE) +#define CALENDAR_PAD (theme->config.is_compact ? 2 : SPACE_2) +#define CALENDAR_DAY_TEXT_COLOR COLOR_ON_SURFACE +#define CALENDAR_HEADER_PAD (theme->config.is_compact ? 2 : SPACE_3) +#define SCALE_COLOR COLOR_ON_SURFACE_VARIANT +#define SCALE_WIDTH DENSITY_BORDER +#define SCALE_TICK_LENGTH (theme->config.is_compact ? 3 : LV_DPX_CALC(theme->disp_dpi, 6)) +#define DIVIDER_COLOR COLOR_OUTLINE_VARIANT +#define DIVIDER_WIDTH DENSITY_BORDER +#define LED_GLOW_WIDTH (theme->config.is_compact ? 4 : LV_DPX_CALC(theme->disp_dpi, 15)) +#define LED_GLOW_SPREAD (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 5)) + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_style_t transition_enter; + lv_style_t transition_exit; + lv_style_t focus_ring; + lv_style_t edit_ring; + lv_style_t focus_ring_inset; + lv_style_t edit_ring_inset; + lv_style_t state_pressed; + lv_style_t disabled; + lv_style_t scrollbar; + lv_style_t scrollbar_scrolled; +#if LV_USE_TABVIEW || LV_USE_TILEVIEW || LV_USE_WIN + lv_style_t page; +#endif + lv_style_t screen; + lv_style_t container; + lv_style_t card; +#if LV_USE_BUTTON + lv_style_t button; +#endif +#if LV_USE_BUTTON + lv_style_t button_pressed; +#endif +#if LV_USE_BUTTON + lv_style_t button_checked; +#endif +#if LV_USE_BUTTON + lv_style_t button_checked_pressed; +#endif + lv_style_t button_disabled; +#if LV_USE_MSGBOX + lv_style_t text_button; +#endif +#if LV_USE_MSGBOX + lv_style_t text_button_pressed; +#endif + lv_style_t icon_button; + lv_style_t icon_button_selected; + lv_style_t icon_button_filled; + lv_style_t icon_button_filled_pressed; + lv_style_t icon_button_tonal; + lv_style_t icon_button_tonal_pressed; + lv_style_t app_bar; + lv_style_t app_bar_actions; + lv_style_t app_bar_button; + lv_style_t app_bar_button_pressed; + lv_style_t app_bar_focus_ring; +#if LV_USE_DROPDOWN + lv_style_t app_bar_field; +#endif +#if LV_USE_SWITCH + lv_style_t switch_track; +#endif +#if LV_USE_SWITCH + lv_style_t switch_track_selected; +#endif +#if LV_USE_SWITCH + lv_style_t switch_indicator_selected; +#endif +#if LV_USE_SWITCH + lv_style_t switch_handle; +#endif +#if LV_USE_SWITCH + lv_style_t switch_handle_selected; +#endif +#if LV_USE_SWITCH + lv_style_t switch_handle_pressed; +#endif +#if LV_USE_CHECKBOX + lv_style_t checkbox; +#endif +#if LV_USE_CHECKBOX + lv_style_t checkbox_box; +#endif +#if LV_USE_CHECKBOX + lv_style_t checkbox_box_selected; +#endif + lv_style_t sliderbox; +#if LV_USE_SLIDER + lv_style_t slider_track_size; +#endif +#if LV_USE_SLIDER + lv_style_t slider_knob_room; +#endif +#if LV_USE_SLIDER + lv_style_t slider_track; +#endif +#if LV_USE_SLIDER + lv_style_t slider_active_track; +#endif +#if LV_USE_SLIDER + lv_style_t slider_handle; +#endif +#if LV_USE_BAR + lv_style_t progress_track; +#endif +#if LV_USE_BAR + lv_style_t progress_indicator; +#endif +#if LV_USE_ARC || LV_USE_SPINNER + lv_style_t arc_track; +#endif +#if LV_USE_ARC || LV_USE_SPINNER + lv_style_t arc_indicator; +#endif +#if LV_USE_ARC + lv_style_t arc_handle; +#endif +#if LV_USE_DROPDOWN || LV_USE_SPINBOX || LV_USE_TEXTAREA + lv_style_t field; +#endif +#if LV_USE_SPINBOX || LV_USE_TEXTAREA + lv_style_t field_focused; +#endif +#if LV_USE_TEXTAREA + lv_style_t field_placeholder; +#endif +#if LV_USE_TEXTAREA + lv_style_t field_cursor; +#endif +#if LV_USE_LABEL && LV_USE_TEXTAREA + lv_style_t field_selection; +#endif +#if LV_USE_SPINBOX + lv_style_t spinbox_cursor; +#endif +#if LV_USE_DROPDOWN + lv_style_t menu; +#endif +#if LV_USE_DROPDOWN + lv_style_t menu_item; +#endif +#if LV_USE_DROPDOWN + lv_style_t menu_item_selected; +#endif +#if LV_USE_ROLLER + lv_style_t roller; +#endif +#if LV_USE_ROLLER + lv_style_t roller_selected; +#endif +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + lv_style_t keys; +#endif +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + lv_style_t key; +#endif +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + lv_style_t key_pressed; +#endif +#if LV_USE_KEYBOARD + lv_style_t key_checked; +#endif +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + lv_style_t key_selected; +#endif +#if LV_USE_LIST + lv_style_t list; +#endif +#if LV_USE_LIST + lv_style_t list_item; +#endif +#if LV_USE_LIST || LV_USE_TABLE + lv_style_t list_item_focused; +#endif +#if LV_USE_LIST + lv_style_t list_subheader; +#endif +#if LV_USE_TABVIEW + lv_style_t tabs; +#endif +#if LV_USE_BUTTON && LV_USE_TABVIEW + lv_style_t tab; +#endif +#if LV_USE_BUTTON && LV_USE_TABVIEW + lv_style_t tab_selected; +#endif +#if LV_USE_TABVIEW + lv_style_t tab_page; +#endif +#if LV_USE_MSGBOX + lv_style_t dialog; +#endif +#if LV_USE_MSGBOX + lv_style_t dialog_header; +#endif +#if LV_USE_LABEL && LV_USE_MSGBOX + lv_style_t dialog_title; +#endif +#if LV_USE_MSGBOX + lv_style_t dialog_content; +#endif +#if LV_USE_MSGBOX + lv_style_t dialog_actions; +#endif +#if LV_USE_MSGBOX + lv_style_t dialog_scrim; +#endif +#if LV_USE_MENU + lv_style_t settings; +#endif +#if LV_USE_MENU + lv_style_t settings_sidebar; +#endif +#if LV_USE_MENU + lv_style_t settings_content; +#endif +#if LV_USE_MENU + lv_style_t settings_page; +#endif +#if LV_USE_MENU + lv_style_t settings_header; +#endif +#if LV_USE_MENU + lv_style_t settings_section; +#endif +#if LV_USE_MENU + lv_style_t settings_item; +#endif +#if LV_USE_MENU + lv_style_t settings_item_selected; +#endif +#if LV_USE_MENU + lv_style_t settings_separator; +#endif +#if LV_USE_WIN + lv_style_t window_header; +#endif +#if LV_USE_LABEL && LV_USE_WIN + lv_style_t window_title; +#endif +#if LV_USE_WIN + lv_style_t window_content; +#endif +#if LV_USE_TABLE + lv_style_t table; +#endif +#if LV_USE_TABLE + lv_style_t table_cell; +#endif +#if LV_USE_CHART + lv_style_t chart; +#endif +#if LV_USE_CHART + lv_style_t chart_series; +#endif +#if LV_USE_CHART + lv_style_t chart_indicator; +#endif +#if LV_USE_CALENDAR + lv_style_t calendar; +#endif +#if LV_USE_BUTTONMATRIX && LV_USE_CALENDAR + lv_style_t calendar_days; +#endif +#if LV_USE_BUTTONMATRIX && LV_USE_CALENDAR + lv_style_t calendar_day; +#endif +#if (LV_USE_CALENDAR && LV_USE_CALENDAR_HEADER_ARROW) || (LV_USE_CALENDAR && LV_USE_CALENDAR_HEADER_DROPDOWN) + lv_style_t calendar_header; +#endif +#if LV_USE_SCALE + lv_style_t scale; +#endif +#if LV_USE_LINE + lv_style_t divider; +#endif +#if LV_USE_LED + lv_style_t led; +#endif +} my_theme_styles_t; + +typedef enum { + DISP_SMALL = 3, + DISP_MEDIUM = 2, + DISP_LARGE = 1, +} disp_size_t; + +typedef struct { + lv_theme_t base; + disp_size_t disp_size; + int32_t disp_dpi; + lv_theme_material_config_t config; + bool inited; + my_theme_styles_t styles; + lv_style_transition_dsc_t trans_state_enter; + lv_style_transition_dsc_t trans_state_exit; +} my_theme_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void style_init_reset(lv_style_t * style); +static void theme_apply(lv_theme_t * th, lv_obj_t * obj); +static void resolution_change_event_cb(lv_event_t * e); + +/********************** + * STATIC VARIABLES + **********************/ + +static my_theme_t * theme_def = NULL; + +/********************** + * STATIC FUNCTIONS + **********************/ + +static inline lv_obj_t * lss_ancestor(lv_obj_t * obj, uint32_t depth) +{ + while(obj != NULL && depth > 0) { + obj = lv_obj_get_parent(obj); + depth--; + } + return obj; +} + +static inline bool lss_is_child(lv_obj_t * parent, int32_t index, lv_obj_t * child) +{ + return parent != NULL && lv_obj_get_child(parent, index) == child; +} + +static bool config_equals(const lv_theme_material_config_t * a, const lv_theme_material_config_t * b) +{ + return a->is_dark == b->is_dark && + a->is_compact == b->is_compact && + a->animations_enabled == b->animations_enabled && + lv_color_eq(a->color_primary, b->color_primary) && + lv_color_eq(a->color_secondary, b->color_secondary) && + lv_color_eq(a->color_error, b->color_error) && + a->font_small == b->font_small && + a->font_normal == b->font_normal && + a->font_large == b->font_large; +} + +static void style_init(my_theme_t * theme) +{ + static const lv_style_prop_t trans_state_enter_props[] = { + LV_STYLE_BG_COLOR, LV_STYLE_BG_OPA, LV_STYLE_BORDER_COLOR, LV_STYLE_TEXT_COLOR, LV_STYLE_OUTLINE_WIDTH, LV_STYLE_OUTLINE_OPA, + 0 + }; + lv_style_transition_dsc_init(&theme->trans_state_enter, trans_state_enter_props, lv_anim_path_ease_out, MOTION_DURATION_SHORT, 0, NULL); + + static const lv_style_prop_t trans_state_exit_props[] = { + LV_STYLE_BG_COLOR, LV_STYLE_BG_OPA, LV_STYLE_BORDER_COLOR, LV_STYLE_TEXT_COLOR, LV_STYLE_OUTLINE_WIDTH, LV_STYLE_OUTLINE_OPA, + 0 + }; + lv_style_transition_dsc_init(&theme->trans_state_exit, trans_state_exit_props, lv_anim_path_ease_out, MOTION_DURATION_SHORT, 70, NULL); + + style_init_reset(&theme->styles.transition_enter); + if(theme->config.animations_enabled) { + lv_style_set_transition(&theme->styles.transition_enter, &theme->trans_state_enter); + } + + style_init_reset(&theme->styles.transition_exit); + if(theme->config.animations_enabled) { + lv_style_set_transition(&theme->styles.transition_exit, &theme->trans_state_exit); + } + + style_init_reset(&theme->styles.focus_ring); + lv_style_set_outline_color(&theme->styles.focus_ring, FOCUS_RING_COLOR); + lv_style_set_outline_width(&theme->styles.focus_ring, FOCUS_RING_WIDTH); + lv_style_set_outline_pad(&theme->styles.focus_ring, FOCUS_RING_OFFSET); + lv_style_set_outline_opa(&theme->styles.focus_ring, LV_OPA_COVER); + + style_init_reset(&theme->styles.edit_ring); + lv_style_set_outline_color(&theme->styles.edit_ring, EDIT_RING_COLOR); + lv_style_set_outline_width(&theme->styles.edit_ring, FOCUS_RING_WIDTH); + lv_style_set_outline_pad(&theme->styles.edit_ring, FOCUS_RING_OFFSET); + lv_style_set_outline_opa(&theme->styles.edit_ring, LV_OPA_COVER); + + style_init_reset(&theme->styles.focus_ring_inset); + lv_style_set_outline_color(&theme->styles.focus_ring_inset, FOCUS_RING_COLOR); + lv_style_set_outline_width(&theme->styles.focus_ring_inset, FOCUS_INSET_WIDTH); + lv_style_set_outline_pad(&theme->styles.focus_ring_inset, -FOCUS_INSET_WIDTH); + lv_style_set_outline_opa(&theme->styles.focus_ring_inset, LV_OPA_COVER); + + style_init_reset(&theme->styles.edit_ring_inset); + lv_style_set_outline_color(&theme->styles.edit_ring_inset, EDIT_RING_COLOR); + lv_style_set_outline_width(&theme->styles.edit_ring_inset, FOCUS_INSET_WIDTH); + lv_style_set_outline_pad(&theme->styles.edit_ring_inset, -FOCUS_INSET_WIDTH); + lv_style_set_outline_opa(&theme->styles.edit_ring_inset, LV_OPA_COVER); + + style_init_reset(&theme->styles.state_pressed); + lv_style_set_bg_color(&theme->styles.state_pressed, STATE_LAYER_COLOR); + lv_style_set_bg_opa(&theme->styles.state_pressed, OPACITY_STATE_PRESSED); + + style_init_reset(&theme->styles.disabled); + lv_style_set_opa(&theme->styles.disabled, OPACITY_DISABLED_CONTENT); + + style_init_reset(&theme->styles.scrollbar); + lv_style_set_bg_color(&theme->styles.scrollbar, SCROLLBAR_COLOR); + lv_style_set_bg_opa(&theme->styles.scrollbar, SCROLLBAR_OPACITY); + lv_style_set_radius(&theme->styles.scrollbar, LV_RADIUS_CIRCLE); + lv_style_set_width(&theme->styles.scrollbar, SCROLLBAR_WIDTH); + lv_style_set_pad_all(&theme->styles.scrollbar, SCROLLBAR_PAD); + + style_init_reset(&theme->styles.scrollbar_scrolled); + lv_style_set_bg_opa(&theme->styles.scrollbar_scrolled, SCROLLBAR_SCROLLED_OPACITY); + +#if LV_USE_TABVIEW || LV_USE_TILEVIEW || LV_USE_WIN + style_init_reset(&theme->styles.page); + lv_style_set_bg_opa(&theme->styles.page, LV_OPA_TRANSP); + lv_style_set_border_width(&theme->styles.page, 0); + lv_style_set_radius(&theme->styles.page, 0); + lv_style_set_pad_all(&theme->styles.page, 0); + lv_style_set_pad_gap(&theme->styles.page, 0); +#endif + + style_init_reset(&theme->styles.screen); + lv_style_set_bg_color(&theme->styles.screen, SCREEN_COLOR); + lv_style_set_bg_opa(&theme->styles.screen, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.screen, SCREEN_TEXT_COLOR); + lv_style_set_text_font(&theme->styles.screen, theme->config.font_normal); + lv_style_set_pad_row(&theme->styles.screen, SCREEN_GAP); + lv_style_set_pad_column(&theme->styles.screen, SCREEN_GAP); + lv_style_set_rotary_sensitivity(&theme->styles.screen, (theme->disp_dpi / 4) * 256); + + style_init_reset(&theme->styles.container); + lv_style_set_bg_color(&theme->styles.container, CONTAINER_COLOR); + lv_style_set_bg_opa(&theme->styles.container, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.container, CONTAINER_TEXT_COLOR); + lv_style_set_border_color(&theme->styles.container, CONTAINER_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.container, CONTAINER_BORDER_WIDTH); + lv_style_set_border_post(&theme->styles.container, true); + lv_style_set_radius(&theme->styles.container, CONTAINER_SHAPE); + lv_style_set_pad_all(&theme->styles.container, CONTAINER_PAD); + lv_style_set_pad_gap(&theme->styles.container, CONTAINER_GAP); + lv_style_set_line_color(&theme->styles.container, DIVIDER_COLOR); + lv_style_set_line_width(&theme->styles.container, DIVIDER_WIDTH); + + style_init_reset(&theme->styles.card); + lv_style_set_bg_color(&theme->styles.card, CARD_COLOR); + lv_style_set_bg_opa(&theme->styles.card, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.card, CARD_TEXT_COLOR); + lv_style_set_border_color(&theme->styles.card, CARD_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.card, CARD_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.card, CARD_SHAPE); + lv_style_set_pad_all(&theme->styles.card, CARD_PAD); + lv_style_set_pad_gap(&theme->styles.card, CARD_GAP); + +#if LV_USE_BUTTON + style_init_reset(&theme->styles.button); + lv_style_set_bg_color(&theme->styles.button, BUTTON_CONTAINER_COLOR); + lv_style_set_bg_opa(&theme->styles.button, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.button, BUTTON_LABEL_COLOR); + lv_style_set_radius(&theme->styles.button, BUTTON_SHAPE); + lv_style_set_pad_hor(&theme->styles.button, BUTTON_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.button, BUTTON_PAD_VER); + lv_style_set_pad_gap(&theme->styles.button, BUTTON_GAP); +#endif + +#if LV_USE_BUTTON + style_init_reset(&theme->styles.button_pressed); + lv_style_set_bg_color(&theme->styles.button_pressed, BUTTON_PRESSED_CONTAINER_COLOR); +#endif + +#if LV_USE_BUTTON + style_init_reset(&theme->styles.button_checked); + lv_style_set_bg_color(&theme->styles.button_checked, BUTTON_CHECKED_CONTAINER_COLOR); + lv_style_set_text_color(&theme->styles.button_checked, BUTTON_CHECKED_LABEL_COLOR); +#endif + +#if LV_USE_BUTTON + style_init_reset(&theme->styles.button_checked_pressed); + lv_style_set_bg_color(&theme->styles.button_checked_pressed, BUTTON_CHECKED_PRESSED_CONTAINER_COLOR); +#endif + + style_init_reset(&theme->styles.button_disabled); + lv_style_set_bg_color(&theme->styles.button_disabled, BUTTON_DISABLED_CONTAINER_COLOR); + lv_style_set_bg_opa(&theme->styles.button_disabled, BUTTON_DISABLED_CONTAINER_OPACITY); + lv_style_set_text_color(&theme->styles.button_disabled, BUTTON_DISABLED_LABEL_COLOR); + lv_style_set_text_opa(&theme->styles.button_disabled, BUTTON_DISABLED_LABEL_OPACITY); + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.text_button); + lv_style_set_bg_opa(&theme->styles.text_button, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.text_button, TEXT_BUTTON_LABEL_COLOR); + lv_style_set_radius(&theme->styles.text_button, BUTTON_SHAPE); + lv_style_set_pad_hor(&theme->styles.text_button, TEXT_BUTTON_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.text_button, BUTTON_PAD_VER); + lv_style_set_pad_gap(&theme->styles.text_button, BUTTON_GAP); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.text_button_pressed); + lv_style_set_bg_color(&theme->styles.text_button_pressed, TEXT_BUTTON_PRESSED_CONTAINER_COLOR); + lv_style_set_bg_opa(&theme->styles.text_button_pressed, OPACITY_STATE_PRESSED); +#endif + + style_init_reset(&theme->styles.icon_button); + lv_style_set_bg_opa(&theme->styles.icon_button, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.icon_button, ICON_BUTTON_COLOR); + lv_style_set_radius(&theme->styles.icon_button, BUTTON_SHAPE); + 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); + 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_text_color(&theme->styles.icon_button_filled, ICON_BUTTON_FILLED_ICON_COLOR); + lv_style_set_radius(&theme->styles.icon_button_filled, ICON_BUTTON_CONTAINER_SHAPE); + lv_style_set_pad_all(&theme->styles.icon_button_filled, BUTTON_PAD_VER); + + style_init_reset(&theme->styles.icon_button_filled_pressed); + lv_style_set_bg_color(&theme->styles.icon_button_filled_pressed, ICON_BUTTON_FILLED_PRESSED_COLOR); + + style_init_reset(&theme->styles.icon_button_tonal); + lv_style_set_bg_color(&theme->styles.icon_button_tonal, ICON_BUTTON_TONAL_COLOR); + lv_style_set_bg_opa(&theme->styles.icon_button_tonal, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.icon_button_tonal, ICON_BUTTON_TONAL_ICON_COLOR); + lv_style_set_radius(&theme->styles.icon_button_tonal, ICON_BUTTON_CONTAINER_SHAPE); + lv_style_set_pad_all(&theme->styles.icon_button_tonal, BUTTON_PAD_VER); + + style_init_reset(&theme->styles.icon_button_tonal_pressed); + lv_style_set_bg_color(&theme->styles.icon_button_tonal_pressed, ICON_BUTTON_TONAL_PRESSED_COLOR); + + style_init_reset(&theme->styles.app_bar); + lv_style_set_bg_color(&theme->styles.app_bar, APP_BAR_COLOR); + lv_style_set_bg_opa(&theme->styles.app_bar, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.app_bar, APP_BAR_CONTENT_COLOR); + lv_style_set_radius(&theme->styles.app_bar, 0); + lv_style_set_border_width(&theme->styles.app_bar, 0); + lv_style_set_pad_hor(&theme->styles.app_bar, APP_BAR_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.app_bar, 0); + 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); + 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); + + style_init_reset(&theme->styles.app_bar_button_pressed); + lv_style_set_bg_color(&theme->styles.app_bar_button_pressed, APP_BAR_CONTENT_COLOR); + lv_style_set_bg_opa(&theme->styles.app_bar_button_pressed, APP_BAR_PRESSED_OPACITY); + + style_init_reset(&theme->styles.app_bar_focus_ring); + lv_style_set_outline_color(&theme->styles.app_bar_focus_ring, APP_BAR_CONTENT_COLOR); + lv_style_set_outline_width(&theme->styles.app_bar_focus_ring, APP_BAR_FOCUS_WIDTH); + lv_style_set_outline_pad(&theme->styles.app_bar_focus_ring, -APP_BAR_FOCUS_WIDTH); + lv_style_set_outline_opa(&theme->styles.app_bar_focus_ring, LV_OPA_COVER); + +#if LV_USE_DROPDOWN + style_init_reset(&theme->styles.app_bar_field); + lv_style_set_text_color(&theme->styles.app_bar_field, APP_BAR_CONTENT_COLOR); + lv_style_set_border_color(&theme->styles.app_bar_field, APP_BAR_CONTENT_COLOR); +#endif + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_track); + lv_style_set_width(&theme->styles.switch_track, SWITCH_WIDTH); + lv_style_set_height(&theme->styles.switch_track, SWITCH_HEIGHT); + lv_style_set_bg_color(&theme->styles.switch_track, SWITCH_TRACK_COLOR); + lv_style_set_bg_opa(&theme->styles.switch_track, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.switch_track, SWITCH_TRACK_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.switch_track, SWITCH_TRACK_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.switch_track, LV_RADIUS_CIRCLE); + lv_style_set_anim_duration(&theme->styles.switch_track, MOTION_DURATION_SHORT); +#endif + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_track_selected); + lv_style_set_border_color(&theme->styles.switch_track_selected, SWITCH_TRACK_SELECTED_COLOR); +#endif + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_indicator_selected); + lv_style_set_bg_color(&theme->styles.switch_indicator_selected, SWITCH_TRACK_SELECTED_COLOR); + lv_style_set_bg_opa(&theme->styles.switch_indicator_selected, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.switch_indicator_selected, LV_RADIUS_CIRCLE); +#endif + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_handle); + lv_style_set_bg_color(&theme->styles.switch_handle, SWITCH_HANDLE_COLOR); + lv_style_set_bg_opa(&theme->styles.switch_handle, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.switch_handle, LV_RADIUS_CIRCLE); + lv_style_set_pad_all(&theme->styles.switch_handle, SWITCH_HANDLE_PAD); +#endif + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_handle_selected); + lv_style_set_bg_color(&theme->styles.switch_handle_selected, SWITCH_HANDLE_SELECTED_COLOR); + lv_style_set_pad_all(&theme->styles.switch_handle_selected, SWITCH_HANDLE_SELECTED_PAD); +#endif + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_handle_pressed); + lv_style_set_pad_all(&theme->styles.switch_handle_pressed, SWITCH_HANDLE_PRESSED_PAD); +#endif + +#if LV_USE_CHECKBOX + style_init_reset(&theme->styles.checkbox); + lv_style_set_pad_gap(&theme->styles.checkbox, CHECKBOX_GAP); + lv_style_set_text_color(&theme->styles.checkbox, COLOR_ON_SURFACE); +#endif + +#if LV_USE_CHECKBOX + style_init_reset(&theme->styles.checkbox_box); + lv_style_set_bg_color(&theme->styles.checkbox_box, CHECKBOX_BOX_COLOR); + lv_style_set_bg_opa(&theme->styles.checkbox_box, CHECKBOX_BOX_OPACITY); + lv_style_set_border_color(&theme->styles.checkbox_box, CHECKBOX_BOX_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.checkbox_box, CHECKBOX_BOX_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.checkbox_box, CHECKBOX_BOX_SHAPE); + lv_style_set_pad_all(&theme->styles.checkbox_box, CHECKBOX_BOX_PAD); + lv_style_set_text_font(&theme->styles.checkbox_box, theme->config.font_small); + lv_style_set_text_color(&theme->styles.checkbox_box, CHECKBOX_ICON_COLOR); +#endif + +#if LV_USE_CHECKBOX + style_init_reset(&theme->styles.checkbox_box_selected); + lv_style_set_bg_color(&theme->styles.checkbox_box_selected, CHECKBOX_BOX_SELECTED_COLOR); + lv_style_set_bg_opa(&theme->styles.checkbox_box_selected, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.checkbox_box_selected, CHECKBOX_BOX_SELECTED_COLOR); + lv_style_set_bg_image_src(&theme->styles.checkbox_box_selected, LV_SYMBOL_OK); +#endif + + style_init_reset(&theme->styles.sliderbox); + lv_style_set_pad_column(&theme->styles.sliderbox, SLIDERBOX_GAP); + +#if LV_USE_SLIDER + style_init_reset(&theme->styles.slider_track_size); + lv_style_set_height(&theme->styles.slider_track_size, SLIDER_TRACK_HEIGHT); +#endif + +#if LV_USE_SLIDER + style_init_reset(&theme->styles.slider_knob_room); + lv_style_set_margin_ver(&theme->styles.slider_knob_room, SLIDER_HANDLE_PAD); +#endif + +#if LV_USE_SLIDER + style_init_reset(&theme->styles.slider_track); + lv_style_set_bg_color(&theme->styles.slider_track, SLIDER_TRACK_COLOR); + lv_style_set_bg_opa(&theme->styles.slider_track, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.slider_track, LV_RADIUS_CIRCLE); +#endif + +#if LV_USE_SLIDER + style_init_reset(&theme->styles.slider_active_track); + lv_style_set_bg_color(&theme->styles.slider_active_track, SLIDER_ACTIVE_TRACK_COLOR); + lv_style_set_bg_opa(&theme->styles.slider_active_track, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.slider_active_track, LV_RADIUS_CIRCLE); +#endif + +#if LV_USE_SLIDER + style_init_reset(&theme->styles.slider_handle); + lv_style_set_bg_color(&theme->styles.slider_handle, SLIDER_HANDLE_COLOR); + lv_style_set_bg_opa(&theme->styles.slider_handle, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.slider_handle, LV_RADIUS_CIRCLE); + lv_style_set_pad_all(&theme->styles.slider_handle, SLIDER_HANDLE_PAD); +#endif + +#if LV_USE_BAR + style_init_reset(&theme->styles.progress_track); + lv_style_set_bg_color(&theme->styles.progress_track, PROGRESS_TRACK_COLOR); + lv_style_set_bg_opa(&theme->styles.progress_track, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.progress_track, LV_RADIUS_CIRCLE); +#endif + +#if LV_USE_BAR + style_init_reset(&theme->styles.progress_indicator); + lv_style_set_bg_color(&theme->styles.progress_indicator, PROGRESS_INDICATOR_COLOR); + lv_style_set_bg_opa(&theme->styles.progress_indicator, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.progress_indicator, LV_RADIUS_CIRCLE); +#endif + +#if LV_USE_ARC || LV_USE_SPINNER + style_init_reset(&theme->styles.arc_track); + lv_style_set_arc_color(&theme->styles.arc_track, ARC_TRACK_COLOR); + lv_style_set_arc_width(&theme->styles.arc_track, ARC_WIDTH); + lv_style_set_arc_rounded(&theme->styles.arc_track, true); +#endif + +#if LV_USE_ARC || LV_USE_SPINNER + style_init_reset(&theme->styles.arc_indicator); + lv_style_set_arc_color(&theme->styles.arc_indicator, ARC_INDICATOR_COLOR); + lv_style_set_arc_width(&theme->styles.arc_indicator, ARC_WIDTH); + lv_style_set_arc_rounded(&theme->styles.arc_indicator, true); +#endif + +#if LV_USE_ARC + style_init_reset(&theme->styles.arc_handle); + lv_style_set_bg_color(&theme->styles.arc_handle, ARC_HANDLE_COLOR); + lv_style_set_bg_opa(&theme->styles.arc_handle, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.arc_handle, LV_RADIUS_CIRCLE); + lv_style_set_pad_all(&theme->styles.arc_handle, ARC_HANDLE_PAD); +#endif + +#if LV_USE_DROPDOWN || LV_USE_SPINBOX || LV_USE_TEXTAREA + style_init_reset(&theme->styles.field); + lv_style_set_bg_color(&theme->styles.field, FIELD_COLOR); + lv_style_set_bg_opa(&theme->styles.field, FIELD_OPACITY); + lv_style_set_text_color(&theme->styles.field, FIELD_TEXT_COLOR); + lv_style_set_border_color(&theme->styles.field, FIELD_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.field, FIELD_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.field, FIELD_SHAPE); + lv_style_set_pad_hor(&theme->styles.field, FIELD_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.field, FIELD_PAD_VER); + lv_style_set_pad_gap(&theme->styles.field, FIELD_PAD_VER); +#endif + +#if LV_USE_SPINBOX || LV_USE_TEXTAREA + style_init_reset(&theme->styles.field_focused); + lv_style_set_border_color(&theme->styles.field_focused, FIELD_FOCUSED_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.field_focused, FIELD_FOCUSED_BORDER_WIDTH); +#endif + +#if LV_USE_TEXTAREA + style_init_reset(&theme->styles.field_placeholder); + lv_style_set_text_color(&theme->styles.field_placeholder, FIELD_PLACEHOLDER_COLOR); +#endif + +#if LV_USE_TEXTAREA + style_init_reset(&theme->styles.field_cursor); + lv_style_set_border_color(&theme->styles.field_cursor, FIELD_CURSOR_COLOR); + lv_style_set_border_width(&theme->styles.field_cursor, FIELD_CURSOR_WIDTH); + lv_style_set_border_side(&theme->styles.field_cursor, LV_BORDER_SIDE_LEFT); + lv_style_set_pad_left(&theme->styles.field_cursor, -1); + lv_style_set_anim_duration(&theme->styles.field_cursor, MOTION_DURATION_LONG); +#endif + +#if LV_USE_LABEL && LV_USE_TEXTAREA + style_init_reset(&theme->styles.field_selection); + lv_style_set_bg_color(&theme->styles.field_selection, FIELD_SELECTION_COLOR); + lv_style_set_text_color(&theme->styles.field_selection, FIELD_SELECTION_TEXT_COLOR); +#endif + +#if LV_USE_SPINBOX + style_init_reset(&theme->styles.spinbox_cursor); + lv_style_set_bg_color(&theme->styles.spinbox_cursor, SPINBOX_CURSOR_COLOR); + lv_style_set_bg_opa(&theme->styles.spinbox_cursor, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.spinbox_cursor, SPINBOX_CURSOR_TEXT_COLOR); +#endif + +#if LV_USE_DROPDOWN + style_init_reset(&theme->styles.menu); + lv_style_set_bg_color(&theme->styles.menu, MENU_COLOR); + lv_style_set_bg_opa(&theme->styles.menu, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.menu, MENU_TEXT_COLOR); + lv_style_set_radius(&theme->styles.menu, MENU_SHAPE); + lv_style_set_pad_all(&theme->styles.menu, MENU_PAD); + lv_style_set_text_line_space(&theme->styles.menu, MENU_ITEM_LINE_SPACE); + lv_style_set_max_height(&theme->styles.menu, MENU_MAX_HEIGHT); + lv_style_set_clip_corner(&theme->styles.menu, true); +#endif + +#if LV_USE_DROPDOWN + style_init_reset(&theme->styles.menu_item); + lv_style_set_bg_opa(&theme->styles.menu_item, LV_OPA_TRANSP); +#endif + +#if LV_USE_DROPDOWN + style_init_reset(&theme->styles.menu_item_selected); + lv_style_set_bg_color(&theme->styles.menu_item_selected, MENU_ITEM_SELECTED_COLOR); + lv_style_set_bg_opa(&theme->styles.menu_item_selected, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.menu_item_selected, MENU_ITEM_SELECTED_TEXT_COLOR); +#endif + +#if LV_USE_ROLLER + style_init_reset(&theme->styles.roller); + lv_style_set_bg_color(&theme->styles.roller, ROLLER_COLOR); + lv_style_set_bg_opa(&theme->styles.roller, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.roller, ROLLER_TEXT_COLOR); + lv_style_set_radius(&theme->styles.roller, SHAPE_CONTAINER); + lv_style_set_text_line_space(&theme->styles.roller, ROLLER_LINE_SPACE); + lv_style_set_text_align(&theme->styles.roller, LV_TEXT_ALIGN_CENTER); + lv_style_set_anim_duration(&theme->styles.roller, MOTION_DURATION_MEDIUM); +#endif + +#if LV_USE_ROLLER + style_init_reset(&theme->styles.roller_selected); + lv_style_set_bg_color(&theme->styles.roller_selected, ROLLER_SELECTED_COLOR); + lv_style_set_bg_opa(&theme->styles.roller_selected, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.roller_selected, ROLLER_SELECTED_TEXT_COLOR); +#endif + +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + style_init_reset(&theme->styles.keys); + lv_style_set_bg_color(&theme->styles.keys, KEYS_COLOR); + lv_style_set_bg_opa(&theme->styles.keys, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.keys, SHAPE_CONTAINER); + lv_style_set_pad_all(&theme->styles.keys, KEYS_PAD); + lv_style_set_pad_gap(&theme->styles.keys, KEYS_GAP); +#endif + +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + style_init_reset(&theme->styles.key); + lv_style_set_bg_color(&theme->styles.key, KEY_COLOR); + lv_style_set_bg_opa(&theme->styles.key, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.key, KEY_TEXT_COLOR); + lv_style_set_radius(&theme->styles.key, KEY_SHAPE); +#endif + +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + style_init_reset(&theme->styles.key_pressed); + lv_style_set_bg_color(&theme->styles.key_pressed, lv_color_mix(STATE_LAYER_COLOR, KEY_COLOR, OPACITY_STATE_PRESSED)); +#endif + +#if LV_USE_KEYBOARD + style_init_reset(&theme->styles.key_checked); + lv_style_set_bg_color(&theme->styles.key_checked, KEY_CHECKED_COLOR); + lv_style_set_text_color(&theme->styles.key_checked, KEY_CHECKED_TEXT_COLOR); +#endif + +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + style_init_reset(&theme->styles.key_selected); + lv_style_set_bg_color(&theme->styles.key_selected, KEY_SELECTED_COLOR); + lv_style_set_text_color(&theme->styles.key_selected, KEY_SELECTED_TEXT_COLOR); +#endif + +#if LV_USE_LIST + style_init_reset(&theme->styles.list); + lv_style_set_bg_color(&theme->styles.list, LIST_COLOR); + lv_style_set_bg_opa(&theme->styles.list, LV_OPA_COVER); + lv_style_set_pad_hor(&theme->styles.list, 0); + lv_style_set_pad_ver(&theme->styles.list, LIST_PAD); + lv_style_set_pad_gap(&theme->styles.list, 0); + lv_style_set_clip_corner(&theme->styles.list, true); +#endif + +#if LV_USE_LIST + style_init_reset(&theme->styles.list_item); + lv_style_set_bg_color(&theme->styles.list_item, LIST_ITEM_COLOR); + lv_style_set_bg_opa(&theme->styles.list_item, LIST_ITEM_OPACITY); + lv_style_set_text_color(&theme->styles.list_item, LIST_ITEM_TEXT_COLOR); + lv_style_set_pad_hor(&theme->styles.list_item, LIST_ITEM_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.list_item, LIST_ITEM_PAD_VER); + lv_style_set_pad_column(&theme->styles.list_item, LIST_ITEM_GAP); + lv_style_set_border_color(&theme->styles.list_item, LIST_DIVIDER_COLOR); + lv_style_set_border_width(&theme->styles.list_item, LIST_DIVIDER_WIDTH); + lv_style_set_border_side(&theme->styles.list_item, LV_BORDER_SIDE_BOTTOM); +#endif + +#if LV_USE_LIST || LV_USE_TABLE + style_init_reset(&theme->styles.list_item_focused); + lv_style_set_bg_color(&theme->styles.list_item_focused, LIST_ITEM_FOCUSED_COLOR); + lv_style_set_bg_opa(&theme->styles.list_item_focused, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.list_item_focused, LIST_ITEM_FOCUSED_TEXT_COLOR); +#endif + +#if LV_USE_LIST + style_init_reset(&theme->styles.list_subheader); + lv_style_set_text_color(&theme->styles.list_subheader, LIST_SUBHEADER_TEXT_COLOR); + lv_style_set_text_font(&theme->styles.list_subheader, theme->config.font_small); + lv_style_set_pad_hor(&theme->styles.list_subheader, LIST_ITEM_PAD_HOR); + lv_style_set_pad_top(&theme->styles.list_subheader, LIST_ITEM_PAD_VER); + lv_style_set_pad_bottom(&theme->styles.list_subheader, LIST_PAD); +#endif + +#if LV_USE_TABVIEW + style_init_reset(&theme->styles.tabs); + lv_style_set_bg_opa(&theme->styles.tabs, LV_OPA_TRANSP); + lv_style_set_border_color(&theme->styles.tabs, TABS_DIVIDER_COLOR); + lv_style_set_border_width(&theme->styles.tabs, DENSITY_BORDER); + lv_style_set_border_side(&theme->styles.tabs, LV_BORDER_SIDE_BOTTOM); +#endif + +#if LV_USE_BUTTON && LV_USE_TABVIEW + style_init_reset(&theme->styles.tab); + lv_style_set_bg_opa(&theme->styles.tab, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.tab, TAB_LABEL_COLOR); + lv_style_set_radius(&theme->styles.tab, 0); +#endif + +#if LV_USE_BUTTON && LV_USE_TABVIEW + style_init_reset(&theme->styles.tab_selected); + lv_style_set_text_color(&theme->styles.tab_selected, TAB_SELECTED_LABEL_COLOR); + lv_style_set_border_color(&theme->styles.tab_selected, TAB_INDICATOR_COLOR); + lv_style_set_border_width(&theme->styles.tab_selected, TAB_INDICATOR_WIDTH); + lv_style_set_border_side(&theme->styles.tab_selected, LV_BORDER_SIDE_BOTTOM); +#endif + +#if LV_USE_TABVIEW + style_init_reset(&theme->styles.tab_page); + lv_style_set_pad_all(&theme->styles.tab_page, TAB_PAGE_PAD); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog); + lv_style_set_bg_color(&theme->styles.dialog, DIALOG_COLOR); + lv_style_set_bg_opa(&theme->styles.dialog, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.dialog, DIALOG_TEXT_COLOR); + lv_style_set_radius(&theme->styles.dialog, DIALOG_SHAPE); + lv_style_set_pad_all(&theme->styles.dialog, 0); + lv_style_set_pad_gap(&theme->styles.dialog, 0); + lv_style_set_clip_corner(&theme->styles.dialog, true); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog_header); + lv_style_set_pad_hor(&theme->styles.dialog_header, DIALOG_PAD); + lv_style_set_pad_top(&theme->styles.dialog_header, DIALOG_PAD); + lv_style_set_pad_bottom(&theme->styles.dialog_header, DIALOG_GAP); + lv_style_set_pad_gap(&theme->styles.dialog_header, DIALOG_GAP); +#endif + +#if LV_USE_LABEL && LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog_title); + lv_style_set_text_color(&theme->styles.dialog_title, DIALOG_TITLE_COLOR); + lv_style_set_text_font(&theme->styles.dialog_title, theme->config.font_large); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog_content); + lv_style_set_pad_hor(&theme->styles.dialog_content, DIALOG_PAD); + lv_style_set_pad_ver(&theme->styles.dialog_content, DIALOG_GAP); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog_actions); + lv_style_set_pad_all(&theme->styles.dialog_actions, DIALOG_PAD); + lv_style_set_pad_top(&theme->styles.dialog_actions, DIALOG_GAP); + lv_style_set_pad_gap(&theme->styles.dialog_actions, DIALOG_GAP); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog_scrim); + lv_style_set_bg_color(&theme->styles.dialog_scrim, DIALOG_SCRIM_COLOR); + lv_style_set_bg_opa(&theme->styles.dialog_scrim, DIALOG_SCRIM_OPACITY); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings); + lv_style_set_bg_color(&theme->styles.settings, SETTINGS_COLOR); + lv_style_set_bg_opa(&theme->styles.settings, LV_OPA_COVER); + lv_style_set_pad_all(&theme->styles.settings, 0); + lv_style_set_pad_gap(&theme->styles.settings, 0); + lv_style_set_radius(&theme->styles.settings, 0); + lv_style_set_clip_corner(&theme->styles.settings, true); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_sidebar); + lv_style_set_pad_all(&theme->styles.settings_sidebar, 0); + lv_style_set_pad_gap(&theme->styles.settings_sidebar, 0); + lv_style_set_border_color(&theme->styles.settings_sidebar, SETTINGS_DIVIDER_COLOR); + lv_style_set_border_width(&theme->styles.settings_sidebar, DENSITY_BORDER); + lv_style_set_border_side(&theme->styles.settings_sidebar, LV_BORDER_SIDE_RIGHT); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_content); + lv_style_set_pad_all(&theme->styles.settings_content, 0); + lv_style_set_pad_gap(&theme->styles.settings_content, 0); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_page); + lv_style_set_pad_hor(&theme->styles.settings_page, 0); + lv_style_set_pad_gap(&theme->styles.settings_page, 0); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_header); + lv_style_set_pad_hor(&theme->styles.settings_header, SETTINGS_ITEM_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.settings_header, DENSITY_LIST_PAD); + lv_style_set_pad_gap(&theme->styles.settings_header, SETTINGS_ITEM_GAP); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_section); + lv_style_set_bg_color(&theme->styles.settings_section, SETTINGS_SECTION_COLOR); + lv_style_set_bg_opa(&theme->styles.settings_section, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.settings_section, SETTINGS_SECTION_SHAPE); + lv_style_set_clip_corner(&theme->styles.settings_section, true); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_item); + lv_style_set_pad_hor(&theme->styles.settings_item, SETTINGS_ITEM_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.settings_item, SETTINGS_ITEM_PAD_VER); + lv_style_set_pad_gap(&theme->styles.settings_item, SETTINGS_ITEM_GAP); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_item_selected); + lv_style_set_bg_color(&theme->styles.settings_item_selected, SETTINGS_ITEM_SELECTED_COLOR); + lv_style_set_bg_opa(&theme->styles.settings_item_selected, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.settings_item_selected, SETTINGS_ITEM_SELECTED_TEXT_COLOR); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_separator); + lv_style_set_bg_opa(&theme->styles.settings_separator, LV_OPA_TRANSP); + lv_style_set_pad_ver(&theme->styles.settings_separator, DENSITY_LIST_PAD); +#endif + +#if LV_USE_WIN + style_init_reset(&theme->styles.window_header); + lv_style_set_bg_color(&theme->styles.window_header, WINDOW_HEADER_COLOR); + lv_style_set_bg_opa(&theme->styles.window_header, LV_OPA_COVER); + lv_style_set_pad_all(&theme->styles.window_header, WINDOW_HEADER_PAD); + lv_style_set_pad_gap(&theme->styles.window_header, WINDOW_HEADER_PAD); +#endif + +#if LV_USE_LABEL && LV_USE_WIN + style_init_reset(&theme->styles.window_title); + lv_style_set_text_font(&theme->styles.window_title, theme->config.font_large); +#endif + +#if LV_USE_WIN + style_init_reset(&theme->styles.window_content); + lv_style_set_bg_opa(&theme->styles.window_content, LV_OPA_TRANSP); + lv_style_set_pad_all(&theme->styles.window_content, WINDOW_CONTENT_PAD); + lv_style_set_pad_gap(&theme->styles.window_content, DENSITY_CONTAINER_GAP); +#endif + +#if LV_USE_TABLE + style_init_reset(&theme->styles.table); + lv_style_set_bg_color(&theme->styles.table, TABLE_COLOR); + lv_style_set_bg_opa(&theme->styles.table, LV_OPA_COVER); + lv_style_set_pad_all(&theme->styles.table, 0); + lv_style_set_radius(&theme->styles.table, 0); + lv_style_set_border_color(&theme->styles.table, TABLE_DIVIDER_COLOR); + lv_style_set_border_width(&theme->styles.table, DENSITY_BORDER); +#endif + +#if LV_USE_TABLE + style_init_reset(&theme->styles.table_cell); + lv_style_set_bg_opa(&theme->styles.table_cell, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.table_cell, TABLE_CELL_TEXT_COLOR); + lv_style_set_pad_all(&theme->styles.table_cell, TABLE_CELL_PAD); + lv_style_set_border_color(&theme->styles.table_cell, TABLE_DIVIDER_COLOR); + lv_style_set_border_width(&theme->styles.table_cell, DENSITY_BORDER); + lv_style_set_border_side(&theme->styles.table_cell, LV_BORDER_SIDE_BOTTOM); +#endif + +#if LV_USE_CHART + style_init_reset(&theme->styles.chart); + lv_style_set_bg_color(&theme->styles.chart, CHART_COLOR); + lv_style_set_bg_opa(&theme->styles.chart, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.chart, SHAPE_CONTAINER); + lv_style_set_pad_all(&theme->styles.chart, DENSITY_CONTAINER_PAD); + lv_style_set_line_color(&theme->styles.chart, CHART_GRID_COLOR); + lv_style_set_border_post(&theme->styles.chart, false); +#endif + +#if LV_USE_CHART + style_init_reset(&theme->styles.chart_series); + lv_style_set_line_width(&theme->styles.chart_series, CHART_SERIES_WIDTH); + lv_style_set_size(&theme->styles.chart_series, CHART_POINT_SIZE, CHART_POINT_SIZE); + lv_style_set_radius(&theme->styles.chart_series, CHART_POINT_SIZE); +#endif + +#if LV_USE_CHART + style_init_reset(&theme->styles.chart_indicator); + lv_style_set_bg_color(&theme->styles.chart_indicator, CHART_INDICATOR_COLOR); + lv_style_set_bg_opa(&theme->styles.chart_indicator, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.chart_indicator, LV_RADIUS_CIRCLE); + lv_style_set_size(&theme->styles.chart_indicator, CHART_POINT_SIZE, CHART_POINT_SIZE); +#endif + +#if LV_USE_CALENDAR + style_init_reset(&theme->styles.calendar); + lv_style_set_bg_color(&theme->styles.calendar, CALENDAR_COLOR); + lv_style_set_bg_opa(&theme->styles.calendar, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.calendar, CALENDAR_SHAPE); + lv_style_set_pad_all(&theme->styles.calendar, CALENDAR_PAD); + lv_style_set_pad_gap(&theme->styles.calendar, CALENDAR_PAD); +#endif + +#if LV_USE_BUTTONMATRIX && LV_USE_CALENDAR + style_init_reset(&theme->styles.calendar_days); + lv_style_set_bg_opa(&theme->styles.calendar_days, LV_OPA_TRANSP); + lv_style_set_pad_all(&theme->styles.calendar_days, 0); + lv_style_set_pad_gap(&theme->styles.calendar_days, DENSITY_KEY_GAP); +#endif + +#if LV_USE_BUTTONMATRIX && LV_USE_CALENDAR + style_init_reset(&theme->styles.calendar_day); + lv_style_set_bg_opa(&theme->styles.calendar_day, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.calendar_day, CALENDAR_DAY_TEXT_COLOR); + lv_style_set_radius(&theme->styles.calendar_day, LV_RADIUS_CIRCLE); +#endif + +#if (LV_USE_CALENDAR && LV_USE_CALENDAR_HEADER_ARROW) || (LV_USE_CALENDAR && LV_USE_CALENDAR_HEADER_DROPDOWN) + style_init_reset(&theme->styles.calendar_header); + lv_style_set_pad_all(&theme->styles.calendar_header, CALENDAR_HEADER_PAD); + lv_style_set_pad_gap(&theme->styles.calendar_header, CALENDAR_HEADER_PAD); +#endif + +#if LV_USE_SCALE + style_init_reset(&theme->styles.scale); + lv_style_set_line_color(&theme->styles.scale, SCALE_COLOR); + lv_style_set_line_width(&theme->styles.scale, SCALE_WIDTH); + lv_style_set_arc_color(&theme->styles.scale, SCALE_COLOR); + lv_style_set_arc_width(&theme->styles.scale, SCALE_WIDTH); + lv_style_set_text_color(&theme->styles.scale, SCALE_COLOR); + lv_style_set_length(&theme->styles.scale, SCALE_TICK_LENGTH); +#endif + +#if LV_USE_LINE + style_init_reset(&theme->styles.divider); + lv_style_set_line_color(&theme->styles.divider, DIVIDER_COLOR); + lv_style_set_line_width(&theme->styles.divider, DIVIDER_WIDTH); +#endif + +#if LV_USE_LED + style_init_reset(&theme->styles.led); + lv_style_set_bg_color(&theme->styles.led, lv_color_white()); + lv_style_set_bg_opa(&theme->styles.led, LV_OPA_COVER); + lv_style_set_bg_grad_color(&theme->styles.led, lv_palette_main(LV_PALETTE_GREY)); + lv_style_set_radius(&theme->styles.led, LV_RADIUS_CIRCLE); + lv_style_set_shadow_width(&theme->styles.led, LED_GLOW_WIDTH); + lv_style_set_shadow_color(&theme->styles.led, lv_color_white()); + lv_style_set_shadow_spread(&theme->styles.led, LED_GLOW_SPREAD); +#endif +} + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_theme_material_config_init(lv_theme_material_config_t * config) +{ + lv_memzero(config, sizeof(*config)); + config->is_dark = false; + config->is_compact = false; + config->animations_enabled = true; + config->color_primary = lv_color_hex(0x00BBFF); + config->color_secondary = lv_color_hex(0xEE81C3); + config->color_error = lv_color_hex(0xB3261E); + config->font_small = LV_FONT_DEFAULT; + config->font_normal = LV_FONT_DEFAULT; + config->font_large = LV_FONT_DEFAULT; +} + +lv_theme_t * lv_theme_material_init(lv_display_t * disp, const lv_theme_material_config_t * config) +{ + if(!lv_theme_material_is_inited()) { + theme_def = lv_malloc_zeroed(sizeof(my_theme_t)); + LV_ASSERT_MALLOC(theme_def); + } + + my_theme_t * theme = theme_def; + + lv_display_t * new_disp = disp == NULL ? lv_display_get_default() : disp; + int32_t new_dpi = lv_display_get_dpi(new_disp); + int32_t hor_res = lv_display_get_horizontal_resolution(new_disp); + int32_t ver_res = lv_display_get_vertical_resolution(new_disp); + int32_t greater_res = LV_MAX(hor_res, ver_res); + disp_size_t new_size; + + if(greater_res <= 320) new_size = DISP_SMALL; + else if(greater_res < 720) new_size = DISP_MEDIUM; + else new_size = DISP_LARGE; + + if(theme->inited && theme->disp_dpi == new_dpi && theme->disp_size == new_size && + config_equals(&theme->config, config)) { + return (lv_theme_t *) theme; + } + + theme->disp_size = new_size; + theme->disp_dpi = new_dpi; + theme->config = *config; + theme->base.disp = new_disp; + theme->base.apply_cb = theme_apply; + theme->base.color_primary = config->color_primary; + theme->base.color_secondary = config->color_secondary; + theme->base.font_small = config->font_small; + theme->base.font_normal = config->font_normal; + theme->base.font_large = config->font_large; + + style_init(theme); + + if(disp == NULL || lv_display_get_theme(disp) == (lv_theme_t *)theme) { + lv_obj_report_style_change(NULL); + } + + theme->inited = true; + + /* Re-initialize the styles when the resolution changes, as the display size affects the styles */ + lv_display_remove_event_cb_with_user_data(new_disp, resolution_change_event_cb, theme); + lv_display_add_event_cb(new_disp, resolution_change_event_cb, LV_EVENT_RESOLUTION_CHANGED, theme); + + return (lv_theme_t *) theme; +} + +bool lv_theme_material_is_inited(void) +{ + return theme_def != NULL && theme_def->inited; +} + +lv_theme_t * lv_theme_material_get(void) +{ + if(!lv_theme_material_is_inited()) { + return NULL; + } + return (lv_theme_t *) theme_def; +} + +void lv_theme_material_deinit(void) +{ + my_theme_t * theme = theme_def; + if(theme) { + if(theme->inited) { + lv_style_t * theme_styles = (lv_style_t *)(&(theme->styles)); + uint32_t i; + for(i = 0; i < sizeof(my_theme_styles_t) / sizeof(lv_style_t); i++) { + lv_style_reset(theme_styles + i); + } + } + lv_free(theme_def); + theme_def = NULL; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void theme_apply(lv_theme_t * th, lv_obj_t * obj) +{ + LV_UNUSED(th); + + my_theme_t * theme = theme_def; + lv_obj_t * parent = lv_obj_get_parent(obj); + LV_UNUSED(theme); + LV_UNUSED(parent); + + /* null > * */ + if(parent == NULL) { + lv_obj_add_style(obj, &theme->styles.screen, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } + if(lv_obj_check_type(obj, &lv_obj_class)) { +#if LV_USE_TABVIEW + /* tabview > obj[1] */ + if(lv_obj_check_type(parent, &lv_tabview_class) && lss_is_child(parent, 1, obj)) { + return; + } +#endif +#if LV_USE_TABVIEW + /* tabview > obj[0] */ + if(lv_obj_check_type(parent, &lv_tabview_class) && lss_is_child(parent, 0, obj)) { + lv_obj_add_style(obj, &theme->styles.tabs, 0); + return; + } +#endif +#if LV_USE_TABVIEW + /* tabview > * > obj */ + if(lv_obj_check_type(lss_ancestor(obj, 2), &lv_tabview_class)) { + lv_obj_add_style(obj, &theme->styles.tab_page, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#endif +#if LV_USE_WIN + /* win > obj[0] */ + if(lv_obj_check_type(parent, &lv_win_class) && lss_is_child(parent, 0, obj)) { + lv_obj_add_style(obj, &theme->styles.window_header, 0); + return; + } +#endif +#if LV_USE_WIN + /* win > obj[1] */ + if(lv_obj_check_type(parent, &lv_win_class) && lss_is_child(parent, 1, obj)) { + lv_obj_add_style(obj, &theme->styles.window_content, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#endif +#if LV_USE_CALENDAR + /* calendar > obj */ + if(lv_obj_check_type(parent, &lv_calendar_class)) { + return; + } +#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 */ + 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_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#if LV_USE_BUTTON + if(lv_obj_check_type(obj, &lv_button_class)) { +#if LV_USE_TABVIEW + /* tabview > *[0] > button */ + if(lv_obj_check_type(lss_ancestor(obj, 2), &lv_tabview_class) && lss_is_child(lss_ancestor(obj, 2), 0, parent)) { + lv_obj_add_style(obj, &theme->styles.tab, 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.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.tab_selected, LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + return; + } +#endif +#if LV_USE_MENU + /* menu_sidebar_header_cont > button */ + if(lv_obj_check_type(parent, &lv_menu_sidebar_header_cont_class)) { + lv_obj_add_style(obj, &theme->styles.icon_button, 0); + lv_obj_add_style(obj, &theme->styles.transition_exit, 0); + lv_obj_add_style(obj, &theme->styles.state_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_MENU + /* menu_main_header_cont > button */ + if(lv_obj_check_type(parent, &lv_menu_main_header_cont_class)) { + lv_obj_add_style(obj, &theme->styles.icon_button, 0); + lv_obj_add_style(obj, &theme->styles.transition_exit, 0); + lv_obj_add_style(obj, &theme->styles.state_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_WIN + /* win > obj[0] > button */ + if(lv_obj_check_type(lss_ancestor(obj, 2), &lv_win_class) && lv_obj_check_type(parent, &lv_obj_class) && lss_is_child(lss_ancestor(obj, 2), 0, parent)) { + lv_obj_add_style(obj, &theme->styles.icon_button, 0); + lv_obj_add_style(obj, &theme->styles.transition_exit, 0); + lv_obj_add_style(obj, &theme->styles.state_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif + /* button */ + lv_obj_add_style(obj, &theme->styles.button, 0); + lv_obj_add_style(obj, &theme->styles.transition_exit, 0); + lv_obj_add_style(obj, &theme->styles.button_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.button_checked, LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.button_checked_pressed, LV_STATE_CHECKED | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.button_disabled, LV_STATE_DISABLED); + return; + } +#endif + if(lv_obj_check_type(obj, &lvgl_icon_button_class)) { + /* toolbar > icon_button */ + if(lv_obj_check_type(parent, &lvgl_toolbar_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; + } + /* 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 */ + 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.state_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + 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.disabled, LV_STATE_DISABLED); + return; + } +#if LV_USE_DROPDOWN + if(lv_obj_check_type(obj, &lv_dropdown_class)) { + /* toolbar > obj > dropdown */ + 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.field, 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.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + lv_obj_add_style(obj, &theme->styles.app_bar_field, 0); + lv_obj_add_style(obj, &theme->styles.app_bar_focus_ring, LV_STATE_FOCUS_KEY); + return; + } + /* dropdown */ + 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.state_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif + if(lv_obj_check_type(obj, &lvgl_toolbar_class)) { + /* toolbar */ + lv_obj_add_style(obj, &theme->styles.app_bar, 0); + return; + } + if(lv_obj_check_type(obj, &lvgl_icon_button_filled_class)) { + /* icon_button_filled */ + lv_obj_add_style(obj, &theme->styles.icon_button_filled, 0); + lv_obj_add_style(obj, &theme->styles.transition_exit, 0); + lv_obj_add_style(obj, &theme->styles.icon_button_filled_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.button_disabled, LV_STATE_DISABLED); + return; + } + if(lv_obj_check_type(obj, &lvgl_icon_button_tonal_class)) { + /* icon_button_tonal */ + lv_obj_add_style(obj, &theme->styles.icon_button_tonal, 0); + lv_obj_add_style(obj, &theme->styles.transition_exit, 0); + lv_obj_add_style(obj, &theme->styles.icon_button_tonal_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.button_disabled, LV_STATE_DISABLED); + return; + } +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_header_button_class)) { + /* msgbox_header_button */ + lv_obj_add_style(obj, &theme->styles.icon_button, 0); + lv_obj_add_style(obj, &theme->styles.transition_exit, 0); + lv_obj_add_style(obj, &theme->styles.state_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_footer_button_class)) { + /* msgbox_footer_button */ + lv_obj_add_style(obj, &theme->styles.text_button, 0); + lv_obj_add_style(obj, &theme->styles.transition_exit, 0); + lv_obj_add_style(obj, &theme->styles.text_button_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_LABEL + if(lv_obj_check_type(obj, &lv_label_class)) { +#if LV_USE_MSGBOX + /* msgbox_header > label */ + if(lv_obj_check_type(parent, &lv_msgbox_header_class)) { + lv_obj_add_style(obj, &theme->styles.dialog_title, 0); + return; + } +#endif +#if LV_USE_WIN + /* win > obj[0] > label */ + if(lv_obj_check_type(lss_ancestor(obj, 2), &lv_win_class) && lv_obj_check_type(parent, &lv_obj_class) && lss_is_child(lss_ancestor(obj, 2), 0, parent)) { + lv_obj_add_style(obj, &theme->styles.window_title, 0); + return; + } +#endif +#if LV_USE_TEXTAREA + /* textarea > label */ + if(lv_obj_check_type(parent, &lv_textarea_class)) { + lv_obj_add_style(obj, &theme->styles.field_selection, LV_PART_SELECTED); + return; + } +#endif + return; + } +#endif +#if LV_USE_SWITCH + if(lv_obj_check_type(obj, &lv_switch_class)) { + /* switch */ + lv_obj_add_style(obj, &theme->styles.switch_track, 0); + lv_obj_add_style(obj, &theme->styles.switch_track_selected, LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.switch_indicator_selected, LV_PART_INDICATOR | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.switch_handle, LV_PART_KNOB); + lv_obj_add_style(obj, &theme->styles.switch_handle_selected, LV_PART_KNOB | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.switch_handle_pressed, LV_PART_KNOB | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_CHECKBOX + if(lv_obj_check_type(obj, &lv_checkbox_class)) { + /* checkbox */ + lv_obj_add_style(obj, &theme->styles.checkbox, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.checkbox_box, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.checkbox_box_selected, LV_PART_INDICATOR | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.transition_exit, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.transition_enter, LV_PART_INDICATOR | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif + if(lv_obj_check_type(obj, &lvgl_card_class)) { + /* card */ + lv_obj_add_style(obj, &theme->styles.card, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } + if(lv_obj_check_type(obj, &lvgl_sliderbox_class)) { + /* sliderbox */ + lv_obj_add_style(obj, &theme->styles.sliderbox, 0); + return; + } +#if LV_USE_SLIDER + if(lv_obj_check_type(obj, &lv_slider_class)) { + /* slider */ + lv_obj_add_style(obj, &theme->styles.slider_track, 0); + lv_obj_add_style(obj, &theme->styles.slider_track_size, 0); + lv_obj_add_style(obj, &theme->styles.slider_knob_room, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.slider_active_track, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.slider_handle, LV_PART_KNOB); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_BAR + if(lv_obj_check_type(obj, &lv_bar_class)) { + /* bar */ + lv_obj_add_style(obj, &theme->styles.progress_track, 0); + lv_obj_add_style(obj, &theme->styles.progress_indicator, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + return; + } +#endif +#if LV_USE_ARC + if(lv_obj_check_type(obj, &lv_arc_class)) { + /* arc */ + lv_obj_add_style(obj, &theme->styles.arc_track, 0); + lv_obj_add_style(obj, &theme->styles.arc_indicator, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.arc_handle, LV_PART_KNOB); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + return; + } +#endif +#if LV_USE_SPINNER + if(lv_obj_check_type(obj, &lv_spinner_class)) { + /* spinner */ + lv_obj_add_style(obj, &theme->styles.arc_track, 0); + lv_obj_add_style(obj, &theme->styles.arc_indicator, LV_PART_INDICATOR); + return; + } +#endif +#if LV_USE_TEXTAREA + if(lv_obj_check_type(obj, &lv_textarea_class)) { + /* textarea */ + lv_obj_add_style(obj, &theme->styles.field, 0); + lv_obj_add_style(obj, &theme->styles.field_focused, LV_STATE_FOCUSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + lv_obj_add_style(obj, &theme->styles.field_cursor, LV_PART_CURSOR | LV_STATE_FOCUSED); + lv_obj_add_style(obj, &theme->styles.field_placeholder, LV_PART_TEXTAREA_PLACEHOLDER); + return; + } +#endif +#if LV_USE_SPINBOX + if(lv_obj_check_type(obj, &lv_spinbox_class)) { + /* spinbox */ + lv_obj_add_style(obj, &theme->styles.field, 0); + lv_obj_add_style(obj, &theme->styles.field_focused, LV_STATE_FOCUSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.spinbox_cursor, LV_PART_CURSOR); + return; + } +#endif +#if LV_USE_DROPDOWN + if(lv_obj_check_type(obj, &lv_dropdownlist_class)) { + /* dropdownlist */ + lv_obj_add_style(obj, &theme->styles.menu, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + lv_obj_add_style(obj, &theme->styles.menu_item, LV_PART_SELECTED); + lv_obj_add_style(obj, &theme->styles.menu_item_selected, LV_PART_SELECTED | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.state_pressed, LV_PART_SELECTED | LV_STATE_PRESSED); + return; + } +#endif +#if LV_USE_ROLLER + if(lv_obj_check_type(obj, &lv_roller_class)) { + /* roller */ + lv_obj_add_style(obj, &theme->styles.roller, 0); + lv_obj_add_style(obj, &theme->styles.roller_selected, LV_PART_SELECTED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + return; + } +#endif +#if LV_USE_BUTTONMATRIX + if(lv_obj_check_type(obj, &lv_buttonmatrix_class)) { +#if LV_USE_CALENDAR + /* calendar > buttonmatrix */ + if(lv_obj_check_type(parent, &lv_calendar_class)) { + lv_obj_add_style(obj, &theme->styles.calendar_days, 0); + lv_obj_add_style(obj, &theme->styles.calendar_day, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.state_pressed, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.key_selected, LV_PART_ITEMS | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_PART_ITEMS | LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_PART_ITEMS | LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_PART_ITEMS | LV_STATE_DISABLED); + return; + } +#endif + /* buttonmatrix */ + lv_obj_add_style(obj, &theme->styles.keys, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.key, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.key_pressed, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.key_selected, LV_PART_ITEMS | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_PART_ITEMS | LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_PART_ITEMS | LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_KEYBOARD + if(lv_obj_check_type(obj, &lv_keyboard_class)) { + /* keyboard */ + lv_obj_add_style(obj, &theme->styles.keys, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.key, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.key_pressed, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.key_checked, LV_PART_ITEMS | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.key_selected, LV_PART_ITEMS | LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_PART_ITEMS | LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_LIST + if(lv_obj_check_type(obj, &lv_list_class)) { + /* list */ + lv_obj_add_style(obj, &theme->styles.list, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#endif +#if LV_USE_LIST + if(lv_obj_check_type(obj, &lv_list_text_class)) { + /* list_text */ + lv_obj_add_style(obj, &theme->styles.list_subheader, 0); + return; + } +#endif +#if LV_USE_LIST + if(lv_obj_check_type(obj, &lv_list_button_class)) { + /* list_button */ + lv_obj_add_style(obj, &theme->styles.list_item, 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.transition_enter, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.list_item_focused, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_TABLE + if(lv_obj_check_type(obj, &lv_table_class)) { + /* table */ + lv_obj_add_style(obj, &theme->styles.table, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + lv_obj_add_style(obj, &theme->styles.table_cell, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.state_pressed, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.list_item_focused, LV_PART_ITEMS | LV_STATE_FOCUS_KEY); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_class)) { + /* menu */ + lv_obj_add_style(obj, &theme->styles.settings, 0); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_sidebar_cont_class)) { + /* menu_sidebar_cont */ + lv_obj_add_style(obj, &theme->styles.settings_sidebar, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_main_cont_class)) { + /* menu_main_cont */ + lv_obj_add_style(obj, &theme->styles.settings_content, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_page_class)) { + /* menu_page */ + lv_obj_add_style(obj, &theme->styles.settings_page, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_sidebar_header_cont_class)) { + /* menu_sidebar_header_cont */ + lv_obj_add_style(obj, &theme->styles.settings_header, 0); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_main_header_cont_class)) { + /* menu_main_header_cont */ + lv_obj_add_style(obj, &theme->styles.settings_header, 0); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_section_class)) { + /* menu_section */ + lv_obj_add_style(obj, &theme->styles.settings_section, 0); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_cont_class)) { + /* menu_cont */ + lv_obj_add_style(obj, &theme->styles.settings_item, 0); + lv_obj_add_style(obj, &theme->styles.state_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.settings_item_selected, LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.settings_item_selected, LV_STATE_FOCUS_KEY); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_separator_class)) { + /* menu_separator */ + lv_obj_add_style(obj, &theme->styles.settings_separator, 0); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_class)) { + /* msgbox */ + lv_obj_add_style(obj, &theme->styles.dialog, 0); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_backdrop_class)) { + /* msgbox_backdrop */ + lv_obj_add_style(obj, &theme->styles.dialog_scrim, 0); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_header_class)) { + /* msgbox_header */ + lv_obj_add_style(obj, &theme->styles.dialog_header, 0); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_content_class)) { + /* msgbox_content */ + lv_obj_add_style(obj, &theme->styles.dialog_content, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_footer_class)) { + /* msgbox_footer */ + lv_obj_add_style(obj, &theme->styles.dialog_actions, 0); + return; + } +#endif +#if LV_USE_TABVIEW + if(lv_obj_check_type(obj, &lv_tabview_class)) { + /* tabview */ + lv_obj_add_style(obj, &theme->styles.page, 0); + return; + } +#endif +#if LV_USE_WIN + if(lv_obj_check_type(obj, &lv_win_class)) { + /* win */ + lv_obj_add_style(obj, &theme->styles.page, 0); + return; + } +#endif +#if LV_USE_TILEVIEW + if(lv_obj_check_type(obj, &lv_tileview_class)) { + /* tileview */ + lv_obj_add_style(obj, &theme->styles.page, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#endif +#if LV_USE_TILEVIEW + if(lv_obj_check_type(obj, &lv_tileview_tile_class)) { + /* tileview_tile */ + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + return; + } +#endif +#if LV_USE_CHART + if(lv_obj_check_type(obj, &lv_chart_class)) { + /* chart */ + lv_obj_add_style(obj, &theme->styles.chart, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + lv_obj_add_style(obj, &theme->styles.chart_series, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.chart_indicator, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.chart_series, LV_PART_CURSOR); + return; + } +#endif +#if LV_USE_CALENDAR + if(lv_obj_check_type(obj, &lv_calendar_class)) { + /* calendar */ + lv_obj_add_style(obj, &theme->styles.calendar, 0); + return; + } +#endif +#if LV_USE_CALENDAR && LV_USE_CALENDAR_HEADER_ARROW + if(lv_obj_check_type(obj, &lv_calendar_header_arrow_class)) { + /* calendar_header_arrow */ + lv_obj_add_style(obj, &theme->styles.calendar_header, 0); + return; + } +#endif +#if LV_USE_CALENDAR && LV_USE_CALENDAR_HEADER_DROPDOWN + if(lv_obj_check_type(obj, &lv_calendar_header_dropdown_class)) { + /* calendar_header_dropdown */ + lv_obj_add_style(obj, &theme->styles.calendar_header, 0); + return; + } +#endif +#if LV_USE_SCALE + if(lv_obj_check_type(obj, &lv_scale_class)) { + /* scale */ + lv_obj_add_style(obj, &theme->styles.scale, LV_PART_MAIN); + lv_obj_add_style(obj, &theme->styles.scale, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.scale, LV_PART_ITEMS); + return; + } +#endif +#if LV_USE_LINE + if(lv_obj_check_type(obj, &lv_line_class)) { + /* line */ + lv_obj_add_style(obj, &theme->styles.divider, 0); + return; + } +#endif +#if LV_USE_LED + if(lv_obj_check_type(obj, &lv_led_class)) { + /* led */ + lv_obj_add_style(obj, &theme->styles.led, 0); + return; + } +#endif +} + +static void style_init_reset(lv_style_t * style) +{ + if(lv_theme_material_is_inited()) { + lv_style_reset(style); + } + else { + lv_style_init(style); + } +} + +static void resolution_change_event_cb(lv_event_t * e) +{ + lv_display_t * disp = lv_event_get_target(e); + my_theme_t * theme = lv_event_get_user_data(e); + lv_theme_material_init(disp, &theme->config); +} diff --git a/Modules/lvgl-module/source/themes/lv_theme_material.h b/Modules/lvgl-module/source/themes/lv_theme_material.h new file mode 100644 index 000000000..42aa69f78 --- /dev/null +++ b/Modules/lvgl-module/source/themes/lv_theme_material.h @@ -0,0 +1,59 @@ +/* Generated by LSS. Do not edit. */ + +#ifndef LV_THEME_MATERIAL_H +#define LV_THEME_MATERIAL_H + +#include + +#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 */ diff --git a/Modules/lvgl-module/source/themes/lv_theme_material_mono.c b/Modules/lvgl-module/source/themes/lv_theme_material_mono.c new file mode 100644 index 000000000..e1b8f2db3 --- /dev/null +++ b/Modules/lvgl-module/source/themes/lv_theme_material_mono.c @@ -0,0 +1,1811 @@ +/* Generated by LSS. Do not edit. */ + +#define LV_USE_PRIVATE_API 1 /* For lv_theme_t */ + +#include "lv_theme_material_mono.h" + +/********************* + * DEFINES + *********************/ + +extern const lv_obj_class_t lvgl_toolbar_class; +extern const lv_obj_class_t lvgl_icon_button_class; +extern const lv_obj_class_t lvgl_icon_button_filled_class; +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_card_class; + +#define BLACK lv_color_black() +#define WHITE lv_color_white() +#define CORNER_NONE 0 +#define CORNER_SMALL (LV_DPX_CALC(theme->disp_dpi, 4)) +#define CORNER_MEDIUM (LV_DPX_CALC(theme->disp_dpi, 8)) +#define CORNER_LARGE (LV_DPX_CALC(theme->disp_dpi, 12)) +#define CORNER_FULL LV_RADIUS_CIRCLE +#define CORNER_COMPACT 2 +#define SPACE_1 (LV_DPX_CALC(theme->disp_dpi, 2)) +#define SPACE_2 (LV_DPX_CALC(theme->disp_dpi, 4)) +#define SPACE_3 (LV_DPX_CALC(theme->disp_dpi, 6)) +#define SPACE_4 (LV_DPX_CALC(theme->disp_dpi, 8)) +#define SPACE_6 (LV_DPX_CALC(theme->disp_dpi, 12)) +#define LINE_THIN 1 +#define LINE_MEDIUM 2 +#define LINE_THICK 3 +#define DURATION_SHORT 100 +#define DURATION_MEDIUM 250 +#define DURATION_LONG 500 +#define INK (theme->config.is_dark ? WHITE : BLACK) +#define PAPER (theme->config.is_dark ? BLACK : WHITE) +#define COLOR_PRIMARY INK +#define COLOR_ON_PRIMARY PAPER +#define COLOR_SURFACE PAPER +#define COLOR_ON_SURFACE INK +#define COLOR_OUTLINE INK +#define SHAPE_CONTROL (theme->config.is_compact ? CORNER_COMPACT : CORNER_FULL) +#define SHAPE_CONTAINER (theme->config.is_compact ? CORNER_COMPACT : CORNER_MEDIUM) +#define SHAPE_FIELD (theme->config.is_compact ? CORNER_COMPACT : CORNER_SMALL) +#define SHAPE_KEY (theme->config.is_compact ? CORNER_COMPACT : CORNER_SMALL) +#define SHAPE_DIALOG (theme->config.is_compact ? CORNER_COMPACT : CORNER_LARGE) +#define DENSITY_CONTAINER_PAD (theme->config.is_compact ? 2 : SPACE_4) +#define DENSITY_CONTAINER_GAP (theme->config.is_compact ? 2 : SPACE_3) +#define DENSITY_CONTROL_PAD_HOR (theme->config.is_compact ? 3 : SPACE_6) +#define DENSITY_CONTROL_PAD_VER (theme->config.is_compact ? 2 : SPACE_3) +#define DENSITY_CONTROL_GAP (theme->config.is_compact ? 2 : SPACE_2) +#define DENSITY_FIELD_PAD (theme->config.is_compact ? 2 : SPACE_3) +#define DENSITY_LIST_PAD (theme->config.is_compact ? 1 : SPACE_2) +#define DENSITY_LIST_ITEM_PAD_HOR (theme->config.is_compact ? 2 : SPACE_4) +#define DENSITY_LIST_ITEM_PAD_VER (theme->config.is_compact ? 2 : SPACE_3) +#define DENSITY_DIALOG_PAD (theme->config.is_compact ? 2 : SPACE_6) +#define DENSITY_KEY_GAP (theme->config.is_compact ? 1 : SPACE_1) +#define BORDER LINE_THIN +#define FOCUS_WIDTH (theme->config.is_compact ? LINE_THIN : LINE_MEDIUM) +#define FOCUS_OFFSET (theme->config.is_compact ? LINE_THIN : LINE_MEDIUM) +#define EDIT_WIDTH (theme->config.is_compact ? LINE_MEDIUM : LINE_THICK) +#define FOCUS_INSET_WIDTH (theme->config.is_compact ? LINE_THIN : LINE_MEDIUM) +#define MOTION_DURATION_SHORT (theme->config.animations_enabled ? DURATION_SHORT : 0) +#define MOTION_DURATION_MEDIUM (theme->config.animations_enabled ? DURATION_MEDIUM : 0) +#define MOTION_DURATION_LONG (theme->config.animations_enabled ? DURATION_LONG : 0) +#define SCREEN_COLOR COLOR_SURFACE +#define SCREEN_TEXT_COLOR COLOR_ON_SURFACE +#define SCREEN_GAP DENSITY_CONTAINER_GAP +#define CONTAINER_COLOR COLOR_SURFACE +#define CONTAINER_TEXT_COLOR COLOR_ON_SURFACE +#define CONTAINER_BORDER_COLOR COLOR_OUTLINE +#define CONTAINER_BORDER_WIDTH BORDER +#define CONTAINER_SHAPE SHAPE_CONTAINER +#define CONTAINER_PAD DENSITY_CONTAINER_PAD +#define CONTAINER_GAP DENSITY_CONTAINER_GAP +#define CARD_COLOR COLOR_SURFACE +#define CARD_TEXT_COLOR COLOR_ON_SURFACE +#define CARD_BORDER_COLOR COLOR_OUTLINE +#define CARD_BORDER_WIDTH LINE_MEDIUM +#define CARD_SHAPE SHAPE_CONTAINER +#define CARD_PAD DENSITY_CONTAINER_PAD +#define CARD_GAP DENSITY_CONTAINER_GAP +#define SCROLLBAR_COLOR COLOR_ON_SURFACE +#define SCROLLBAR_WIDTH (theme->config.is_compact ? 2 : LV_DPX_CALC(theme->disp_dpi, 3)) +#define SCROLLBAR_PAD 1 +#define BUTTON_CONTAINER_COLOR COLOR_SURFACE +#define BUTTON_LABEL_COLOR COLOR_ON_SURFACE +#define BUTTON_BORDER_COLOR COLOR_OUTLINE +#define BUTTON_BORDER_WIDTH BORDER +#define BUTTON_INVERTED_CONTAINER_COLOR COLOR_PRIMARY +#define BUTTON_INVERTED_LABEL_COLOR COLOR_ON_PRIMARY +#define BUTTON_SHAPE SHAPE_CONTROL +#define BUTTON_PAD_HOR DENSITY_CONTROL_PAD_HOR +#define BUTTON_PAD_VER DENSITY_CONTROL_PAD_VER +#define BUTTON_GAP DENSITY_CONTROL_GAP +#define ICON_BUTTON_CONTAINER_SHAPE (theme->config.is_compact ? CORNER_COMPACT : CORNER_LARGE) +#define ICON_BUTTON_FILLED_COLOR COLOR_PRIMARY +#define ICON_BUTTON_FILLED_ICON_COLOR COLOR_ON_PRIMARY +#define ICON_BUTTON_TONAL_COLOR COLOR_SURFACE +#define ICON_BUTTON_TONAL_ICON_COLOR COLOR_ON_SURFACE +#define ICON_BUTTON_TONAL_BORDER_COLOR COLOR_OUTLINE +#define APP_BAR_TITLE_COLOR COLOR_ON_SURFACE +#define APP_BAR_DIVIDER_COLOR COLOR_OUTLINE +#define APP_BAR_PAD_HOR (theme->config.is_compact ? 0 : SPACE_1) +#define APP_BAR_GAP (theme->config.is_compact ? 2 : SPACE_1) +#define APP_BAR_FOCUS_WIDTH FOCUS_INSET_WIDTH +#define FOCUS_RING_COLOR COLOR_ON_SURFACE +#define FOCUS_RING_WIDTH FOCUS_WIDTH +#define FOCUS_RING_OFFSET FOCUS_OFFSET +#define EDIT_RING_WIDTH EDIT_WIDTH +#define SWITCH_WIDTH (theme->config.is_compact ? 25 : LV_DPX_CALC(theme->disp_dpi, 40)) +#define SWITCH_HEIGHT (theme->config.is_compact ? 15 : LV_DPX_CALC(theme->disp_dpi, 24)) +#define SWITCH_TRACK_COLOR COLOR_SURFACE +#define SWITCH_TRACK_BORDER_COLOR COLOR_OUTLINE +#define SWITCH_TRACK_BORDER_WIDTH BORDER +#define SWITCH_TRACK_SELECTED_COLOR COLOR_PRIMARY +#define SWITCH_HANDLE_COLOR COLOR_ON_SURFACE +#define SWITCH_HANDLE_SELECTED_COLOR COLOR_ON_PRIMARY +#define SWITCH_HANDLE_PAD (theme->config.is_compact ? -3 : -LV_DPX_CALC(theme->disp_dpi, 5)) +#define SWITCH_HANDLE_SELECTED_PAD (theme->config.is_compact ? -2 : -LV_DPX_CALC(theme->disp_dpi, 3)) +#define CHECKBOX_GAP (theme->config.is_compact ? 3 : SPACE_4) +#define CHECKBOX_BOX_COLOR COLOR_SURFACE +#define CHECKBOX_BOX_BORDER_COLOR COLOR_OUTLINE +#define CHECKBOX_BOX_BORDER_WIDTH BORDER +#define CHECKBOX_BOX_SHAPE 2 +#define CHECKBOX_BOX_PAD 1 +#define CHECKBOX_BOX_SELECTED_COLOR COLOR_PRIMARY +#define CHECKBOX_ICON_COLOR COLOR_ON_PRIMARY +#define SLIDER_TRACK_COLOR COLOR_SURFACE +#define SLIDER_TRACK_BORDER_COLOR COLOR_OUTLINE +#define SLIDER_ACTIVE_TRACK_COLOR COLOR_PRIMARY +#define SLIDER_HANDLE_COLOR COLOR_PRIMARY +#define SLIDER_TRACK_HEIGHT (LV_DPX_CALC(theme->disp_dpi, 8)) +#define SLIDER_HANDLE_PAD ((theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 3)) + LV_DPX_CALC(theme->disp_dpi, 4)) +#define SLIDERBOX_GAP (theme->config.is_compact ? 4 : ((SLIDER_TRACK_HEIGHT / 2) + SLIDER_HANDLE_PAD) + SPACE_2) +#define ARC_TRACK_COLOR COLOR_ON_SURFACE +#define ARC_TRACK_WIDTH LINE_THIN +#define ARC_INDICATOR_COLOR COLOR_PRIMARY +#define ARC_INDICATOR_WIDTH (theme->config.is_compact ? LINE_MEDIUM : LV_DPX_CALC(theme->disp_dpi, 4)) +#define ARC_HANDLE_COLOR COLOR_PRIMARY +#define ARC_HANDLE_PAD (theme->config.is_compact ? 1 : LV_DPX_CALC(theme->disp_dpi, 3)) +#define FIELD_COLOR COLOR_SURFACE +#define FIELD_TEXT_COLOR COLOR_ON_SURFACE +#define FIELD_BORDER_COLOR COLOR_OUTLINE +#define FIELD_BORDER_WIDTH BORDER +#define FIELD_FOCUSED_BORDER_WIDTH LINE_MEDIUM +#define FIELD_SHAPE SHAPE_FIELD +#define FIELD_PAD DENSITY_FIELD_PAD +#define FIELD_CURSOR_COLOR COLOR_ON_SURFACE +#define FIELD_CURSOR_WIDTH LINE_MEDIUM +#define FIELD_SELECTION_COLOR COLOR_PRIMARY +#define FIELD_SELECTION_TEXT_COLOR COLOR_ON_PRIMARY +#define MENU_COLOR COLOR_SURFACE +#define MENU_BORDER_COLOR COLOR_OUTLINE +#define MENU_SHAPE SHAPE_FIELD +#define MENU_MAX_HEIGHT (LV_DPI_DEF * 2) +#define MENU_ITEM_LINE_SPACE (theme->config.is_compact ? 2 : SPACE_3) +#define ROLLER_LINE_SPACE (theme->config.is_compact ? 2 : SPACE_3) +#define KEYS_PAD DENSITY_KEY_GAP +#define KEYS_GAP DENSITY_KEY_GAP +#define KEY_COLOR COLOR_SURFACE +#define KEY_TEXT_COLOR COLOR_ON_SURFACE +#define KEY_BORDER_COLOR COLOR_OUTLINE +#define KEY_SHAPE SHAPE_KEY +#define LIST_PAD DENSITY_LIST_PAD +#define LIST_ITEM_PAD_HOR DENSITY_LIST_ITEM_PAD_HOR +#define LIST_ITEM_PAD_VER DENSITY_LIST_ITEM_PAD_VER +#define LIST_ITEM_GAP (theme->config.is_compact ? 3 : SPACE_4) +#define LIST_DIVIDER_COLOR COLOR_OUTLINE +#define TABS_DIVIDER_COLOR COLOR_OUTLINE +#define TAB_INDICATOR_COLOR COLOR_PRIMARY +#define TAB_INDICATOR_WIDTH (theme->config.is_compact ? LINE_MEDIUM : LINE_THICK) +#define TAB_PAGE_PAD DENSITY_CONTAINER_PAD +#define DIALOG_COLOR COLOR_SURFACE +#define DIALOG_BORDER_COLOR COLOR_OUTLINE +#define DIALOG_BORDER_WIDTH LINE_MEDIUM +#define DIALOG_SHAPE SHAPE_DIALOG +#define DIALOG_PAD DENSITY_DIALOG_PAD +#define DIALOG_GAP (theme->config.is_compact ? 2 : SPACE_2) +#define WINDOW_HEADER_BORDER_COLOR COLOR_OUTLINE +#define WINDOW_HEADER_PAD (theme->config.is_compact ? 2 : SPACE_2) +#define WINDOW_CONTENT_PAD DENSITY_CONTAINER_PAD +#define TABLE_CELL_PAD (theme->config.is_compact ? 2 : SPACE_3) +#define TABLE_DIVIDER_COLOR COLOR_OUTLINE +#define CHART_GRID_COLOR COLOR_ON_SURFACE +#define CHART_POINT_SIZE (theme->config.is_compact ? 3 : LV_DPX_CALC(theme->disp_dpi, 6)) +#define CALENDAR_PAD (theme->config.is_compact ? 2 : SPACE_2) +#define SCALE_COLOR COLOR_ON_SURFACE +#define SCALE_TICK_LENGTH (theme->config.is_compact ? 3 : LV_DPX_CALC(theme->disp_dpi, 6)) +#define DIVIDER_COLOR COLOR_OUTLINE + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_style_t focus_ring; + lv_style_t edit_ring; + lv_style_t focus_ring_inset; + lv_style_t edit_ring_inset; + lv_style_t inverted; + lv_style_t disabled; + lv_style_t scrollbar; +#if LV_USE_TABVIEW || LV_USE_TILEVIEW || LV_USE_WIN + lv_style_t page; +#endif + lv_style_t screen; + lv_style_t container; + lv_style_t card; +#if LV_USE_BUTTON || LV_USE_MSGBOX + lv_style_t button; +#endif + lv_style_t icon_button; + lv_style_t icon_button_selected; + lv_style_t icon_button_filled; + lv_style_t icon_button_filled_pressed; + lv_style_t icon_button_tonal; + lv_style_t app_bar; + lv_style_t app_bar_actions; + lv_style_t app_bar_button; + lv_style_t app_bar_focus_ring; +#if LV_USE_SWITCH + lv_style_t switch_track; +#endif +#if LV_USE_SWITCH + lv_style_t switch_indicator_selected; +#endif +#if LV_USE_SWITCH + lv_style_t switch_handle; +#endif +#if LV_USE_SWITCH + lv_style_t switch_handle_selected; +#endif +#if LV_USE_CHECKBOX + lv_style_t checkbox; +#endif +#if LV_USE_CHECKBOX + lv_style_t checkbox_box; +#endif +#if LV_USE_CHECKBOX + lv_style_t checkbox_box_selected; +#endif + lv_style_t sliderbox; +#if LV_USE_SLIDER + lv_style_t slider_track_size; +#endif +#if LV_USE_SLIDER + lv_style_t slider_knob_room; +#endif +#if LV_USE_BAR || LV_USE_SLIDER + lv_style_t slider_track; +#endif +#if LV_USE_BAR || LV_USE_SLIDER + lv_style_t slider_active_track; +#endif +#if LV_USE_SLIDER + lv_style_t slider_handle; +#endif +#if LV_USE_ARC || LV_USE_SPINNER + lv_style_t arc_track; +#endif +#if LV_USE_ARC || LV_USE_SPINNER + lv_style_t arc_indicator; +#endif +#if LV_USE_ARC + lv_style_t arc_handle; +#endif +#if LV_USE_DROPDOWN || LV_USE_SPINBOX || LV_USE_TEXTAREA + lv_style_t field; +#endif +#if LV_USE_SPINBOX || LV_USE_TEXTAREA + lv_style_t field_focused; +#endif +#if LV_USE_TEXTAREA + lv_style_t field_cursor; +#endif +#if LV_USE_LABEL && LV_USE_TEXTAREA + lv_style_t field_selection; +#endif +#if LV_USE_DROPDOWN + lv_style_t menu; +#endif +#if LV_USE_DROPDOWN + lv_style_t menu_item; +#endif +#if LV_USE_ROLLER + lv_style_t roller; +#endif +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + lv_style_t keys; +#endif +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + lv_style_t key; +#endif +#if LV_USE_LIST + lv_style_t list; +#endif +#if LV_USE_LIST + lv_style_t list_item; +#endif +#if LV_USE_LIST + lv_style_t list_subheader; +#endif +#if LV_USE_TABVIEW + lv_style_t tabs; +#endif +#if LV_USE_BUTTON && LV_USE_TABVIEW + lv_style_t tab; +#endif +#if LV_USE_BUTTON && LV_USE_TABVIEW + lv_style_t tab_selected; +#endif +#if LV_USE_TABVIEW + lv_style_t tab_page; +#endif +#if LV_USE_MSGBOX + lv_style_t dialog; +#endif +#if LV_USE_MSGBOX + lv_style_t dialog_header; +#endif +#if LV_USE_LABEL && LV_USE_MSGBOX + lv_style_t dialog_title; +#endif +#if LV_USE_MSGBOX + lv_style_t dialog_content; +#endif +#if LV_USE_MSGBOX + lv_style_t dialog_actions; +#endif +#if LV_USE_MENU + lv_style_t settings; +#endif +#if LV_USE_MENU + lv_style_t settings_sidebar; +#endif +#if LV_USE_MENU + lv_style_t settings_content; +#endif +#if LV_USE_MENU + lv_style_t settings_page; +#endif +#if LV_USE_MENU + lv_style_t settings_header; +#endif +#if LV_USE_MENU + lv_style_t settings_section; +#endif +#if LV_USE_MENU + lv_style_t settings_item; +#endif +#if LV_USE_MENU + lv_style_t settings_separator; +#endif +#if LV_USE_WIN + lv_style_t window_header; +#endif +#if LV_USE_LABEL && LV_USE_WIN + lv_style_t window_title; +#endif +#if LV_USE_WIN + lv_style_t window_content; +#endif +#if LV_USE_TABLE + lv_style_t table; +#endif +#if LV_USE_TABLE + lv_style_t table_cell; +#endif +#if LV_USE_CHART + lv_style_t chart; +#endif +#if LV_USE_CHART + lv_style_t chart_series; +#endif +#if LV_USE_CHART + lv_style_t chart_indicator; +#endif +#if LV_USE_CALENDAR + lv_style_t calendar; +#endif +#if LV_USE_BUTTONMATRIX && LV_USE_CALENDAR + lv_style_t calendar_day; +#endif +#if LV_USE_SCALE + lv_style_t scale; +#endif +#if LV_USE_LINE + lv_style_t divider; +#endif +#if LV_USE_LED + lv_style_t led; +#endif +} my_theme_styles_t; + +typedef enum { + DISP_SMALL = 3, + DISP_MEDIUM = 2, + DISP_LARGE = 1, +} disp_size_t; + +typedef struct { + lv_theme_t base; + disp_size_t disp_size; + int32_t disp_dpi; + lv_theme_material_mono_config_t config; + bool inited; + my_theme_styles_t styles; + +} my_theme_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void style_init_reset(lv_style_t * style); +static void theme_apply(lv_theme_t * th, lv_obj_t * obj); +static void resolution_change_event_cb(lv_event_t * e); + +/********************** + * STATIC VARIABLES + **********************/ + +static my_theme_t * theme_def = NULL; + +/********************** + * STATIC FUNCTIONS + **********************/ + +static inline lv_obj_t * lss_ancestor(lv_obj_t * obj, uint32_t depth) +{ + while(obj != NULL && depth > 0) { + obj = lv_obj_get_parent(obj); + depth--; + } + return obj; +} + +static inline bool lss_is_child(lv_obj_t * parent, int32_t index, lv_obj_t * child) +{ + return parent != NULL && lv_obj_get_child(parent, index) == child; +} + +static bool config_equals(const lv_theme_material_mono_config_t * a, const lv_theme_material_mono_config_t * b) +{ + return a->is_dark == b->is_dark && + a->is_compact == b->is_compact && + a->animations_enabled == b->animations_enabled && + lv_color_eq(a->color_primary, b->color_primary) && + lv_color_eq(a->color_secondary, b->color_secondary) && + a->font_small == b->font_small && + a->font_normal == b->font_normal && + a->font_large == b->font_large; +} + +static void style_init(my_theme_t * theme) +{ + style_init_reset(&theme->styles.focus_ring); + lv_style_set_outline_color(&theme->styles.focus_ring, FOCUS_RING_COLOR); + lv_style_set_outline_width(&theme->styles.focus_ring, FOCUS_RING_WIDTH); + lv_style_set_outline_pad(&theme->styles.focus_ring, FOCUS_RING_OFFSET); + lv_style_set_outline_opa(&theme->styles.focus_ring, LV_OPA_COVER); + + style_init_reset(&theme->styles.edit_ring); + lv_style_set_outline_color(&theme->styles.edit_ring, FOCUS_RING_COLOR); + lv_style_set_outline_width(&theme->styles.edit_ring, EDIT_RING_WIDTH); + lv_style_set_outline_pad(&theme->styles.edit_ring, FOCUS_RING_OFFSET); + lv_style_set_outline_opa(&theme->styles.edit_ring, LV_OPA_COVER); + + style_init_reset(&theme->styles.focus_ring_inset); + lv_style_set_outline_color(&theme->styles.focus_ring_inset, FOCUS_RING_COLOR); + lv_style_set_outline_width(&theme->styles.focus_ring_inset, FOCUS_INSET_WIDTH); + lv_style_set_outline_pad(&theme->styles.focus_ring_inset, -FOCUS_INSET_WIDTH); + lv_style_set_outline_opa(&theme->styles.focus_ring_inset, LV_OPA_COVER); + + style_init_reset(&theme->styles.edit_ring_inset); + lv_style_set_outline_color(&theme->styles.edit_ring_inset, FOCUS_RING_COLOR); + lv_style_set_outline_width(&theme->styles.edit_ring_inset, EDIT_RING_WIDTH); + lv_style_set_outline_pad(&theme->styles.edit_ring_inset, -EDIT_RING_WIDTH); + lv_style_set_outline_opa(&theme->styles.edit_ring_inset, LV_OPA_COVER); + + style_init_reset(&theme->styles.inverted); + lv_style_set_bg_color(&theme->styles.inverted, BUTTON_INVERTED_CONTAINER_COLOR); + lv_style_set_bg_opa(&theme->styles.inverted, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.inverted, BUTTON_INVERTED_LABEL_COLOR); + lv_style_set_border_color(&theme->styles.inverted, BUTTON_INVERTED_LABEL_COLOR); + lv_style_set_line_color(&theme->styles.inverted, BUTTON_INVERTED_LABEL_COLOR); + lv_style_set_arc_color(&theme->styles.inverted, BUTTON_INVERTED_LABEL_COLOR); + lv_style_set_outline_color(&theme->styles.inverted, BUTTON_INVERTED_LABEL_COLOR); + + style_init_reset(&theme->styles.disabled); + lv_style_set_border_width(&theme->styles.disabled, 0); + + style_init_reset(&theme->styles.scrollbar); + lv_style_set_bg_color(&theme->styles.scrollbar, SCROLLBAR_COLOR); + lv_style_set_bg_opa(&theme->styles.scrollbar, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.scrollbar, LV_RADIUS_CIRCLE); + lv_style_set_width(&theme->styles.scrollbar, SCROLLBAR_WIDTH); + lv_style_set_pad_all(&theme->styles.scrollbar, SCROLLBAR_PAD); + +#if LV_USE_TABVIEW || LV_USE_TILEVIEW || LV_USE_WIN + style_init_reset(&theme->styles.page); + lv_style_set_bg_opa(&theme->styles.page, LV_OPA_TRANSP); + lv_style_set_border_width(&theme->styles.page, 0); + lv_style_set_radius(&theme->styles.page, 0); + lv_style_set_pad_all(&theme->styles.page, 0); + lv_style_set_pad_gap(&theme->styles.page, 0); +#endif + + style_init_reset(&theme->styles.screen); + lv_style_set_bg_color(&theme->styles.screen, SCREEN_COLOR); + lv_style_set_bg_opa(&theme->styles.screen, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.screen, SCREEN_TEXT_COLOR); + lv_style_set_text_font(&theme->styles.screen, theme->config.font_normal); + lv_style_set_pad_row(&theme->styles.screen, SCREEN_GAP); + lv_style_set_pad_column(&theme->styles.screen, SCREEN_GAP); + + style_init_reset(&theme->styles.container); + lv_style_set_bg_color(&theme->styles.container, CONTAINER_COLOR); + lv_style_set_bg_opa(&theme->styles.container, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.container, CONTAINER_TEXT_COLOR); + lv_style_set_border_color(&theme->styles.container, CONTAINER_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.container, CONTAINER_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.container, CONTAINER_SHAPE); + lv_style_set_pad_all(&theme->styles.container, CONTAINER_PAD); + lv_style_set_pad_gap(&theme->styles.container, CONTAINER_GAP); + lv_style_set_line_color(&theme->styles.container, DIVIDER_COLOR); + lv_style_set_line_width(&theme->styles.container, BORDER); + lv_style_set_arc_color(&theme->styles.container, COLOR_ON_SURFACE); + lv_style_set_arc_width(&theme->styles.container, LINE_MEDIUM); + + style_init_reset(&theme->styles.card); + lv_style_set_bg_color(&theme->styles.card, CARD_COLOR); + lv_style_set_bg_opa(&theme->styles.card, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.card, CARD_TEXT_COLOR); + lv_style_set_border_color(&theme->styles.card, CARD_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.card, CARD_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.card, CARD_SHAPE); + lv_style_set_pad_all(&theme->styles.card, CARD_PAD); + lv_style_set_pad_gap(&theme->styles.card, CARD_GAP); + +#if LV_USE_BUTTON || LV_USE_MSGBOX + style_init_reset(&theme->styles.button); + lv_style_set_bg_color(&theme->styles.button, BUTTON_CONTAINER_COLOR); + lv_style_set_bg_opa(&theme->styles.button, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.button, BUTTON_LABEL_COLOR); + lv_style_set_border_color(&theme->styles.button, BUTTON_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.button, BUTTON_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.button, BUTTON_SHAPE); + lv_style_set_pad_hor(&theme->styles.button, BUTTON_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.button, BUTTON_PAD_VER); + lv_style_set_pad_gap(&theme->styles.button, BUTTON_GAP); +#endif + + style_init_reset(&theme->styles.icon_button); + lv_style_set_bg_opa(&theme->styles.icon_button, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.icon_button, COLOR_ON_SURFACE); + lv_style_set_border_width(&theme->styles.icon_button, 0); + lv_style_set_radius(&theme->styles.icon_button, BUTTON_SHAPE); + 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); + 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_text_color(&theme->styles.icon_button_filled, ICON_BUTTON_FILLED_ICON_COLOR); + lv_style_set_border_width(&theme->styles.icon_button_filled, 0); + lv_style_set_radius(&theme->styles.icon_button_filled, ICON_BUTTON_CONTAINER_SHAPE); + lv_style_set_pad_all(&theme->styles.icon_button_filled, BUTTON_PAD_VER); + + style_init_reset(&theme->styles.icon_button_filled_pressed); + lv_style_set_bg_color(&theme->styles.icon_button_filled_pressed, ICON_BUTTON_TONAL_COLOR); + lv_style_set_text_color(&theme->styles.icon_button_filled_pressed, ICON_BUTTON_TONAL_ICON_COLOR); + lv_style_set_border_color(&theme->styles.icon_button_filled_pressed, ICON_BUTTON_TONAL_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.icon_button_filled_pressed, BORDER); + + style_init_reset(&theme->styles.icon_button_tonal); + lv_style_set_bg_color(&theme->styles.icon_button_tonal, ICON_BUTTON_TONAL_COLOR); + lv_style_set_bg_opa(&theme->styles.icon_button_tonal, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.icon_button_tonal, ICON_BUTTON_TONAL_ICON_COLOR); + lv_style_set_border_color(&theme->styles.icon_button_tonal, ICON_BUTTON_TONAL_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.icon_button_tonal, BORDER); + lv_style_set_radius(&theme->styles.icon_button_tonal, ICON_BUTTON_CONTAINER_SHAPE); + lv_style_set_pad_all(&theme->styles.icon_button_tonal, BUTTON_PAD_VER); + + style_init_reset(&theme->styles.app_bar); + lv_style_set_bg_opa(&theme->styles.app_bar, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.app_bar, APP_BAR_TITLE_COLOR); + lv_style_set_radius(&theme->styles.app_bar, 0); + lv_style_set_border_color(&theme->styles.app_bar, APP_BAR_DIVIDER_COLOR); + lv_style_set_border_width(&theme->styles.app_bar, BORDER); + lv_style_set_border_side(&theme->styles.app_bar, LV_BORDER_SIDE_BOTTOM); + lv_style_set_pad_hor(&theme->styles.app_bar, APP_BAR_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.app_bar, 0); + 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); + lv_style_set_pad_all(&theme->styles.app_bar_button, 0); + + style_init_reset(&theme->styles.app_bar_focus_ring); + lv_style_set_outline_color(&theme->styles.app_bar_focus_ring, FOCUS_RING_COLOR); + lv_style_set_outline_width(&theme->styles.app_bar_focus_ring, APP_BAR_FOCUS_WIDTH); + lv_style_set_outline_pad(&theme->styles.app_bar_focus_ring, -APP_BAR_FOCUS_WIDTH); + lv_style_set_outline_opa(&theme->styles.app_bar_focus_ring, LV_OPA_COVER); + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_track); + lv_style_set_width(&theme->styles.switch_track, SWITCH_WIDTH); + lv_style_set_height(&theme->styles.switch_track, SWITCH_HEIGHT); + lv_style_set_bg_color(&theme->styles.switch_track, SWITCH_TRACK_COLOR); + lv_style_set_bg_opa(&theme->styles.switch_track, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.switch_track, SWITCH_TRACK_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.switch_track, SWITCH_TRACK_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.switch_track, LV_RADIUS_CIRCLE); + lv_style_set_pad_all(&theme->styles.switch_track, 0); + lv_style_set_anim_duration(&theme->styles.switch_track, MOTION_DURATION_SHORT); +#endif + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_indicator_selected); + lv_style_set_bg_color(&theme->styles.switch_indicator_selected, SWITCH_TRACK_SELECTED_COLOR); + lv_style_set_bg_opa(&theme->styles.switch_indicator_selected, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.switch_indicator_selected, LV_RADIUS_CIRCLE); +#endif + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_handle); + lv_style_set_bg_color(&theme->styles.switch_handle, SWITCH_HANDLE_COLOR); + lv_style_set_bg_opa(&theme->styles.switch_handle, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.switch_handle, LV_RADIUS_CIRCLE); + lv_style_set_pad_all(&theme->styles.switch_handle, SWITCH_HANDLE_PAD); +#endif + +#if LV_USE_SWITCH + style_init_reset(&theme->styles.switch_handle_selected); + lv_style_set_bg_color(&theme->styles.switch_handle_selected, SWITCH_HANDLE_SELECTED_COLOR); + lv_style_set_pad_all(&theme->styles.switch_handle_selected, SWITCH_HANDLE_SELECTED_PAD); +#endif + +#if LV_USE_CHECKBOX + style_init_reset(&theme->styles.checkbox); + lv_style_set_pad_gap(&theme->styles.checkbox, CHECKBOX_GAP); +#endif + +#if LV_USE_CHECKBOX + style_init_reset(&theme->styles.checkbox_box); + lv_style_set_bg_color(&theme->styles.checkbox_box, CHECKBOX_BOX_COLOR); + lv_style_set_bg_opa(&theme->styles.checkbox_box, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.checkbox_box, CHECKBOX_BOX_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.checkbox_box, CHECKBOX_BOX_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.checkbox_box, CHECKBOX_BOX_SHAPE); + lv_style_set_pad_all(&theme->styles.checkbox_box, CHECKBOX_BOX_PAD); + lv_style_set_text_font(&theme->styles.checkbox_box, theme->config.font_small); + lv_style_set_text_color(&theme->styles.checkbox_box, CHECKBOX_ICON_COLOR); +#endif + +#if LV_USE_CHECKBOX + style_init_reset(&theme->styles.checkbox_box_selected); + lv_style_set_bg_color(&theme->styles.checkbox_box_selected, CHECKBOX_BOX_SELECTED_COLOR); + lv_style_set_bg_image_src(&theme->styles.checkbox_box_selected, LV_SYMBOL_OK); +#endif + + style_init_reset(&theme->styles.sliderbox); + lv_style_set_pad_column(&theme->styles.sliderbox, SLIDERBOX_GAP); + +#if LV_USE_SLIDER + style_init_reset(&theme->styles.slider_track_size); + lv_style_set_height(&theme->styles.slider_track_size, SLIDER_TRACK_HEIGHT); +#endif + +#if LV_USE_SLIDER + style_init_reset(&theme->styles.slider_knob_room); + lv_style_set_margin_ver(&theme->styles.slider_knob_room, SLIDER_HANDLE_PAD); +#endif + +#if LV_USE_BAR || LV_USE_SLIDER + style_init_reset(&theme->styles.slider_track); + lv_style_set_bg_color(&theme->styles.slider_track, SLIDER_TRACK_COLOR); + lv_style_set_bg_opa(&theme->styles.slider_track, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.slider_track, SLIDER_TRACK_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.slider_track, BORDER); + lv_style_set_radius(&theme->styles.slider_track, LV_RADIUS_CIRCLE); + lv_style_set_pad_all(&theme->styles.slider_track, 0); +#endif + +#if LV_USE_BAR || LV_USE_SLIDER + style_init_reset(&theme->styles.slider_active_track); + lv_style_set_bg_color(&theme->styles.slider_active_track, SLIDER_ACTIVE_TRACK_COLOR); + lv_style_set_bg_opa(&theme->styles.slider_active_track, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.slider_active_track, LV_RADIUS_CIRCLE); +#endif + +#if LV_USE_SLIDER + style_init_reset(&theme->styles.slider_handle); + lv_style_set_bg_color(&theme->styles.slider_handle, SLIDER_HANDLE_COLOR); + lv_style_set_bg_opa(&theme->styles.slider_handle, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.slider_handle, LV_RADIUS_CIRCLE); + lv_style_set_pad_all(&theme->styles.slider_handle, SLIDER_HANDLE_PAD); +#endif + +#if LV_USE_ARC || LV_USE_SPINNER + style_init_reset(&theme->styles.arc_track); + lv_style_set_arc_color(&theme->styles.arc_track, ARC_TRACK_COLOR); + lv_style_set_arc_width(&theme->styles.arc_track, ARC_TRACK_WIDTH); +#endif + +#if LV_USE_ARC || LV_USE_SPINNER + style_init_reset(&theme->styles.arc_indicator); + lv_style_set_arc_color(&theme->styles.arc_indicator, ARC_INDICATOR_COLOR); + lv_style_set_arc_width(&theme->styles.arc_indicator, ARC_INDICATOR_WIDTH); + lv_style_set_arc_rounded(&theme->styles.arc_indicator, true); +#endif + +#if LV_USE_ARC + style_init_reset(&theme->styles.arc_handle); + lv_style_set_bg_color(&theme->styles.arc_handle, ARC_HANDLE_COLOR); + lv_style_set_bg_opa(&theme->styles.arc_handle, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.arc_handle, LV_RADIUS_CIRCLE); + lv_style_set_pad_all(&theme->styles.arc_handle, ARC_HANDLE_PAD); +#endif + +#if LV_USE_DROPDOWN || LV_USE_SPINBOX || LV_USE_TEXTAREA + style_init_reset(&theme->styles.field); + lv_style_set_bg_color(&theme->styles.field, FIELD_COLOR); + lv_style_set_bg_opa(&theme->styles.field, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.field, FIELD_TEXT_COLOR); + lv_style_set_border_color(&theme->styles.field, FIELD_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.field, FIELD_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.field, FIELD_SHAPE); + lv_style_set_pad_all(&theme->styles.field, FIELD_PAD); + lv_style_set_pad_gap(&theme->styles.field, FIELD_PAD); +#endif + +#if LV_USE_SPINBOX || LV_USE_TEXTAREA + style_init_reset(&theme->styles.field_focused); + lv_style_set_border_width(&theme->styles.field_focused, FIELD_FOCUSED_BORDER_WIDTH); +#endif + +#if LV_USE_TEXTAREA + style_init_reset(&theme->styles.field_cursor); + lv_style_set_border_color(&theme->styles.field_cursor, FIELD_CURSOR_COLOR); + lv_style_set_border_width(&theme->styles.field_cursor, FIELD_CURSOR_WIDTH); + lv_style_set_border_side(&theme->styles.field_cursor, LV_BORDER_SIDE_LEFT); + lv_style_set_bg_opa(&theme->styles.field_cursor, LV_OPA_TRANSP); + lv_style_set_anim_duration(&theme->styles.field_cursor, MOTION_DURATION_LONG); +#endif + +#if LV_USE_LABEL && LV_USE_TEXTAREA + style_init_reset(&theme->styles.field_selection); + lv_style_set_bg_color(&theme->styles.field_selection, FIELD_SELECTION_COLOR); + lv_style_set_bg_opa(&theme->styles.field_selection, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.field_selection, FIELD_SELECTION_TEXT_COLOR); +#endif + +#if LV_USE_DROPDOWN + style_init_reset(&theme->styles.menu); + lv_style_set_bg_color(&theme->styles.menu, MENU_COLOR); + lv_style_set_bg_opa(&theme->styles.menu, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.menu, MENU_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.menu, BORDER); + lv_style_set_radius(&theme->styles.menu, MENU_SHAPE); + lv_style_set_text_line_space(&theme->styles.menu, MENU_ITEM_LINE_SPACE); + lv_style_set_max_height(&theme->styles.menu, MENU_MAX_HEIGHT); + lv_style_set_clip_corner(&theme->styles.menu, true); +#endif + +#if LV_USE_DROPDOWN + style_init_reset(&theme->styles.menu_item); + lv_style_set_bg_opa(&theme->styles.menu_item, LV_OPA_TRANSP); +#endif + +#if LV_USE_ROLLER + style_init_reset(&theme->styles.roller); + lv_style_set_bg_color(&theme->styles.roller, COLOR_SURFACE); + lv_style_set_bg_opa(&theme->styles.roller, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.roller, COLOR_ON_SURFACE); + lv_style_set_border_color(&theme->styles.roller, COLOR_OUTLINE); + lv_style_set_border_width(&theme->styles.roller, BORDER); + lv_style_set_radius(&theme->styles.roller, SHAPE_CONTAINER); + lv_style_set_text_line_space(&theme->styles.roller, ROLLER_LINE_SPACE); + lv_style_set_text_align(&theme->styles.roller, LV_TEXT_ALIGN_CENTER); + lv_style_set_anim_duration(&theme->styles.roller, MOTION_DURATION_MEDIUM); +#endif + +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + style_init_reset(&theme->styles.keys); + lv_style_set_bg_opa(&theme->styles.keys, LV_OPA_TRANSP); + lv_style_set_border_width(&theme->styles.keys, 0); + lv_style_set_pad_all(&theme->styles.keys, KEYS_PAD); + lv_style_set_pad_gap(&theme->styles.keys, KEYS_GAP); +#endif + +#if LV_USE_BUTTONMATRIX || LV_USE_KEYBOARD + style_init_reset(&theme->styles.key); + lv_style_set_bg_color(&theme->styles.key, KEY_COLOR); + lv_style_set_bg_opa(&theme->styles.key, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.key, KEY_TEXT_COLOR); + lv_style_set_border_color(&theme->styles.key, KEY_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.key, BORDER); + lv_style_set_radius(&theme->styles.key, KEY_SHAPE); +#endif + +#if LV_USE_LIST + style_init_reset(&theme->styles.list); + lv_style_set_bg_color(&theme->styles.list, COLOR_SURFACE); + lv_style_set_bg_opa(&theme->styles.list, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.list, COLOR_OUTLINE); + lv_style_set_border_width(&theme->styles.list, BORDER); + lv_style_set_radius(&theme->styles.list, SHAPE_CONTAINER); + lv_style_set_pad_hor(&theme->styles.list, 0); + lv_style_set_pad_ver(&theme->styles.list, LIST_PAD); + lv_style_set_pad_gap(&theme->styles.list, 0); + lv_style_set_clip_corner(&theme->styles.list, true); +#endif + +#if LV_USE_LIST + style_init_reset(&theme->styles.list_item); + lv_style_set_bg_opa(&theme->styles.list_item, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.list_item, COLOR_ON_SURFACE); + lv_style_set_border_color(&theme->styles.list_item, LIST_DIVIDER_COLOR); + lv_style_set_border_width(&theme->styles.list_item, BORDER); + lv_style_set_border_side(&theme->styles.list_item, LV_BORDER_SIDE_BOTTOM); + lv_style_set_radius(&theme->styles.list_item, 0); + lv_style_set_pad_hor(&theme->styles.list_item, LIST_ITEM_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.list_item, LIST_ITEM_PAD_VER); + lv_style_set_pad_column(&theme->styles.list_item, LIST_ITEM_GAP); +#endif + +#if LV_USE_LIST + style_init_reset(&theme->styles.list_subheader); + lv_style_set_text_font(&theme->styles.list_subheader, theme->config.font_small); + lv_style_set_text_decor(&theme->styles.list_subheader, LV_TEXT_DECOR_UNDERLINE); + lv_style_set_pad_hor(&theme->styles.list_subheader, LIST_ITEM_PAD_HOR); + lv_style_set_pad_top(&theme->styles.list_subheader, LIST_ITEM_PAD_VER); + lv_style_set_pad_bottom(&theme->styles.list_subheader, LIST_PAD); +#endif + +#if LV_USE_TABVIEW + style_init_reset(&theme->styles.tabs); + lv_style_set_bg_opa(&theme->styles.tabs, LV_OPA_TRANSP); + lv_style_set_border_color(&theme->styles.tabs, TABS_DIVIDER_COLOR); + lv_style_set_border_width(&theme->styles.tabs, BORDER); + lv_style_set_border_side(&theme->styles.tabs, LV_BORDER_SIDE_BOTTOM); +#endif + +#if LV_USE_BUTTON && LV_USE_TABVIEW + style_init_reset(&theme->styles.tab); + lv_style_set_bg_opa(&theme->styles.tab, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.tab, COLOR_ON_SURFACE); + lv_style_set_border_width(&theme->styles.tab, 0); + lv_style_set_radius(&theme->styles.tab, 0); +#endif + +#if LV_USE_BUTTON && LV_USE_TABVIEW + style_init_reset(&theme->styles.tab_selected); + lv_style_set_border_color(&theme->styles.tab_selected, TAB_INDICATOR_COLOR); + lv_style_set_border_width(&theme->styles.tab_selected, TAB_INDICATOR_WIDTH); + lv_style_set_border_side(&theme->styles.tab_selected, LV_BORDER_SIDE_BOTTOM); +#endif + +#if LV_USE_TABVIEW + style_init_reset(&theme->styles.tab_page); + lv_style_set_pad_all(&theme->styles.tab_page, TAB_PAGE_PAD); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog); + lv_style_set_bg_color(&theme->styles.dialog, DIALOG_COLOR); + lv_style_set_bg_opa(&theme->styles.dialog, LV_OPA_COVER); + lv_style_set_text_color(&theme->styles.dialog, COLOR_ON_SURFACE); + lv_style_set_border_color(&theme->styles.dialog, DIALOG_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.dialog, DIALOG_BORDER_WIDTH); + lv_style_set_radius(&theme->styles.dialog, DIALOG_SHAPE); + lv_style_set_pad_all(&theme->styles.dialog, 0); + lv_style_set_pad_gap(&theme->styles.dialog, 0); + lv_style_set_clip_corner(&theme->styles.dialog, true); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog_header); + lv_style_set_pad_hor(&theme->styles.dialog_header, DIALOG_PAD); + lv_style_set_pad_top(&theme->styles.dialog_header, DIALOG_PAD); + lv_style_set_pad_bottom(&theme->styles.dialog_header, DIALOG_GAP); + lv_style_set_pad_gap(&theme->styles.dialog_header, DIALOG_GAP); +#endif + +#if LV_USE_LABEL && LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog_title); + lv_style_set_text_font(&theme->styles.dialog_title, theme->config.font_large); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog_content); + lv_style_set_pad_hor(&theme->styles.dialog_content, DIALOG_PAD); + lv_style_set_pad_ver(&theme->styles.dialog_content, DIALOG_GAP); +#endif + +#if LV_USE_MSGBOX + style_init_reset(&theme->styles.dialog_actions); + lv_style_set_pad_all(&theme->styles.dialog_actions, DIALOG_PAD); + lv_style_set_pad_top(&theme->styles.dialog_actions, DIALOG_GAP); + lv_style_set_pad_gap(&theme->styles.dialog_actions, DIALOG_GAP); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings); + lv_style_set_bg_color(&theme->styles.settings, COLOR_SURFACE); + lv_style_set_bg_opa(&theme->styles.settings, LV_OPA_COVER); + lv_style_set_pad_all(&theme->styles.settings, 0); + lv_style_set_pad_gap(&theme->styles.settings, 0); + lv_style_set_radius(&theme->styles.settings, 0); + lv_style_set_clip_corner(&theme->styles.settings, true); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_sidebar); + lv_style_set_pad_all(&theme->styles.settings_sidebar, 0); + lv_style_set_pad_gap(&theme->styles.settings_sidebar, 0); + lv_style_set_border_color(&theme->styles.settings_sidebar, COLOR_OUTLINE); + lv_style_set_border_width(&theme->styles.settings_sidebar, BORDER); + lv_style_set_border_side(&theme->styles.settings_sidebar, LV_BORDER_SIDE_RIGHT); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_content); + lv_style_set_pad_all(&theme->styles.settings_content, 0); + lv_style_set_pad_gap(&theme->styles.settings_content, 0); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_page); + lv_style_set_pad_hor(&theme->styles.settings_page, 0); + lv_style_set_pad_gap(&theme->styles.settings_page, 0); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_header); + lv_style_set_pad_hor(&theme->styles.settings_header, DENSITY_LIST_ITEM_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.settings_header, DENSITY_LIST_PAD); + lv_style_set_pad_gap(&theme->styles.settings_header, DENSITY_CONTAINER_GAP); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_section); + lv_style_set_bg_color(&theme->styles.settings_section, COLOR_SURFACE); + lv_style_set_bg_opa(&theme->styles.settings_section, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.settings_section, COLOR_OUTLINE); + lv_style_set_border_width(&theme->styles.settings_section, BORDER); + lv_style_set_radius(&theme->styles.settings_section, SHAPE_CONTAINER); + lv_style_set_clip_corner(&theme->styles.settings_section, true); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_item); + lv_style_set_pad_hor(&theme->styles.settings_item, DENSITY_LIST_ITEM_PAD_HOR); + lv_style_set_pad_ver(&theme->styles.settings_item, DENSITY_LIST_ITEM_PAD_VER); + lv_style_set_pad_gap(&theme->styles.settings_item, DENSITY_CONTAINER_GAP); +#endif + +#if LV_USE_MENU + style_init_reset(&theme->styles.settings_separator); + lv_style_set_bg_opa(&theme->styles.settings_separator, LV_OPA_TRANSP); + lv_style_set_pad_ver(&theme->styles.settings_separator, DENSITY_LIST_PAD); +#endif + +#if LV_USE_WIN + style_init_reset(&theme->styles.window_header); + lv_style_set_bg_color(&theme->styles.window_header, COLOR_SURFACE); + lv_style_set_bg_opa(&theme->styles.window_header, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.window_header, WINDOW_HEADER_BORDER_COLOR); + lv_style_set_border_width(&theme->styles.window_header, BORDER); + lv_style_set_border_side(&theme->styles.window_header, LV_BORDER_SIDE_BOTTOM); + lv_style_set_pad_all(&theme->styles.window_header, WINDOW_HEADER_PAD); + lv_style_set_pad_gap(&theme->styles.window_header, WINDOW_HEADER_PAD); +#endif + +#if LV_USE_LABEL && LV_USE_WIN + style_init_reset(&theme->styles.window_title); + lv_style_set_text_font(&theme->styles.window_title, theme->config.font_large); +#endif + +#if LV_USE_WIN + style_init_reset(&theme->styles.window_content); + lv_style_set_bg_opa(&theme->styles.window_content, LV_OPA_TRANSP); + lv_style_set_pad_all(&theme->styles.window_content, WINDOW_CONTENT_PAD); + lv_style_set_pad_gap(&theme->styles.window_content, DENSITY_CONTAINER_GAP); +#endif + +#if LV_USE_TABLE + style_init_reset(&theme->styles.table); + lv_style_set_bg_color(&theme->styles.table, COLOR_SURFACE); + lv_style_set_bg_opa(&theme->styles.table, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.table, COLOR_OUTLINE); + lv_style_set_border_width(&theme->styles.table, BORDER); + lv_style_set_radius(&theme->styles.table, 0); + lv_style_set_pad_all(&theme->styles.table, 0); +#endif + +#if LV_USE_TABLE + style_init_reset(&theme->styles.table_cell); + lv_style_set_bg_opa(&theme->styles.table_cell, LV_OPA_TRANSP); + lv_style_set_text_color(&theme->styles.table_cell, COLOR_ON_SURFACE); + lv_style_set_pad_all(&theme->styles.table_cell, TABLE_CELL_PAD); + lv_style_set_border_color(&theme->styles.table_cell, TABLE_DIVIDER_COLOR); + lv_style_set_border_width(&theme->styles.table_cell, BORDER); + lv_style_set_border_side(&theme->styles.table_cell, LV_BORDER_SIDE_BOTTOM); +#endif + +#if LV_USE_CHART + style_init_reset(&theme->styles.chart); + lv_style_set_bg_color(&theme->styles.chart, COLOR_SURFACE); + lv_style_set_bg_opa(&theme->styles.chart, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.chart, COLOR_OUTLINE); + lv_style_set_border_width(&theme->styles.chart, BORDER); + lv_style_set_radius(&theme->styles.chart, SHAPE_CONTAINER); + lv_style_set_pad_all(&theme->styles.chart, DENSITY_CONTAINER_PAD); + lv_style_set_line_color(&theme->styles.chart, CHART_GRID_COLOR); +#endif + +#if LV_USE_CHART + style_init_reset(&theme->styles.chart_series); + lv_style_set_line_color(&theme->styles.chart_series, COLOR_ON_SURFACE); + lv_style_set_line_width(&theme->styles.chart_series, LINE_MEDIUM); + lv_style_set_size(&theme->styles.chart_series, CHART_POINT_SIZE, CHART_POINT_SIZE); +#endif + +#if LV_USE_CHART + style_init_reset(&theme->styles.chart_indicator); + lv_style_set_bg_color(&theme->styles.chart_indicator, COLOR_PRIMARY); + lv_style_set_bg_opa(&theme->styles.chart_indicator, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.chart_indicator, LV_RADIUS_CIRCLE); + lv_style_set_size(&theme->styles.chart_indicator, CHART_POINT_SIZE, CHART_POINT_SIZE); +#endif + +#if LV_USE_CALENDAR + style_init_reset(&theme->styles.calendar); + lv_style_set_bg_color(&theme->styles.calendar, COLOR_SURFACE); + lv_style_set_bg_opa(&theme->styles.calendar, LV_OPA_COVER); + lv_style_set_border_color(&theme->styles.calendar, COLOR_OUTLINE); + lv_style_set_border_width(&theme->styles.calendar, BORDER); + lv_style_set_radius(&theme->styles.calendar, SHAPE_CONTAINER); + lv_style_set_pad_all(&theme->styles.calendar, CALENDAR_PAD); + lv_style_set_pad_gap(&theme->styles.calendar, CALENDAR_PAD); +#endif + +#if LV_USE_BUTTONMATRIX && LV_USE_CALENDAR + style_init_reset(&theme->styles.calendar_day); + lv_style_set_bg_opa(&theme->styles.calendar_day, LV_OPA_TRANSP); + lv_style_set_border_width(&theme->styles.calendar_day, 0); + lv_style_set_radius(&theme->styles.calendar_day, LV_RADIUS_CIRCLE); +#endif + +#if LV_USE_SCALE + style_init_reset(&theme->styles.scale); + lv_style_set_line_color(&theme->styles.scale, SCALE_COLOR); + lv_style_set_line_width(&theme->styles.scale, BORDER); + lv_style_set_arc_color(&theme->styles.scale, SCALE_COLOR); + lv_style_set_arc_width(&theme->styles.scale, BORDER); + lv_style_set_text_color(&theme->styles.scale, SCALE_COLOR); + lv_style_set_length(&theme->styles.scale, SCALE_TICK_LENGTH); +#endif + +#if LV_USE_LINE + style_init_reset(&theme->styles.divider); + lv_style_set_line_color(&theme->styles.divider, DIVIDER_COLOR); + lv_style_set_line_width(&theme->styles.divider, BORDER); +#endif + +#if LV_USE_LED + style_init_reset(&theme->styles.led); + lv_style_set_bg_color(&theme->styles.led, COLOR_ON_SURFACE); + lv_style_set_bg_opa(&theme->styles.led, LV_OPA_COVER); + lv_style_set_radius(&theme->styles.led, LV_RADIUS_CIRCLE); + lv_style_set_border_color(&theme->styles.led, COLOR_OUTLINE); + lv_style_set_border_width(&theme->styles.led, BORDER); +#endif +} + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_theme_material_mono_config_init(lv_theme_material_mono_config_t * config) +{ + lv_memzero(config, sizeof(*config)); + config->is_dark = false; + config->is_compact = false; + config->animations_enabled = true; + config->color_primary = lv_color_black(); + config->color_secondary = lv_color_black(); + config->font_small = LV_FONT_DEFAULT; + config->font_normal = LV_FONT_DEFAULT; + config->font_large = LV_FONT_DEFAULT; +} + +lv_theme_t * lv_theme_material_mono_init(lv_display_t * disp, const lv_theme_material_mono_config_t * config) +{ + if(!lv_theme_material_mono_is_inited()) { + theme_def = lv_malloc_zeroed(sizeof(my_theme_t)); + LV_ASSERT_MALLOC(theme_def); + } + + my_theme_t * theme = theme_def; + + lv_display_t * new_disp = disp == NULL ? lv_display_get_default() : disp; + int32_t new_dpi = lv_display_get_dpi(new_disp); + int32_t hor_res = lv_display_get_horizontal_resolution(new_disp); + int32_t ver_res = lv_display_get_vertical_resolution(new_disp); + int32_t greater_res = LV_MAX(hor_res, ver_res); + disp_size_t new_size; + + if(greater_res <= 320) new_size = DISP_SMALL; + else if(greater_res < 720) new_size = DISP_MEDIUM; + else new_size = DISP_LARGE; + + if(theme->inited && theme->disp_dpi == new_dpi && theme->disp_size == new_size && + config_equals(&theme->config, config)) { + return (lv_theme_t *) theme; + } + + theme->disp_size = new_size; + theme->disp_dpi = new_dpi; + theme->config = *config; + theme->base.disp = new_disp; + theme->base.apply_cb = theme_apply; + theme->base.color_primary = config->color_primary; + theme->base.color_secondary = config->color_secondary; + theme->base.font_small = config->font_small; + theme->base.font_normal = config->font_normal; + theme->base.font_large = config->font_large; + + style_init(theme); + + if(disp == NULL || lv_display_get_theme(disp) == (lv_theme_t *)theme) { + lv_obj_report_style_change(NULL); + } + + theme->inited = true; + + /* Re-initialize the styles when the resolution changes, as the display size affects the styles */ + lv_display_remove_event_cb_with_user_data(new_disp, resolution_change_event_cb, theme); + lv_display_add_event_cb(new_disp, resolution_change_event_cb, LV_EVENT_RESOLUTION_CHANGED, theme); + + return (lv_theme_t *) theme; +} + +bool lv_theme_material_mono_is_inited(void) +{ + return theme_def != NULL && theme_def->inited; +} + +lv_theme_t * lv_theme_material_mono_get(void) +{ + if(!lv_theme_material_mono_is_inited()) { + return NULL; + } + return (lv_theme_t *) theme_def; +} + +void lv_theme_material_mono_deinit(void) +{ + my_theme_t * theme = theme_def; + if(theme) { + if(theme->inited) { + lv_style_t * theme_styles = (lv_style_t *)(&(theme->styles)); + uint32_t i; + for(i = 0; i < sizeof(my_theme_styles_t) / sizeof(lv_style_t); i++) { + lv_style_reset(theme_styles + i); + } + } + lv_free(theme_def); + theme_def = NULL; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void theme_apply(lv_theme_t * th, lv_obj_t * obj) +{ + LV_UNUSED(th); + + my_theme_t * theme = theme_def; + lv_obj_t * parent = lv_obj_get_parent(obj); + LV_UNUSED(theme); + LV_UNUSED(parent); + + /* null > * */ + if(parent == NULL) { + lv_obj_add_style(obj, &theme->styles.screen, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } + if(lv_obj_check_type(obj, &lv_obj_class)) { +#if LV_USE_TABVIEW + /* tabview > obj[1] */ + if(lv_obj_check_type(parent, &lv_tabview_class) && lss_is_child(parent, 1, obj)) { + return; + } +#endif +#if LV_USE_TABVIEW + /* tabview > obj[0] */ + if(lv_obj_check_type(parent, &lv_tabview_class) && lss_is_child(parent, 0, obj)) { + lv_obj_add_style(obj, &theme->styles.tabs, 0); + return; + } +#endif +#if LV_USE_TABVIEW + /* tabview > * > obj */ + if(lv_obj_check_type(lss_ancestor(obj, 2), &lv_tabview_class)) { + lv_obj_add_style(obj, &theme->styles.tab_page, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#endif +#if LV_USE_WIN + /* win > obj[0] */ + if(lv_obj_check_type(parent, &lv_win_class) && lss_is_child(parent, 0, obj)) { + lv_obj_add_style(obj, &theme->styles.window_header, 0); + return; + } +#endif +#if LV_USE_WIN + /* win > obj[1] */ + if(lv_obj_check_type(parent, &lv_win_class) && lss_is_child(parent, 1, obj)) { + lv_obj_add_style(obj, &theme->styles.window_content, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#endif +#if LV_USE_CALENDAR + /* calendar > obj */ + if(lv_obj_check_type(parent, &lv_calendar_class)) { + return; + } +#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 */ + lv_obj_add_style(obj, &theme->styles.container, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#if LV_USE_BUTTON + if(lv_obj_check_type(obj, &lv_button_class)) { +#if LV_USE_TABVIEW + /* tabview > *[0] > button */ + if(lv_obj_check_type(lss_ancestor(obj, 2), &lv_tabview_class) && lss_is_child(lss_ancestor(obj, 2), 0, parent)) { + lv_obj_add_style(obj, &theme->styles.tab, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.tab_selected, LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + return; + } +#endif +#if LV_USE_MENU + /* menu_sidebar_header_cont > button */ + if(lv_obj_check_type(parent, &lv_menu_sidebar_header_cont_class)) { + lv_obj_add_style(obj, &theme->styles.icon_button, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + return; + } +#endif +#if LV_USE_MENU + /* menu_main_header_cont > button */ + if(lv_obj_check_type(parent, &lv_menu_main_header_cont_class)) { + lv_obj_add_style(obj, &theme->styles.icon_button, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + return; + } +#endif +#if LV_USE_WIN + /* win > obj[0] > button */ + if(lv_obj_check_type(lss_ancestor(obj, 2), &lv_win_class) && lv_obj_check_type(parent, &lv_obj_class) && lss_is_child(lss_ancestor(obj, 2), 0, parent)) { + lv_obj_add_style(obj, &theme->styles.icon_button, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + return; + } +#endif + /* button */ + lv_obj_add_style(obj, &theme->styles.button, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif + if(lv_obj_check_type(obj, &lvgl_icon_button_class)) { + /* toolbar > icon_button */ + if(lv_obj_check_type(parent, &lvgl_toolbar_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; + } + /* 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 */ + 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.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.disabled, LV_STATE_DISABLED); + return; + } + if(lv_obj_check_type(obj, &lvgl_toolbar_class)) { + /* toolbar */ + lv_obj_add_style(obj, &theme->styles.app_bar, 0); + return; + } + if(lv_obj_check_type(obj, &lvgl_icon_button_filled_class)) { + /* icon_button_filled */ + lv_obj_add_style(obj, &theme->styles.icon_button_filled, 0); + lv_obj_add_style(obj, &theme->styles.icon_button_filled_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } + if(lv_obj_check_type(obj, &lvgl_icon_button_tonal_class)) { + /* icon_button_tonal */ + lv_obj_add_style(obj, &theme->styles.icon_button_tonal, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_header_button_class)) { + /* msgbox_header_button */ + lv_obj_add_style(obj, &theme->styles.icon_button, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_footer_button_class)) { + /* msgbox_footer_button */ + lv_obj_add_style(obj, &theme->styles.button, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_LABEL + if(lv_obj_check_type(obj, &lv_label_class)) { +#if LV_USE_MSGBOX + /* msgbox_header > label */ + if(lv_obj_check_type(parent, &lv_msgbox_header_class)) { + lv_obj_add_style(obj, &theme->styles.dialog_title, 0); + return; + } +#endif +#if LV_USE_WIN + /* win > obj[0] > label */ + if(lv_obj_check_type(lss_ancestor(obj, 2), &lv_win_class) && lv_obj_check_type(parent, &lv_obj_class) && lss_is_child(lss_ancestor(obj, 2), 0, parent)) { + lv_obj_add_style(obj, &theme->styles.window_title, 0); + return; + } +#endif +#if LV_USE_TEXTAREA + /* textarea > label */ + if(lv_obj_check_type(parent, &lv_textarea_class)) { + lv_obj_add_style(obj, &theme->styles.field_selection, LV_PART_SELECTED); + return; + } +#endif + return; + } +#endif +#if LV_USE_SWITCH + if(lv_obj_check_type(obj, &lv_switch_class)) { + /* switch */ + lv_obj_add_style(obj, &theme->styles.switch_track, 0); + lv_obj_add_style(obj, &theme->styles.switch_indicator_selected, LV_PART_INDICATOR | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.switch_handle, LV_PART_KNOB); + lv_obj_add_style(obj, &theme->styles.switch_handle_selected, LV_PART_KNOB | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_CHECKBOX + if(lv_obj_check_type(obj, &lv_checkbox_class)) { + /* checkbox */ + lv_obj_add_style(obj, &theme->styles.checkbox, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.checkbox_box, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.checkbox_box_selected, LV_PART_INDICATOR | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_PART_INDICATOR | LV_STATE_DISABLED); + return; + } +#endif + if(lv_obj_check_type(obj, &lvgl_card_class)) { + /* card */ + lv_obj_add_style(obj, &theme->styles.card, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } + if(lv_obj_check_type(obj, &lvgl_sliderbox_class)) { + /* sliderbox */ + lv_obj_add_style(obj, &theme->styles.sliderbox, 0); + return; + } +#if LV_USE_SLIDER + if(lv_obj_check_type(obj, &lv_slider_class)) { + /* slider */ + lv_obj_add_style(obj, &theme->styles.slider_track, 0); + lv_obj_add_style(obj, &theme->styles.slider_track_size, 0); + lv_obj_add_style(obj, &theme->styles.slider_knob_room, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.slider_active_track, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.slider_handle, LV_PART_KNOB); + return; + } +#endif +#if LV_USE_BAR + if(lv_obj_check_type(obj, &lv_bar_class)) { + /* bar */ + lv_obj_add_style(obj, &theme->styles.slider_track, 0); + lv_obj_add_style(obj, &theme->styles.slider_active_track, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + return; + } +#endif +#if LV_USE_ARC + if(lv_obj_check_type(obj, &lv_arc_class)) { + /* arc */ + lv_obj_add_style(obj, &theme->styles.arc_track, 0); + lv_obj_add_style(obj, &theme->styles.arc_indicator, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.arc_handle, LV_PART_KNOB); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + return; + } +#endif +#if LV_USE_SPINNER + if(lv_obj_check_type(obj, &lv_spinner_class)) { + /* spinner */ + lv_obj_add_style(obj, &theme->styles.arc_track, 0); + lv_obj_add_style(obj, &theme->styles.arc_indicator, LV_PART_INDICATOR); + return; + } +#endif +#if LV_USE_TEXTAREA + if(lv_obj_check_type(obj, &lv_textarea_class)) { + /* textarea */ + lv_obj_add_style(obj, &theme->styles.field, 0); + lv_obj_add_style(obj, &theme->styles.field_focused, LV_STATE_FOCUSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.field_cursor, LV_PART_CURSOR | LV_STATE_FOCUSED); + return; + } +#endif +#if LV_USE_SPINBOX + if(lv_obj_check_type(obj, &lv_spinbox_class)) { + /* spinbox */ + lv_obj_add_style(obj, &theme->styles.field, 0); + lv_obj_add_style(obj, &theme->styles.field_focused, LV_STATE_FOCUSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_CURSOR); + return; + } +#endif +#if LV_USE_DROPDOWN + if(lv_obj_check_type(obj, &lv_dropdown_class)) { + /* dropdown */ + lv_obj_add_style(obj, &theme->styles.field, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_DROPDOWN + if(lv_obj_check_type(obj, &lv_dropdownlist_class)) { + /* dropdownlist */ + lv_obj_add_style(obj, &theme->styles.menu, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.menu_item, LV_PART_SELECTED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_SELECTED | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_SELECTED | LV_STATE_PRESSED); + return; + } +#endif +#if LV_USE_ROLLER + if(lv_obj_check_type(obj, &lv_roller_class)) { + /* roller */ + lv_obj_add_style(obj, &theme->styles.roller, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_SELECTED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + return; + } +#endif +#if LV_USE_BUTTONMATRIX + if(lv_obj_check_type(obj, &lv_buttonmatrix_class)) { +#if LV_USE_CALENDAR + /* calendar > buttonmatrix */ + if(lv_obj_check_type(parent, &lv_calendar_class)) { + lv_obj_add_style(obj, &theme->styles.keys, 0); + lv_obj_add_style(obj, &theme->styles.calendar_day, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_ITEMS | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_PART_ITEMS | LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_PART_ITEMS | LV_STATE_DISABLED); + return; + } +#endif + /* buttonmatrix */ + lv_obj_add_style(obj, &theme->styles.keys, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.key, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_ITEMS | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_PART_ITEMS | LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_PART_ITEMS | LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_KEYBOARD + if(lv_obj_check_type(obj, &lv_keyboard_class)) { + /* keyboard */ + lv_obj_add_style(obj, &theme->styles.keys, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.key, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_ITEMS | LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_PART_ITEMS | LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_PART_ITEMS | LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_LIST + if(lv_obj_check_type(obj, &lv_list_class)) { + /* list */ + lv_obj_add_style(obj, &theme->styles.list, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#endif +#if LV_USE_LIST + if(lv_obj_check_type(obj, &lv_list_text_class)) { + /* list_text */ + lv_obj_add_style(obj, &theme->styles.list_subheader, 0); + return; + } +#endif +#if LV_USE_LIST + if(lv_obj_check_type(obj, &lv_list_button_class)) { + /* list_button */ + lv_obj_add_style(obj, &theme->styles.list_item, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED); + return; + } +#endif +#if LV_USE_TABLE + if(lv_obj_check_type(obj, &lv_table_class)) { + /* table */ + lv_obj_add_style(obj, &theme->styles.table, 0); + lv_obj_add_style(obj, &theme->styles.focus_ring, LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &theme->styles.edit_ring, LV_STATE_EDITED); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.table_cell, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_PART_ITEMS | LV_STATE_FOCUS_KEY); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_class)) { + /* menu */ + lv_obj_add_style(obj, &theme->styles.settings, 0); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_sidebar_cont_class)) { + /* menu_sidebar_cont */ + lv_obj_add_style(obj, &theme->styles.settings_sidebar, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_main_cont_class)) { + /* menu_main_cont */ + lv_obj_add_style(obj, &theme->styles.settings_content, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_page_class)) { + /* menu_page */ + lv_obj_add_style(obj, &theme->styles.settings_page, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_sidebar_header_cont_class)) { + /* menu_sidebar_header_cont */ + lv_obj_add_style(obj, &theme->styles.settings_header, 0); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_main_header_cont_class)) { + /* menu_main_header_cont */ + lv_obj_add_style(obj, &theme->styles.settings_header, 0); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_section_class)) { + /* menu_section */ + lv_obj_add_style(obj, &theme->styles.settings_section, 0); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_cont_class)) { + /* menu_cont */ + lv_obj_add_style(obj, &theme->styles.settings_item, 0); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_PRESSED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_CHECKED); + lv_obj_add_style(obj, &theme->styles.inverted, LV_STATE_FOCUS_KEY); + return; + } +#endif +#if LV_USE_MENU + if(lv_obj_check_type(obj, &lv_menu_separator_class)) { + /* menu_separator */ + lv_obj_add_style(obj, &theme->styles.settings_separator, 0); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_class)) { + /* msgbox */ + lv_obj_add_style(obj, &theme->styles.dialog, 0); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_backdrop_class)) { + /* msgbox_backdrop */ + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_header_class)) { + /* msgbox_header */ + lv_obj_add_style(obj, &theme->styles.dialog_header, 0); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_content_class)) { + /* msgbox_content */ + lv_obj_add_style(obj, &theme->styles.dialog_content, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#endif +#if LV_USE_MSGBOX + if(lv_obj_check_type(obj, &lv_msgbox_footer_class)) { + /* msgbox_footer */ + lv_obj_add_style(obj, &theme->styles.dialog_actions, 0); + return; + } +#endif +#if LV_USE_TABVIEW + if(lv_obj_check_type(obj, &lv_tabview_class)) { + /* tabview */ + lv_obj_add_style(obj, &theme->styles.page, 0); + return; + } +#endif +#if LV_USE_WIN + if(lv_obj_check_type(obj, &lv_win_class)) { + /* win */ + lv_obj_add_style(obj, &theme->styles.page, 0); + return; + } +#endif +#if LV_USE_TILEVIEW + if(lv_obj_check_type(obj, &lv_tileview_class)) { + /* tileview */ + lv_obj_add_style(obj, &theme->styles.page, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#endif +#if LV_USE_TILEVIEW + if(lv_obj_check_type(obj, &lv_tileview_tile_class)) { + /* tileview_tile */ + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + return; + } +#endif +#if LV_USE_CHART + if(lv_obj_check_type(obj, &lv_chart_class)) { + /* chart */ + lv_obj_add_style(obj, &theme->styles.chart, 0); + lv_obj_add_style(obj, &theme->styles.scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &theme->styles.chart_series, LV_PART_ITEMS); + lv_obj_add_style(obj, &theme->styles.chart_indicator, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.chart_series, LV_PART_CURSOR); + return; + } +#endif +#if LV_USE_CALENDAR + if(lv_obj_check_type(obj, &lv_calendar_class)) { + /* calendar */ + lv_obj_add_style(obj, &theme->styles.calendar, 0); + return; + } +#endif +#if LV_USE_SCALE + if(lv_obj_check_type(obj, &lv_scale_class)) { + /* scale */ + lv_obj_add_style(obj, &theme->styles.scale, LV_PART_MAIN); + lv_obj_add_style(obj, &theme->styles.scale, LV_PART_INDICATOR); + lv_obj_add_style(obj, &theme->styles.scale, LV_PART_ITEMS); + return; + } +#endif +#if LV_USE_LINE + if(lv_obj_check_type(obj, &lv_line_class)) { + /* line */ + lv_obj_add_style(obj, &theme->styles.divider, 0); + return; + } +#endif +#if LV_USE_LED + if(lv_obj_check_type(obj, &lv_led_class)) { + /* led */ + lv_obj_add_style(obj, &theme->styles.led, 0); + return; + } +#endif +} + +static void style_init_reset(lv_style_t * style) +{ + if(lv_theme_material_mono_is_inited()) { + lv_style_reset(style); + } + else { + lv_style_init(style); + } +} + +static void resolution_change_event_cb(lv_event_t * e) +{ + lv_display_t * disp = lv_event_get_target(e); + my_theme_t * theme = lv_event_get_user_data(e); + lv_theme_material_mono_init(disp, &theme->config); +} diff --git a/Modules/lvgl-module/source/themes/lv_theme_material_mono.h b/Modules/lvgl-module/source/themes/lv_theme_material_mono.h new file mode 100644 index 000000000..3da37e557 --- /dev/null +++ b/Modules/lvgl-module/source/themes/lv_theme_material_mono.h @@ -0,0 +1,58 @@ +/* Generated by LSS. Do not edit. */ + +#ifndef LV_THEME_MATERIAL_MONO_H +#define LV_THEME_MATERIAL_MONO_H + +#include + +#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 */ diff --git a/Modules/lvgl-module/source/widgets/card.cpp b/Modules/lvgl-module/source/widgets/card.cpp new file mode 100644 index 000000000..a1eaa7cb1 --- /dev/null +++ b/Modules/lvgl-module/source/widgets/card.cpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +#define LV_USE_PRIVATE_API 1 // For the lv_obj_class_t declaration + +#include + +// 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; +} diff --git a/Modules/lvgl-module/source/widgets/icon_button.cpp b/Modules/lvgl-module/source/widgets/icon_button.cpp new file mode 100644 index 000000000..b94f25a12 --- /dev/null +++ b/Modules/lvgl-module/source/widgets/icon_button.cpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +#define LV_USE_PRIVATE_API 1 // For the lv_obj_class_t declaration + +#include + +// 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); +} diff --git a/Modules/lvgl-module/source/widgets/sliderbox.cpp b/Modules/lvgl-module/source/widgets/sliderbox.cpp index 59de76e09..ec4301431 100644 --- a/Modules/lvgl-module/source/widgets/sliderbox.cpp +++ b/Modules/lvgl-module/source/widgets/sliderbox.cpp @@ -17,7 +17,8 @@ typedef struct { 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, .constructor_cb = &sliderbox_constructor, .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_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_column(obj, 4, 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); } @@ -139,7 +139,7 @@ lv_obj_t* lvgl_sliderbox_create(lv_obj_t* parent, int32_t min, int32_t max, int3 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); auto* sliderBox = reinterpret_cast(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 // a flat per-character estimate instead of trusting raw glyph width too tightly. char buffer[16]; + lv_text_attributes_t text_attributes; + lv_text_attributes_init(&text_attributes); 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); - 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; lv_obj_set_width(sliderBox->valueLabel, max_width + 8); diff --git a/Modules/lvgl-module/source/widgets/toolbar.cpp b/Modules/lvgl-module/source/widgets/toolbar.cpp index 99cef708d..b03be279a 100644 --- a/Modules/lvgl-module/source/widgets/toolbar.cpp +++ b/Modules/lvgl-module/source/widgets/toolbar.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -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); - // Minimal 8 pixels total padding for selection/animation (4+4 pixels) - return (uiDensity != LVGL_UI_DENSITY_COMPACT) ? (uint32_t)(toolbar_height * 0.2f) : 8; -} - -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; + auto padding = (uiDensity != LVGL_UI_DENSITY_COMPACT) ? (uint32_t)(toolbar_height * 0.2f) : 8; + return toolbar_height - padding; } typedef struct { @@ -88,7 +46,8 @@ typedef struct { 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, .constructor_cb = &toolbar_constructor, .destructor_cb = nullptr, @@ -96,7 +55,7 @@ static lv_obj_class_t toolbar_class = { .user_data = nullptr, .name = nullptr, .width_def = LV_PCT(100), - .height_def = 0, + .height_def = LV_SIZE_CONTENT, .editable = false, .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, .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); } +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) { auto ui_density = lvgl_get_ui_density(); - auto toolbar_height = getToolbarHeight(ui_density); - toolbar_class.height_def = toolbar_height; - lv_obj_t* obj = lv_obj_class_create_obj(&toolbar_class, parent); + lv_obj_t* obj = lv_obj_class_create_obj(&lvgl_toolbar_class, parent); 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(obj); 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_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); - - 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 = create_action_button(obj, ui_density); toolbar->close_button_image = lv_image_create(toolbar->close_button); lv_obj_align(toolbar->close_button_image, LV_ALIGN_CENTER, 0, 0); - auto* title_wrapper = lv_obj_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); + toolbar->title_label = lv_label_create(obj); 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_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_align(toolbar->title_label, LV_ALIGN_LEFT_MID, 0, 0); - lv_obj_set_width(toolbar->title_label, LV_PCT(100)); + lv_obj_set_flex_grow(toolbar->title_label, 1); 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_style_pad_all(toolbar->action_container, 0, LV_STATE_DEFAULT); - 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); + 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); @@ -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"); toolbar->action_count++; - auto ui_density = 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_t* action_button = create_action_button(toolbar->action_container, lvgl_get_ui_density()); lv_obj_add_event_cb(action_button, callback, LV_EVENT_SHORT_CLICKED, user_data); lv_obj_t* button_content; 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"); toolbar->action_count++; - auto ui_density = lvgl_get_ui_density(); - 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; + return lv_switch_create(toolbar->action_container); } 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"); toolbar->action_count++; - auto ui_density = lvgl_get_ui_density(); - 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_obj_t* widget = lv_dropdown_create(toolbar->action_container); lv_dropdown_set_options(widget, options); lv_dropdown_set_selected_highlight(widget, false); 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) { lv_dropdown_set_text(widget, text); } - lv_obj_set_align(widget, LV_ALIGN_CENTER); 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"); toolbar->action_count++; - auto ui_density = lvgl_get_ui_density(); - auto* wrapper = create_action_wrapper(toolbar->action_container, ui_density); - - auto* spinner = lvgl_spinner_create(wrapper); - lv_obj_set_align(spinner, LV_ALIGN_CENTER); + auto* spinner = lvgl_spinner_create(toolbar->action_container); 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); diff --git a/Modules/lvgl-module/themes/material.lss b/Modules/lvgl-module/themes/material.lss new file mode 100644 index 000000000..c21f2bc92 --- /dev/null +++ b/Modules/lvgl-module/themes/material.lss @@ -0,0 +1,1624 @@ +// Material 3 theme for colour displays +// +// Tokens are layered: +// 1. Base tokens: raw values without meaning (tones, scales) +// 2. Semantic tokens: Material 3 roles (colors, shapes, density, states) +// 3. Widget tokens: per widget, built from semantic tokens +// Styles only use widget tokens, or semantic tokens for shared state styles. + +@theme material; + +@config { + isDark: bool = false; + isCompact: bool = false; + animationsEnabled: bool = true; + colorPrimary: color = #00bbff; + colorSecondary: color = #ee81c3; + colorError: color = #B3261E; + fontSmall: font = raw("LV_FONT_DEFAULT"); + fontNormal: font = raw("LV_FONT_DEFAULT"); + fontLarge: font = raw("LV_FONT_DEFAULT"); +} + +// Tactility widgets +@widget toolbar: lvgl_toolbar_class; +@widget icon_button: lvgl_icon_button_class; +@widget icon_button_filled: lvgl_icon_button_filled_class; +@widget icon_button_tonal: lvgl_icon_button_tonal_class; +@widget sliderbox: lvgl_sliderbox_class; +@widget card: lvgl_card_class; + +// ============================================================ +// 1. Base tokens +// ============================================================ + +// Neutral tones (greys by Material 3 tone) +@neutral-4: #0E0E0E; +@neutral-6: #131313; +@neutral-10: #1C1C1C; +@neutral-12: #1F1F1F; +@neutral-17: #2B2B2B; +@neutral-20: #303030; +@neutral-22: #343434; +@neutral-30: #474747; +@neutral-50: #777777; +@neutral-60: #919191; +@neutral-80: #C6C6C6; +@neutral-90: #E2E2E2; +@neutral-92: #E8E8E8; +@neutral-94: #EEEEEE; +@neutral-95: #F0F0F0; +@neutral-96: #F3F3F3; +@neutral-98: #FAFAFA; +@neutral-100: #FFFFFF; + +// How much of the primary color tints the neutral tones +@tint-neutral: 4%; +@tint-neutral-variant: 8%; + +// Shape scale +@corner-none: 0; +@corner-extra-small: 4dp; +@corner-small: 8dp; +@corner-medium: 12dp; +@corner-large: 16dp; +@corner-extra-large: 28dp; +@corner-full: circle; +@corner-compact: 3; + +// Spacing scale (4dp grid) +@space-1: 4dp; +@space-2: 8dp; +@space-3: 12dp; +@space-4: 16dp; +@space-6: 24dp; + +// Opacity scale +@opacity-state-hover: 8%; +@opacity-state-focus: 10%; +@opacity-state-pressed: 10%; +@opacity-disabled-content: 38%; +@opacity-disabled-container: 12%; +@opacity-scrim: 32%; + +// Motion +@duration-short: 100; +@duration-medium: 250; +@duration-long: 500; + +// ============================================================ +// 2. Semantic tokens +// ============================================================ + +// Tonal roles derived from a seed color +@color-primary: if($isDark, mix($colorPrimary, white, 55%), $colorPrimary); +@color-on-primary: if($isDark, mix($colorPrimary, black, 50%), white); +@color-primary-container: if($isDark, mix($colorPrimary, black, 70%), mix($colorPrimary, white, 25%)); +@color-on-primary-container: if($isDark, mix($colorPrimary, white, 20%), mix($colorPrimary, black, 60%)); + +@color-secondary: if($isDark, mix($colorSecondary, white, 55%), $colorSecondary); +@color-on-secondary: if($isDark, mix($colorSecondary, black, 50%), white); +@color-secondary-container: if($isDark, mix($colorSecondary, black, 70%), mix($colorSecondary, white, 25%)); +@color-on-secondary-container: if($isDark, mix($colorSecondary, white, 20%), mix($colorSecondary, black, 60%)); + +@color-error: if($isDark, mix($colorError, white, 55%), $colorError); +@color-on-error: if($isDark, mix($colorError, black, 50%), white); + +// Surfaces: neutral tones tinted with the primary color +@color-surface: mix($colorPrimary, if($isDark, @neutral-6, @neutral-98), @tint-neutral); +@color-on-surface: mix($colorPrimary, if($isDark, @neutral-90, @neutral-10), @tint-neutral); +@color-on-surface-variant: mix($colorPrimary, if($isDark, @neutral-80, @neutral-30), @tint-neutral-variant); +@color-surface-container-lowest: mix($colorPrimary, if($isDark, @neutral-4, @neutral-100), @tint-neutral); +@color-surface-container-low: mix($colorPrimary, if($isDark, @neutral-10, @neutral-96), @tint-neutral); +@color-surface-container: mix($colorPrimary, if($isDark, @neutral-12, @neutral-94), @tint-neutral); +@color-surface-container-high: mix($colorPrimary, if($isDark, @neutral-17, @neutral-92), @tint-neutral); +@color-surface-container-highest: mix($colorPrimary, if($isDark, @neutral-22, @neutral-90), @tint-neutral); +@color-outline: mix($colorPrimary, if($isDark, @neutral-60, @neutral-50), @tint-neutral-variant); +@color-outline-variant: mix($colorPrimary, if($isDark, @neutral-30, @neutral-80), @tint-neutral-variant); +@color-inverse-surface: mix($colorPrimary, if($isDark, @neutral-90, @neutral-20), @tint-neutral); +@color-inverse-on-surface: mix($colorPrimary, if($isDark, @neutral-20, @neutral-95), @tint-neutral); +@color-scrim: black; + +// Shapes +@shape-control: if($isCompact, @corner-compact, @corner-full); +@shape-container: if($isCompact, @corner-compact, @corner-medium); +@shape-field: if($isCompact, @corner-compact, @corner-extra-small); +@shape-key: if($isCompact, @corner-compact, @corner-small); +@shape-dialog: if($isCompact, @corner-compact, @corner-extra-large); +@shape-menu: if($isCompact, @corner-compact, @corner-extra-small); + +// Density +@density-container-pad: if($isCompact, 2, @space-4); +@density-container-gap: if($isCompact, 2, @space-3); +@density-container-border: if($isCompact, 1, 0); +@density-control-pad-hor: if($isCompact, 3, @space-6); +@density-control-pad-ver: if($isCompact, 3, 10dp); +@density-control-gap: if($isCompact, 2, @space-2); +@density-field-pad-hor: if($isCompact, 2, @space-4); +@density-field-pad-ver: if($isCompact, 2, @space-2); +@density-list-pad: if($isCompact, 2, @space-2); +@density-list-item-pad-hor: if($isCompact, 2, @space-4); +@density-list-item-pad-ver: if($isCompact, 2, @space-3); +@density-dialog-pad: if($isCompact, 2, @space-6); +@density-key-gap: if($isCompact, 2, @space-1); +@density-border: 1dp; + +// Focus indicator +@focus-color: @color-secondary; +@focus-width: if($isCompact, 1, 3dp); +@focus-offset: if($isCompact, 1, 2dp); +// Drawn inside the widget, for widgets with little room around them (icon buttons) +@focus-inset-width: if($isCompact, 1, 2dp); +@edit-color: @color-primary; + +// State layers: the state color blended over the container color +@state-pressed-on-primary: mix(@color-on-primary, @color-primary, @opacity-state-pressed); +@state-pressed-on-secondary: mix(@color-on-secondary, @color-secondary, @opacity-state-pressed); +@state-layer-color: @color-on-surface; + +// Motion +@motion-duration-short: if($animationsEnabled, @duration-short, 0); +@motion-duration-medium: if($animationsEnabled, @duration-medium, 0); +@motion-duration-long: if($animationsEnabled, @duration-long, 0); + +// ============================================================ +// 3. Widget tokens +// ============================================================ + +// Screen +@screen-color: @color-surface; +@screen-text-color: @color-on-surface; +@screen-gap: @density-container-gap; + +// Container (lv_obj): Material 3 filled card +@container-color: @color-surface-container-low; +@container-text-color: @color-on-surface; +@container-border-color: @color-outline-variant; +@container-border-width: @density-container-border; +@container-shape: @shape-container; +@container-pad: @density-container-pad; +@container-gap: @density-container-gap; + +// Card (lvgl_card_create): a container one tone above the background, so it stands out +@card-color: @color-surface-container-high; +@card-text-color: @color-on-surface; +@card-border-color: @color-outline-variant; +@card-border-width: @density-container-border; +@card-shape: @shape-container; +@card-pad: @density-container-pad; +@card-gap: @density-container-gap; + +// Scrollbar +@scrollbar-color: @color-outline; +@scrollbar-opacity: 40%; +@scrollbar-scrolled-opacity: 70%; +@scrollbar-width: if($isCompact, 2, @space-1); +@scrollbar-pad: if($isCompact, 1, @space-1); + +// Button: Material 3 filled button +@button-container-color: @color-primary; +@button-label-color: @color-on-primary; +@button-pressed-container-color: @state-pressed-on-primary; +@button-checked-container-color: @color-secondary; +@button-checked-label-color: @color-on-secondary; +@button-checked-pressed-container-color: @state-pressed-on-secondary; +@button-disabled-container-color: @color-on-surface; +@button-disabled-container-opacity: @opacity-disabled-container; +@button-disabled-label-color: @color-on-surface; +@button-disabled-label-opacity: @opacity-disabled-content; +@button-shape: @shape-control; +@button-pad-hor: @density-control-pad-hor; +@button-pad-ver: @density-control-pad-ver; +@button-gap: @density-control-gap; + +// Text button (dialog actions, menu header buttons) +@text-button-label-color: @color-primary; +@text-button-pressed-container-color: @color-primary; +@text-button-pad-hor: if($isCompact, 3, @space-3); + +// Icon button (dialog and window header buttons) +@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 +@icon-button-container-shape: if($isCompact, @corner-compact, @corner-large); +@icon-button-filled-color: @color-primary; +@icon-button-filled-icon-color: @color-on-primary; +@icon-button-filled-pressed-color: @state-pressed-on-primary; +@icon-button-tonal-color: @color-secondary-container; +@icon-button-tonal-icon-color: @color-on-secondary-container; +@icon-button-tonal-pressed-color: mix(@color-on-secondary-container, @color-secondary-container, @opacity-state-pressed); + +// Toolbar: Material 2 top app bar, filled with a dark neutral tone for contrast with the content. +// The tone is the same in light and dark mode. The statusbar uses a darker neutral tone with the same tint. +@app-bar-color: mix($colorPrimary, @neutral-22, @tint-neutral); +@app-bar-content-color: white; +@app-bar-pressed-opacity: 20%; +@app-bar-pad-hor: if($isCompact, 0, @space-1); +@app-bar-gap: if($isCompact, 2, @space-1); +// The focus ring is drawn inside toolbar buttons, as there's little room around them +@app-bar-focus-width: @focus-inset-width; + +// Focus ring +@focus-ring-color: @focus-color; +@focus-ring-width: @focus-width; +@focus-ring-offset: @focus-offset; +@edit-ring-color: @edit-color; + +// Switch +@switch-width: if($isCompact, 25, 52dp); +@switch-height: if($isCompact, 15, 32dp); +@switch-track-color: @color-surface-container-highest; +@switch-track-border-color: @color-outline; +@switch-track-border-width: if($isCompact, 1, 2dp); +@switch-track-selected-color: @color-primary; +@switch-handle-color: @color-outline; +@switch-handle-selected-color: @color-on-primary; +@switch-handle-pad: if($isCompact, -3, -8dp); +@switch-handle-selected-pad: if($isCompact, -2, -4dp); +@switch-handle-pressed-pad: if($isCompact, -1, -2dp); + +// Checkbox +@checkbox-gap: if($isCompact, 3, @space-3); +@checkbox-box-color: @color-surface; +@checkbox-box-opacity: transp; +@checkbox-box-border-color: @color-on-surface-variant; +@checkbox-box-border-width: if($isCompact, 1, 2dp); +@checkbox-box-shape: if($isCompact, 2, 2dp); +@checkbox-box-pad: if($isCompact, 1, 2dp); +@checkbox-box-selected-color: @color-primary; +@checkbox-icon-color: @color-on-primary; + +// Slider, bar (linear progress) +@slider-track-color: @color-secondary-container; +@slider-active-track-color: @color-primary; +@slider-handle-color: @color-primary; +// The track is half of LVGL's default thickness (LV_DPI_DEF / 10). The knob is the track height plus its padding on both +// sides, so the padding includes the removed thickness to keep the knob's size. +@slider-track-height: 8dp; +@slider-handle-pad: if($isCompact, 2, 6dp) + 4dp; +// Sliderbox: in the normal layout, the gap between the slider and its buttons leaves room for the knob, which sticks out at the slider's ends +@sliderbox-gap: if($isCompact, 4, (@slider-track-height / 2) + @slider-handle-pad + @space-2); +@progress-track-color: @color-secondary-container; +@progress-indicator-color: @color-primary; + +// Arc, spinner (circular progress) +@arc-track-color: @color-secondary-container; +@arc-indicator-color: @color-primary; +@arc-width: if($isCompact, 3, @space-1); +@arc-handle-color: @color-primary; +@arc-handle-pad: if($isCompact, 2, @space-1); + +// Text field: Material 3 outlined text field (textarea, spinbox, dropdown) +@field-color: @color-surface; +@field-opacity: transp; +@field-text-color: @color-on-surface; +@field-border-color: @color-outline; +@field-border-width: @density-border; +@field-focused-border-color: @color-primary; +@field-focused-border-width: if($isCompact, 1, 2dp); +@field-shape: @shape-field; +@field-pad-hor: @density-field-pad-hor; +@field-pad-ver: @density-field-pad-ver; +@field-placeholder-color: @color-on-surface-variant; +@field-cursor-color: @color-primary; +@field-cursor-width: if($isCompact, 1, 2dp); +@field-selection-color: @color-primary-container; +@field-selection-text-color: @color-on-primary-container; +@spinbox-cursor-color: @color-primary; +@spinbox-cursor-text-color: @color-on-primary; + +// Menu: Material 3 menu (dropdown list) +@menu-color: @color-surface-container; +@menu-text-color: @color-on-surface; +@menu-shape: @shape-menu; +@menu-pad: if($isCompact, 1, @space-2); +@menu-max-height: dpi-def * 2; +@menu-item-selected-color: @color-secondary-container; +@menu-item-selected-text-color: @color-on-secondary-container; +@menu-item-line-space: if($isCompact, 4, @space-4); + +// Roller +@roller-color: @color-surface-container; +@roller-text-color: @color-on-surface-variant; +@roller-selected-color: @color-secondary-container; +@roller-selected-text-color: @color-on-secondary-container; +@roller-line-space: if($isCompact, 4, @space-4); + +// Button matrix and keyboard: keys are tonal buttons +@keys-color: @color-surface-container; +@keys-pad: @density-key-gap; +@keys-gap: @density-key-gap; +@key-color: @color-surface-container-highest; +@key-text-color: @color-on-surface; +@key-shape: @shape-key; +@key-checked-color: @color-secondary-container; +@key-checked-text-color: @color-on-secondary-container; +@key-selected-color: @color-primary; +@key-selected-text-color: @color-on-primary; + +// List +@list-color: @color-surface; +@list-pad: @density-list-pad; +@list-item-color: @color-surface; +@list-item-opacity: transp; +@list-item-text-color: @color-on-surface; +@list-item-pad-hor: @density-list-item-pad-hor; +@list-item-pad-ver: @density-list-item-pad-ver; +@list-item-gap: if($isCompact, 3, @space-4); +@list-item-focused-color: @color-secondary-container; +@list-item-focused-text-color: @color-on-secondary-container; +@list-divider-color: @color-outline-variant; +@list-divider-width: @density-border; +@list-subheader-text-color: @color-on-surface-variant; + +// Tabs: Material 3 primary tabs +@tabs-divider-color: @color-outline-variant; +@tab-label-color: @color-on-surface-variant; +@tab-selected-label-color: @color-primary; +@tab-indicator-color: @color-primary; +@tab-indicator-width: if($isCompact, 2, 3dp); +@tab-page-pad: @density-container-pad; + +// Dialog (message box) +@dialog-color: @color-surface-container-high; +@dialog-text-color: @color-on-surface-variant; +@dialog-title-color: @color-on-surface; +@dialog-shape: @shape-dialog; +@dialog-pad: @density-dialog-pad; +@dialog-gap: if($isCompact, 2, @space-2); +@dialog-scrim-color: @color-scrim; +@dialog-scrim-opacity: @opacity-scrim; + +// Settings menu (lv_menu) +@settings-color: @color-surface; +@settings-section-color: @color-surface-container-low; +@settings-section-shape: @shape-container; +@settings-item-pad-hor: @density-list-item-pad-hor; +@settings-item-pad-ver: @density-list-item-pad-ver; +@settings-item-gap: @density-container-gap; +@settings-divider-color: @color-outline-variant; +@settings-item-selected-color: @color-secondary-container; +@settings-item-selected-text-color: @color-on-secondary-container; + +// Window +@window-header-color: @color-surface-container; +@window-header-pad: if($isCompact, 2, @space-2); +@window-content-pad: @density-container-pad; + +// Table +@table-color: @color-surface; +@table-cell-text-color: @color-on-surface; +@table-cell-pad: if($isCompact, 2, @space-3); +@table-divider-color: @color-outline-variant; + +// Chart +@chart-color: @color-surface-container-low; +@chart-grid-color: @color-outline-variant; +@chart-series-width: if($isCompact, 1, 2dp); +@chart-point-size: if($isCompact, 3, 6dp); +@chart-indicator-color: @color-primary; + +// Calendar +@calendar-color: @color-surface-container-high; +@calendar-shape: if($isCompact, @corner-compact, @corner-large); +@calendar-pad: if($isCompact, 2, @space-2); +@calendar-day-text-color: @color-on-surface; +@calendar-header-pad: if($isCompact, 2, @space-3); + +// Scale +@scale-color: @color-on-surface-variant; +@scale-width: @density-border; +@scale-tick-length: if($isCompact, 3, 6dp); + +// Divider (lv_line) +@divider-color: @color-outline-variant; +@divider-width: @density-border; + +// LED +@led-glow-width: if($isCompact, 4, 15dp); +@led-glow-spread: if($isCompact, 1, 5dp); + +// ============================================================ +// Transitions +// ============================================================ + +@transition state-enter { + props: bgColor, bgOpa, borderColor, textColor, outlineWidth, outlineOpa; + path: ease-out; + duration: @motion-duration-short; + delay: 0; +} + +@transition state-exit { + props: bgColor, bgOpa, borderColor, textColor, outlineWidth, outlineOpa; + path: ease-out; + duration: @motion-duration-short; + delay: 70; +} + +// ============================================================ +// Styles: shared states +// ============================================================ + +transition_enter { + @if ($animationsEnabled) { + transition: state-enter; + } +} + +transition_exit { + @if ($animationsEnabled) { + transition: state-exit; + } +} + +focus_ring { + outlineColor: @focus-ring-color; + outlineWidth: @focus-ring-width; + outlinePad: @focus-ring-offset; + outlineOpa: cover; +} + +edit_ring { + outlineColor: @edit-ring-color; + outlineWidth: @focus-ring-width; + outlinePad: @focus-ring-offset; + outlineOpa: cover; +} + +focus_ring_inset { + outlineColor: @focus-ring-color; + outlineWidth: @focus-inset-width; + outlinePad: -@focus-inset-width; + outlineOpa: cover; +} + +edit_ring_inset { + outlineColor: @edit-ring-color; + outlineWidth: @focus-inset-width; + outlinePad: -@focus-inset-width; + outlineOpa: cover; +} + +// State layer for transparent containers +state_pressed { + bgColor: @state-layer-color; + bgOpa: @opacity-state-pressed; +} + +disabled { + opa: @opacity-disabled-content; +} + +scrollbar { + bgColor: @scrollbar-color; + bgOpa: @scrollbar-opacity; + radius: circle; + width: @scrollbar-width; + padAll: @scrollbar-pad; +} + +scrollbar_scrolled { + bgOpa: @scrollbar-scrolled-opacity; +} + +// Page-like widgets show the shared app background instead of painting their own +page { + bgOpa: transp; + borderWidth: 0; + radius: 0; + padAll: 0; + padGap: 0; +} + +// ============================================================ +// Styles: widgets +// ============================================================ + +screen { + bgColor: @screen-color; + bgOpa: cover; + textColor: @screen-text-color; + textFont: $fontNormal; + padRow: @screen-gap; + padColumn: @screen-gap; + rotarySensitivity: dpi / 4 * 256; +} + +container { + bgColor: @container-color; + bgOpa: cover; + textColor: @container-text-color; + borderColor: @container-border-color; + borderWidth: @container-border-width; + borderPost: true; + radius: @container-shape; + padAll: @container-pad; + padGap: @container-gap; + lineColor: @divider-color; + lineWidth: @divider-width; +} + +card { + bgColor: @card-color; + bgOpa: cover; + textColor: @card-text-color; + borderColor: @card-border-color; + borderWidth: @card-border-width; + radius: @card-shape; + padAll: @card-pad; + padGap: @card-gap; +} + +button { + bgColor: @button-container-color; + bgOpa: cover; + textColor: @button-label-color; + radius: @button-shape; + padHor: @button-pad-hor; + padVer: @button-pad-ver; + padGap: @button-gap; +} + +button_pressed { + bgColor: @button-pressed-container-color; +} + +button_checked { + bgColor: @button-checked-container-color; + textColor: @button-checked-label-color; +} + +button_checked_pressed { + bgColor: @button-checked-pressed-container-color; +} + +button_disabled { + bgColor: @button-disabled-container-color; + bgOpa: @button-disabled-container-opacity; + textColor: @button-disabled-label-color; + textOpa: @button-disabled-label-opacity; +} + +text_button { + bgOpa: transp; + textColor: @text-button-label-color; + radius: @button-shape; + padHor: @text-button-pad-hor; + padVer: @button-pad-ver; + padGap: @button-gap; +} + +text_button_pressed { + bgColor: @text-button-pressed-container-color; + bgOpa: @opacity-state-pressed; +} + +icon_button { + bgOpa: transp; + textColor: @icon-button-color; + radius: @button-shape; + padAll: @button-pad-ver; +} + +// Material 3 selected (toggled) icon button +icon_button_selected { + textColor: @icon-button-selected-color; +} + +icon_button_filled { + bgColor: @icon-button-filled-color; + bgOpa: cover; + textColor: @icon-button-filled-icon-color; + radius: @icon-button-container-shape; + padAll: @button-pad-ver; +} + +icon_button_filled_pressed { + bgColor: @icon-button-filled-pressed-color; +} + +icon_button_tonal { + bgColor: @icon-button-tonal-color; + bgOpa: cover; + textColor: @icon-button-tonal-icon-color; + radius: @icon-button-container-shape; + padAll: @button-pad-ver; +} + +icon_button_tonal_pressed { + bgColor: @icon-button-tonal-pressed-color; +} + +app_bar { + bgColor: @app-bar-color; + bgOpa: cover; + textColor: @app-bar-content-color; + radius: 0; + borderWidth: 0; + padHor: @app-bar-pad-hor; + padVer: 0; + padGap: @app-bar-gap; +} + +app_bar_actions { + bgOpa: transp; + borderWidth: 0; + radius: 0; + padAll: 0; + padGap: @app-bar-gap; +} + +app_bar_button { + padAll: 0; + textColor: @app-bar-content-color; +} + +app_bar_button_pressed { + bgColor: @app-bar-content-color; + bgOpa: @app-bar-pressed-opacity; +} + +app_bar_focus_ring { + outlineColor: @app-bar-content-color; + outlineWidth: @app-bar-focus-width; + outlinePad: -@app-bar-focus-width; + outlineOpa: cover; +} + +app_bar_field { + textColor: @app-bar-content-color; + borderColor: @app-bar-content-color; +} + +switch_track { + width: @switch-width; + height: @switch-height; + bgColor: @switch-track-color; + bgOpa: cover; + borderColor: @switch-track-border-color; + borderWidth: @switch-track-border-width; + radius: circle; + animDuration: @motion-duration-short; +} + +switch_track_selected { + borderColor: @switch-track-selected-color; +} + +switch_indicator_selected { + bgColor: @switch-track-selected-color; + bgOpa: cover; + radius: circle; +} + +switch_handle { + bgColor: @switch-handle-color; + bgOpa: cover; + radius: circle; + padAll: @switch-handle-pad; +} + +switch_handle_selected { + bgColor: @switch-handle-selected-color; + padAll: @switch-handle-selected-pad; +} + +switch_handle_pressed { + padAll: @switch-handle-pressed-pad; +} + +checkbox { + padGap: @checkbox-gap; + textColor: @color-on-surface; +} + +checkbox_box { + bgColor: @checkbox-box-color; + bgOpa: @checkbox-box-opacity; + borderColor: @checkbox-box-border-color; + borderWidth: @checkbox-box-border-width; + radius: @checkbox-box-shape; + padAll: @checkbox-box-pad; + textFont: $fontSmall; + textColor: @checkbox-icon-color; +} + +checkbox_box_selected { + bgColor: @checkbox-box-selected-color; + bgOpa: cover; + borderColor: @checkbox-box-selected-color; + bgImageSrc: symbol(ok); +} + +sliderbox { + padColumn: @sliderbox-gap; +} + +slider_track_size { + height: @slider-track-height; +} + +// The knob extends beyond the slider by its padding, so the slider reserves that room above and below it in layouts. +// Not left and right: flex grow (e.g. in the sliderbox) doesn't take a growing item's own margins into account. +slider_knob_room { + marginVer: @slider-handle-pad; +} + +slider_track { + bgColor: @slider-track-color; + bgOpa: cover; + radius: circle; +} + +slider_active_track { + bgColor: @slider-active-track-color; + bgOpa: cover; + radius: circle; +} + +slider_handle { + bgColor: @slider-handle-color; + bgOpa: cover; + radius: circle; + padAll: @slider-handle-pad; +} + +progress_track { + bgColor: @progress-track-color; + bgOpa: cover; + radius: circle; +} + +progress_indicator { + bgColor: @progress-indicator-color; + bgOpa: cover; + radius: circle; +} + +arc_track { + arcColor: @arc-track-color; + arcWidth: @arc-width; + arcRounded: true; +} + +arc_indicator { + arcColor: @arc-indicator-color; + arcWidth: @arc-width; + arcRounded: true; +} + +arc_handle { + bgColor: @arc-handle-color; + bgOpa: cover; + radius: circle; + padAll: @arc-handle-pad; +} + +field { + bgColor: @field-color; + bgOpa: @field-opacity; + textColor: @field-text-color; + borderColor: @field-border-color; + borderWidth: @field-border-width; + radius: @field-shape; + padHor: @field-pad-hor; + padVer: @field-pad-ver; + padGap: @field-pad-ver; +} + +field_focused { + borderColor: @field-focused-border-color; + borderWidth: @field-focused-border-width; +} + +field_placeholder { + textColor: @field-placeholder-color; +} + +field_cursor { + borderColor: @field-cursor-color; + borderWidth: @field-cursor-width; + borderSide: left; + padLeft: -1; + animDuration: @motion-duration-long; +} + +field_selection { + bgColor: @field-selection-color; + textColor: @field-selection-text-color; +} + +spinbox_cursor { + bgColor: @spinbox-cursor-color; + bgOpa: cover; + textColor: @spinbox-cursor-text-color; +} + +menu { + bgColor: @menu-color; + bgOpa: cover; + textColor: @menu-text-color; + radius: @menu-shape; + padAll: @menu-pad; + textLineSpace: @menu-item-line-space; + maxHeight: @menu-max-height; + clipCorner: true; +} + +menu_item { + bgOpa: transp; +} + +menu_item_selected { + bgColor: @menu-item-selected-color; + bgOpa: cover; + textColor: @menu-item-selected-text-color; +} + +roller { + bgColor: @roller-color; + bgOpa: cover; + textColor: @roller-text-color; + radius: @shape-container; + textLineSpace: @roller-line-space; + textAlign: center; + animDuration: @motion-duration-medium; +} + +roller_selected { + bgColor: @roller-selected-color; + bgOpa: cover; + textColor: @roller-selected-text-color; +} + +keys { + bgColor: @keys-color; + bgOpa: cover; + radius: @shape-container; + padAll: @keys-pad; + padGap: @keys-gap; +} + +key { + bgColor: @key-color; + bgOpa: cover; + textColor: @key-text-color; + radius: @key-shape; +} + +key_pressed { + bgColor: mix(@state-layer-color, @key-color, @opacity-state-pressed); +} + +key_checked { + bgColor: @key-checked-color; + textColor: @key-checked-text-color; +} + +key_selected { + bgColor: @key-selected-color; + textColor: @key-selected-text-color; +} + +list { + bgColor: @list-color; + bgOpa: cover; + padHor: 0; + padVer: @list-pad; + padGap: 0; + clipCorner: true; +} + +list_item { + bgColor: @list-item-color; + bgOpa: @list-item-opacity; + textColor: @list-item-text-color; + padHor: @list-item-pad-hor; + padVer: @list-item-pad-ver; + padColumn: @list-item-gap; + borderColor: @list-divider-color; + borderWidth: @list-divider-width; + borderSide: bottom; +} + +list_item_focused { + bgColor: @list-item-focused-color; + bgOpa: cover; + textColor: @list-item-focused-text-color; +} + +list_subheader { + textColor: @list-subheader-text-color; + textFont: $fontSmall; + padHor: @list-item-pad-hor; + padTop: @list-item-pad-ver; + padBottom: @list-pad; +} + +tabs { + bgOpa: transp; + borderColor: @tabs-divider-color; + borderWidth: @density-border; + borderSide: bottom; +} + +tab { + bgOpa: transp; + textColor: @tab-label-color; + radius: 0; +} + +tab_selected { + textColor: @tab-selected-label-color; + borderColor: @tab-indicator-color; + borderWidth: @tab-indicator-width; + borderSide: bottom; +} + +tab_page { + padAll: @tab-page-pad; +} + +dialog { + bgColor: @dialog-color; + bgOpa: cover; + textColor: @dialog-text-color; + radius: @dialog-shape; + padAll: 0; + padGap: 0; + clipCorner: true; +} + +dialog_header { + padHor: @dialog-pad; + padTop: @dialog-pad; + padBottom: @dialog-gap; + padGap: @dialog-gap; +} + +dialog_title { + textColor: @dialog-title-color; + textFont: $fontLarge; +} + +dialog_content { + padHor: @dialog-pad; + padVer: @dialog-gap; +} + +dialog_actions { + padAll: @dialog-pad; + padTop: @dialog-gap; + padGap: @dialog-gap; +} + +dialog_scrim { + bgColor: @dialog-scrim-color; + bgOpa: @dialog-scrim-opacity; +} + +settings { + bgColor: @settings-color; + bgOpa: cover; + padAll: 0; + padGap: 0; + radius: 0; + clipCorner: true; +} + +settings_sidebar { + padAll: 0; + padGap: 0; + borderColor: @settings-divider-color; + borderWidth: @density-border; + borderSide: right; +} + +settings_content { + padAll: 0; + padGap: 0; +} + +settings_page { + padHor: 0; + padGap: 0; +} + +settings_header { + padHor: @settings-item-pad-hor; + padVer: @density-list-pad; + padGap: @settings-item-gap; +} + +settings_section { + bgColor: @settings-section-color; + bgOpa: cover; + radius: @settings-section-shape; + clipCorner: true; +} + +settings_item { + padHor: @settings-item-pad-hor; + padVer: @settings-item-pad-ver; + padGap: @settings-item-gap; +} + +settings_item_selected { + bgColor: @settings-item-selected-color; + bgOpa: cover; + textColor: @settings-item-selected-text-color; +} + +settings_separator { + bgOpa: transp; + padVer: @density-list-pad; +} + +window_header { + bgColor: @window-header-color; + bgOpa: cover; + padAll: @window-header-pad; + padGap: @window-header-pad; +} + +window_title { + textFont: $fontLarge; +} + +window_content { + bgOpa: transp; + padAll: @window-content-pad; + padGap: @density-container-gap; +} + +table { + bgColor: @table-color; + bgOpa: cover; + padAll: 0; + radius: 0; + borderColor: @table-divider-color; + borderWidth: @density-border; +} + +table_cell { + bgOpa: transp; + textColor: @table-cell-text-color; + padAll: @table-cell-pad; + borderColor: @table-divider-color; + borderWidth: @density-border; + borderSide: bottom; +} + +chart { + bgColor: @chart-color; + bgOpa: cover; + radius: @shape-container; + padAll: @density-container-pad; + lineColor: @chart-grid-color; + borderPost: false; +} + +chart_series { + lineWidth: @chart-series-width; + size: @chart-point-size, @chart-point-size; + radius: @chart-point-size; +} + +chart_indicator { + bgColor: @chart-indicator-color; + bgOpa: cover; + radius: circle; + size: @chart-point-size, @chart-point-size; +} + +calendar { + bgColor: @calendar-color; + bgOpa: cover; + radius: @calendar-shape; + padAll: @calendar-pad; + padGap: @calendar-pad; +} + +calendar_days { + bgOpa: transp; + padAll: 0; + padGap: @density-key-gap; +} + +calendar_day { + bgOpa: transp; + textColor: @calendar-day-text-color; + radius: circle; +} + +calendar_header { + padAll: @calendar-header-pad; + padGap: @calendar-header-pad; +} + +scale { + lineColor: @scale-color; + lineWidth: @scale-width; + arcColor: @scale-color; + arcWidth: @scale-width; + textColor: @scale-color; + length: @scale-tick-length; +} + +divider { + lineColor: @divider-color; + lineWidth: @divider-width; +} + +led { + bgColor: white; + bgOpa: cover; + bgGradColor: palette(grey); + radius: circle; + shadowWidth: @led-glow-width; + shadowColor: white; + shadowSpread: @led-glow-spread; +} + +// ============================================================ +// Apply rules +// ============================================================ + +apply(null > *) { + screen: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +// Base object + +apply(tabview > obj[1]) { + // Tab content area: no styles +} + +apply(tabview > obj[0]) { + tabs: default; +} + +apply(tabview > * > obj) { + tab_page: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +apply(win > obj[0]) { + window_header: default; +} + +apply(win > obj[1]) { + window_content: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +apply(calendar > obj) { + // No styles +} + +apply(toolbar > obj) { + app_bar_actions: default; +} + +apply(obj) { + container: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +// Buttons + +apply(tabview > *[0] > button) { + tab: default; + transition_exit: default; + state_pressed: state.pressed; + transition_enter: state.pressed; + tab_selected: state.checked; + focus_ring: state.focusKey; + edit_ring: state.edited; +} + +apply(menu_sidebar_header_cont > button, menu_main_header_cont > button, win > obj[0] > button) { + icon_button: default; + transition_exit: default; + state_pressed: state.pressed; + transition_enter: state.pressed; + focus_ring: state.focusKey; + disabled: state.disabled; +} + +apply(button) { + button: default; + transition_exit: default; + button_pressed: state.pressed; + transition_enter: state.pressed; + button_checked: state.checked; + button_checked_pressed: state.checked, state.pressed; + focus_ring: state.focusKey; + edit_ring: state.edited; + button_disabled: state.disabled; +} + +apply(toolbar > icon_button, toolbar > obj > icon_button) { + icon_button: default; + app_bar_button: default; + transition_exit: default; + app_bar_button_pressed: state.pressed; + transition_enter: state.pressed; + app_bar_focus_ring: state.focusKey; + app_bar_focus_ring: state.edited; + disabled: state.disabled; +} + +apply(toolbar > obj > dropdown) { + @include dropdown; + app_bar_field: default; + app_bar_focus_ring: state.focusKey; +} + +apply(toolbar) { + app_bar: default; +} + +apply(icon_button) { + icon_button: default; + icon_button_selected: state.checked; + transition_exit: default; + state_pressed: state.pressed; + transition_enter: state.pressed; + focus_ring_inset: state.focusKey; + edit_ring_inset: state.edited; + disabled: state.disabled; +} + +apply(icon_button_filled) { + icon_button_filled: default; + transition_exit: default; + icon_button_filled_pressed: state.pressed; + transition_enter: state.pressed; + focus_ring: state.focusKey; + edit_ring: state.edited; + button_disabled: state.disabled; +} + +apply(icon_button_tonal) { + icon_button_tonal: default; + transition_exit: default; + icon_button_tonal_pressed: state.pressed; + transition_enter: state.pressed; + focus_ring: state.focusKey; + edit_ring: state.edited; + button_disabled: state.disabled; +} + +apply(msgbox_header_button) { + icon_button: default; + transition_exit: default; + state_pressed: state.pressed; + transition_enter: state.pressed; + focus_ring: state.focusKey; + disabled: state.disabled; +} + +apply(msgbox_footer_button) { + text_button: default; + transition_exit: default; + text_button_pressed: state.pressed; + transition_enter: state.pressed; + focus_ring: state.focusKey; + disabled: state.disabled; +} + +// Labels + +apply(msgbox_header > label) { + dialog_title: default; +} + +apply(win > obj[0] > label) { + window_title: default; +} + +apply(textarea > label) { + field_selection: part.selected; +} + +// Selection controls + +apply(switch) { + switch_track: default; + switch_track_selected: state.checked; + switch_indicator_selected: part.indicator, state.checked; + switch_handle: part.knob; + switch_handle_selected: part.knob, state.checked; + switch_handle_pressed: part.knob, state.pressed; + focus_ring: state.focusKey; + edit_ring: state.edited; + disabled: state.disabled; +} + +apply(checkbox) { + checkbox: default; + focus_ring: state.focusKey; + checkbox_box: part.indicator; + checkbox_box_selected: part.indicator, state.checked; + transition_exit: part.indicator; + transition_enter: part.indicator, state.pressed; + disabled: state.disabled; +} + +apply(card) { + card: default; + scrollbar: part.scrollbar; +} + +apply(sliderbox) { + sliderbox: default; +} + +apply(slider) { + slider_track: default; + slider_track_size: default; + slider_knob_room: default; + focus_ring: state.focusKey; + edit_ring: state.edited; + slider_active_track: part.indicator; + slider_handle: part.knob; + disabled: state.disabled; +} + +apply(bar) { + progress_track: default; + progress_indicator: part.indicator; + focus_ring: state.focusKey; +} + +apply(arc) { + arc_track: default; + arc_indicator: part.indicator; + arc_handle: part.knob; + focus_ring: state.focusKey; + edit_ring: state.edited; +} + +apply(spinner) { + arc_track: default; + arc_indicator: part.indicator; +} + +// Text input + +apply(textarea) { + field: default; + field_focused: state.focused; + focus_ring: state.focusKey; + edit_ring: state.edited; + disabled: state.disabled; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; + field_cursor: part.cursor, state.focused; + field_placeholder: part.textareaPlaceholder; +} + +apply(spinbox) { + field: default; + field_focused: state.focused; + focus_ring: state.focusKey; + edit_ring: state.edited; + spinbox_cursor: part.cursor; +} + +apply(dropdown) { + field: default; + transition_exit: default; + state_pressed: state.pressed; + transition_enter: state.pressed; + focus_ring: state.focusKey; + edit_ring: state.edited; + disabled: state.disabled; +} + +apply(dropdownlist) { + menu: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; + menu_item: part.selected; + menu_item_selected: part.selected, state.checked; + state_pressed: part.selected, state.pressed; +} + +apply(roller) { + roller: default; + roller_selected: part.selected; + focus_ring: state.focusKey; + edit_ring: state.edited; +} + +// Button matrix and keyboard + +apply(calendar > buttonmatrix) { + calendar_days: default; + calendar_day: part.items; + state_pressed: part.items, state.pressed; + key_selected: part.items, state.checked; + focus_ring: part.items, state.focusKey; + edit_ring: part.items, state.edited; + disabled: part.items, state.disabled; +} + +apply(buttonmatrix) { + keys: default; + focus_ring: state.focusKey; + edit_ring: state.edited; + key: part.items; + key_pressed: part.items, state.pressed; + key_selected: part.items, state.checked; + focus_ring: part.items, state.focusKey; + disabled: part.items, state.disabled; +} + +apply(keyboard) { + keys: default; + focus_ring: state.focusKey; + edit_ring: state.edited; + key: part.items; + key_pressed: part.items, state.pressed; + key_checked: part.items, state.checked; + key_selected: part.items, state.focusKey; + disabled: part.items, state.disabled; +} + +// Lists + +apply(list) { + list: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +apply(list_text) { + list_subheader: default; +} + +apply(list_button) { + list_item: default; + transition_exit: default; + state_pressed: state.pressed; + transition_enter: state.pressed; + list_item_focused: state.focusKey; + disabled: state.disabled; +} + +apply(table) { + table: default; + focus_ring: state.focusKey; + edit_ring: state.edited; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; + table_cell: part.items; + state_pressed: part.items, state.pressed; + list_item_focused: part.items, state.focusKey; +} + +// Settings menu + +apply(menu) { + settings: default; +} + +apply(menu_sidebar_cont) { + settings_sidebar: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +apply(menu_main_cont) { + settings_content: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +apply(menu_page) { + settings_page: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +apply(menu_sidebar_header_cont, menu_main_header_cont) { + settings_header: default; +} + +apply(menu_section) { + settings_section: default; +} + +apply(menu_cont) { + settings_item: default; + state_pressed: state.pressed; + settings_item_selected: state.checked; + settings_item_selected: state.focusKey; +} + +apply(menu_separator) { + settings_separator: default; +} + +// Dialog + +apply(msgbox) { + dialog: default; +} + +apply(msgbox_backdrop) { + dialog_scrim: default; +} + +apply(msgbox_header) { + dialog_header: default; +} + +apply(msgbox_content) { + dialog_content: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +apply(msgbox_footer) { + dialog_actions: default; +} + +// Containers + +apply(tabview, win) { + page: default; +} + +apply(tileview) { + page: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +apply(tileview_tile) { + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; +} + +// Data display + +apply(chart) { + chart: default; + scrollbar: part.scrollbar; + scrollbar_scrolled: part.scrollbar, state.scrolled; + chart_series: part.items; + chart_indicator: part.indicator; + chart_series: part.cursor; +} + +apply(calendar) { + calendar: default; +} + +apply(calendar_header_arrow, calendar_header_dropdown) { + calendar_header: default; +} + +apply(scale) { + scale: part.main; + scale: part.indicator; + scale: part.items; +} + +apply(line) { + divider: default; +} + +apply(led) { + led: default; +} diff --git a/Modules/lvgl-module/themes/material_mono.lss b/Modules/lvgl-module/themes/material_mono.lss new file mode 100644 index 000000000..a8688cac0 --- /dev/null +++ b/Modules/lvgl-module/themes/material_mono.lss @@ -0,0 +1,1264 @@ +// Material 3 theme for monochrome and greyscale displays (OLED, e-paper) +// +// Tokens are layered: +// 1. Base tokens: raw values without meaning +// 2. Semantic tokens: Material 3 roles, mapped onto ink and paper +// 3. Widget tokens: per widget, built from semantic tokens +// Styles only use widget tokens, or semantic tokens for shared state styles. +// +// A 1-bit display can't show opacity or tints, so states are shown with inversion and borders. + +@theme material_mono; + +@config { + isDark: bool = false; + isCompact: bool = false; + animationsEnabled: bool = true; + colorPrimary: color = black; + colorSecondary: color = black; + fontSmall: font = raw("LV_FONT_DEFAULT"); + fontNormal: font = raw("LV_FONT_DEFAULT"); + fontLarge: font = raw("LV_FONT_DEFAULT"); +} + +// Tactility widgets +@widget toolbar: lvgl_toolbar_class; +@widget icon_button: lvgl_icon_button_class; +@widget icon_button_filled: lvgl_icon_button_filled_class; +@widget icon_button_tonal: lvgl_icon_button_tonal_class; +@widget sliderbox: lvgl_sliderbox_class; +@widget card: lvgl_card_class; + +// ============================================================ +// 1. Base tokens +// ============================================================ + +@black: black; +@white: white; + +// Shape scale +@corner-none: 0; +@corner-small: 4dp; +@corner-medium: 8dp; +@corner-large: 12dp; +@corner-full: circle; +@corner-compact: 2; + +// Spacing scale +@space-1: 2dp; +@space-2: 4dp; +@space-3: 6dp; +@space-4: 8dp; +@space-6: 12dp; + +// Line widths +@line-thin: 1; +@line-medium: 2; +@line-thick: 3; + +// Motion +@duration-short: 100; +@duration-medium: 250; +@duration-long: 500; + +// ============================================================ +// 2. Semantic tokens +// ============================================================ + +// Ink is the foreground, paper the background +@ink: if($isDark, @white, @black); +@paper: if($isDark, @black, @white); + +@color-primary: @ink; +@color-on-primary: @paper; +@color-surface: @paper; +@color-on-surface: @ink; +@color-outline: @ink; + +// Shapes +@shape-control: if($isCompact, @corner-compact, @corner-full); +@shape-container: if($isCompact, @corner-compact, @corner-medium); +@shape-field: if($isCompact, @corner-compact, @corner-small); +@shape-key: if($isCompact, @corner-compact, @corner-small); +@shape-dialog: if($isCompact, @corner-compact, @corner-large); + +// Density +@density-container-pad: if($isCompact, 2, @space-4); +@density-container-gap: if($isCompact, 2, @space-3); +@density-control-pad-hor: if($isCompact, 3, @space-6); +@density-control-pad-ver: if($isCompact, 2, @space-3); +@density-control-gap: if($isCompact, 2, @space-2); +@density-field-pad: if($isCompact, 2, @space-3); +@density-list-pad: if($isCompact, 1, @space-2); +@density-list-item-pad-hor: if($isCompact, 2, @space-4); +@density-list-item-pad-ver: if($isCompact, 2, @space-3); +@density-dialog-pad: if($isCompact, 2, @space-6); +@density-key-gap: if($isCompact, 1, @space-1); + +// Borders and indicators +@border: @line-thin; +@focus-width: if($isCompact, @line-thin, @line-medium); +@focus-offset: if($isCompact, @line-thin, @line-medium); +@edit-width: if($isCompact, @line-medium, @line-thick); +// Drawn inside the widget, for widgets with little room around them (icon buttons) +@focus-inset-width: if($isCompact, @line-thin, @line-medium); + +// Motion +@motion-duration-short: if($animationsEnabled, @duration-short, 0); +@motion-duration-medium: if($animationsEnabled, @duration-medium, 0); +@motion-duration-long: if($animationsEnabled, @duration-long, 0); + +// ============================================================ +// 3. Widget tokens +// ============================================================ + +// Screen +@screen-color: @color-surface; +@screen-text-color: @color-on-surface; +@screen-gap: @density-container-gap; + +// Container (lv_obj): Material 3 outlined card +@container-color: @color-surface; +@container-text-color: @color-on-surface; +@container-border-color: @color-outline; +@container-border-width: @border; +@container-shape: @shape-container; +@container-pad: @density-container-pad; +@container-gap: @density-container-gap; + +// Card (lvgl_card_create): containers are already outlined, so a card has a thicker outline to stand out +@card-color: @color-surface; +@card-text-color: @color-on-surface; +@card-border-color: @color-outline; +@card-border-width: @line-medium; +@card-shape: @shape-container; +@card-pad: @density-container-pad; +@card-gap: @density-container-gap; + +// Scrollbar +@scrollbar-color: @color-on-surface; +@scrollbar-width: if($isCompact, 2, 3dp); +@scrollbar-pad: 1; + +// Button: Material 3 outlined button, inverted when pressed or checked +@button-container-color: @color-surface; +@button-label-color: @color-on-surface; +@button-border-color: @color-outline; +@button-border-width: @border; +@button-inverted-container-color: @color-primary; +@button-inverted-label-color: @color-on-primary; +@button-shape: @shape-control; +@button-pad-hor: @density-control-pad-hor; +@button-pad-ver: @density-control-pad-ver; +@button-gap: @density-control-gap; + +// Filled and tonal icon buttons: filled is ink, tonal is outlined +@icon-button-container-shape: if($isCompact, @corner-compact, @corner-large); +@icon-button-filled-color: @color-primary; +@icon-button-filled-icon-color: @color-on-primary; +@icon-button-tonal-color: @color-surface; +@icon-button-tonal-icon-color: @color-on-surface; +@icon-button-tonal-border-color: @color-outline; + +// Toolbar: top app bar with a divider, which shows the background like the statusbar does +@app-bar-title-color: @color-on-surface; +@app-bar-divider-color: @color-outline; +@app-bar-pad-hor: if($isCompact, 0, @space-1); +@app-bar-gap: if($isCompact, 2, @space-1); +// The focus ring is drawn inside toolbar buttons, as there's little room around them +@app-bar-focus-width: @focus-inset-width; + +// Focus ring +@focus-ring-color: @color-on-surface; +@focus-ring-width: @focus-width; +@focus-ring-offset: @focus-offset; +@edit-ring-width: @edit-width; + +// Switch +@switch-width: if($isCompact, 25, 40dp); +@switch-height: if($isCompact, 15, 24dp); +@switch-track-color: @color-surface; +@switch-track-border-color: @color-outline; +@switch-track-border-width: @border; +@switch-track-selected-color: @color-primary; +@switch-handle-color: @color-on-surface; +@switch-handle-selected-color: @color-on-primary; +@switch-handle-pad: if($isCompact, -3, -5dp); +@switch-handle-selected-pad: if($isCompact, -2, -3dp); + +// Checkbox +@checkbox-gap: if($isCompact, 3, @space-4); +@checkbox-box-color: @color-surface; +@checkbox-box-border-color: @color-outline; +@checkbox-box-border-width: @border; +@checkbox-box-shape: 2; +@checkbox-box-pad: 1; +@checkbox-box-selected-color: @color-primary; +@checkbox-icon-color: @color-on-primary; + +// Slider, bar, arc, spinner +@slider-track-color: @color-surface; +@slider-track-border-color: @color-outline; +@slider-active-track-color: @color-primary; +@slider-handle-color: @color-primary; +// The track is half of LVGL's default thickness (LV_DPI_DEF / 10). The knob is the track height plus its padding on both +// sides, so the padding includes the removed thickness to keep the knob's size. +@slider-track-height: 8dp; +@slider-handle-pad: if($isCompact, 1, 3dp) + 4dp; +// Sliderbox: in the normal layout, the gap between the slider and its buttons leaves room for the knob, which sticks out at the slider's ends +@sliderbox-gap: if($isCompact, 4, (@slider-track-height / 2) + @slider-handle-pad + @space-2); +@arc-track-color: @color-on-surface; +@arc-track-width: @line-thin; +@arc-indicator-color: @color-primary; +@arc-indicator-width: if($isCompact, @line-medium, 4dp); +@arc-handle-color: @color-primary; +@arc-handle-pad: if($isCompact, 1, 3dp); + +// Text field (textarea, spinbox, dropdown) +@field-color: @color-surface; +@field-text-color: @color-on-surface; +@field-border-color: @color-outline; +@field-border-width: @border; +@field-focused-border-width: @line-medium; +@field-shape: @shape-field; +@field-pad: @density-field-pad; +@field-cursor-color: @color-on-surface; +@field-cursor-width: @line-medium; +@field-selection-color: @color-primary; +@field-selection-text-color: @color-on-primary; + +// Menu (dropdown list), roller +@menu-color: @color-surface; +@menu-border-color: @color-outline; +@menu-shape: @shape-field; +@menu-max-height: dpi-def * 2; +@menu-item-line-space: if($isCompact, 2, @space-3); +@roller-line-space: if($isCompact, 2, @space-3); + +// Button matrix and keyboard +@keys-pad: @density-key-gap; +@keys-gap: @density-key-gap; +@key-color: @color-surface; +@key-text-color: @color-on-surface; +@key-border-color: @color-outline; +@key-shape: @shape-key; + +// List +@list-pad: @density-list-pad; +@list-item-pad-hor: @density-list-item-pad-hor; +@list-item-pad-ver: @density-list-item-pad-ver; +@list-item-gap: if($isCompact, 3, @space-4); +@list-divider-color: @color-outline; + +// Tabs +@tabs-divider-color: @color-outline; +@tab-indicator-color: @color-primary; +@tab-indicator-width: if($isCompact, @line-medium, @line-thick); +@tab-page-pad: @density-container-pad; + +// Dialog +@dialog-color: @color-surface; +@dialog-border-color: @color-outline; +@dialog-border-width: @line-medium; +@dialog-shape: @shape-dialog; +@dialog-pad: @density-dialog-pad; +@dialog-gap: if($isCompact, 2, @space-2); + +// Window +@window-header-border-color: @color-outline; +@window-header-pad: if($isCompact, 2, @space-2); +@window-content-pad: @density-container-pad; + +// Table, chart, calendar, scale, divider +@table-cell-pad: if($isCompact, 2, @space-3); +@table-divider-color: @color-outline; +@chart-grid-color: @color-on-surface; +@chart-point-size: if($isCompact, 3, 6dp); +@calendar-pad: if($isCompact, 2, @space-2); +@scale-color: @color-on-surface; +@scale-tick-length: if($isCompact, 3, 6dp); +@divider-color: @color-outline; + +// ============================================================ +// Styles: shared states +// ============================================================ + +focus_ring { + outlineColor: @focus-ring-color; + outlineWidth: @focus-ring-width; + outlinePad: @focus-ring-offset; + outlineOpa: cover; +} + +edit_ring { + outlineColor: @focus-ring-color; + outlineWidth: @edit-ring-width; + outlinePad: @focus-ring-offset; + outlineOpa: cover; +} + +focus_ring_inset { + outlineColor: @focus-ring-color; + outlineWidth: @focus-inset-width; + outlinePad: -@focus-inset-width; + outlineOpa: cover; +} + +edit_ring_inset { + outlineColor: @focus-ring-color; + outlineWidth: @edit-ring-width; + outlinePad: -@edit-ring-width; + outlineOpa: cover; +} + +inverted { + bgColor: @button-inverted-container-color; + bgOpa: cover; + textColor: @button-inverted-label-color; + borderColor: @button-inverted-label-color; + lineColor: @button-inverted-label-color; + arcColor: @button-inverted-label-color; + outlineColor: @button-inverted-label-color; +} + +disabled { + borderWidth: 0; +} + +scrollbar { + bgColor: @scrollbar-color; + bgOpa: cover; + radius: circle; + width: @scrollbar-width; + padAll: @scrollbar-pad; +} + +// Page-like widgets show the shared app background instead of painting their own +page { + bgOpa: transp; + borderWidth: 0; + radius: 0; + padAll: 0; + padGap: 0; +} + +// ============================================================ +// Styles: widgets +// ============================================================ + +screen { + bgColor: @screen-color; + bgOpa: cover; + textColor: @screen-text-color; + textFont: $fontNormal; + padRow: @screen-gap; + padColumn: @screen-gap; +} + +container { + bgColor: @container-color; + bgOpa: cover; + textColor: @container-text-color; + borderColor: @container-border-color; + borderWidth: @container-border-width; + radius: @container-shape; + padAll: @container-pad; + padGap: @container-gap; + lineColor: @divider-color; + lineWidth: @border; + arcColor: @color-on-surface; + arcWidth: @line-medium; +} + +card { + bgColor: @card-color; + bgOpa: cover; + textColor: @card-text-color; + borderColor: @card-border-color; + borderWidth: @card-border-width; + radius: @card-shape; + padAll: @card-pad; + padGap: @card-gap; +} + +button { + bgColor: @button-container-color; + bgOpa: cover; + textColor: @button-label-color; + borderColor: @button-border-color; + borderWidth: @button-border-width; + radius: @button-shape; + padHor: @button-pad-hor; + padVer: @button-pad-ver; + padGap: @button-gap; +} + +icon_button { + bgOpa: transp; + textColor: @color-on-surface; + borderWidth: 0; + radius: @button-shape; + 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 { + bgColor: @icon-button-filled-color; + bgOpa: cover; + textColor: @icon-button-filled-icon-color; + borderWidth: 0; + radius: @icon-button-container-shape; + padAll: @button-pad-ver; +} + +// The filled button is already ink, so pressing it shows the outlined look +icon_button_filled_pressed { + bgColor: @icon-button-tonal-color; + textColor: @icon-button-tonal-icon-color; + borderColor: @icon-button-tonal-border-color; + borderWidth: @border; +} + +icon_button_tonal { + bgColor: @icon-button-tonal-color; + bgOpa: cover; + textColor: @icon-button-tonal-icon-color; + borderColor: @icon-button-tonal-border-color; + borderWidth: @border; + radius: @icon-button-container-shape; + padAll: @button-pad-ver; +} + +app_bar { + bgOpa: transp; + textColor: @app-bar-title-color; + radius: 0; + borderColor: @app-bar-divider-color; + borderWidth: @border; + borderSide: bottom; + padHor: @app-bar-pad-hor; + padVer: 0; + padGap: @app-bar-gap; +} + +app_bar_actions { + bgOpa: transp; + borderWidth: 0; + radius: 0; + padAll: 0; + padGap: @app-bar-gap; +} + +app_bar_button { + padAll: 0; +} + +app_bar_focus_ring { + outlineColor: @focus-ring-color; + outlineWidth: @app-bar-focus-width; + outlinePad: -@app-bar-focus-width; + outlineOpa: cover; +} + +switch_track { + width: @switch-width; + height: @switch-height; + bgColor: @switch-track-color; + bgOpa: cover; + borderColor: @switch-track-border-color; + borderWidth: @switch-track-border-width; + radius: circle; + padAll: 0; + animDuration: @motion-duration-short; +} + +switch_indicator_selected { + bgColor: @switch-track-selected-color; + bgOpa: cover; + radius: circle; +} + +switch_handle { + bgColor: @switch-handle-color; + bgOpa: cover; + radius: circle; + padAll: @switch-handle-pad; +} + +switch_handle_selected { + bgColor: @switch-handle-selected-color; + padAll: @switch-handle-selected-pad; +} + +checkbox { + padGap: @checkbox-gap; +} + +checkbox_box { + bgColor: @checkbox-box-color; + bgOpa: cover; + borderColor: @checkbox-box-border-color; + borderWidth: @checkbox-box-border-width; + radius: @checkbox-box-shape; + padAll: @checkbox-box-pad; + textFont: $fontSmall; + textColor: @checkbox-icon-color; +} + +checkbox_box_selected { + bgColor: @checkbox-box-selected-color; + bgImageSrc: symbol(ok); +} + +sliderbox { + padColumn: @sliderbox-gap; +} + +slider_track_size { + height: @slider-track-height; +} + +// The knob extends beyond the slider by its padding, so the slider reserves that room above and below it in layouts. +// Not left and right: flex grow (e.g. in the sliderbox) doesn't take a growing item's own margins into account. +slider_knob_room { + marginVer: @slider-handle-pad; +} + +slider_track { + bgColor: @slider-track-color; + bgOpa: cover; + borderColor: @slider-track-border-color; + borderWidth: @border; + radius: circle; + padAll: 0; +} + +slider_active_track { + bgColor: @slider-active-track-color; + bgOpa: cover; + radius: circle; +} + +slider_handle { + bgColor: @slider-handle-color; + bgOpa: cover; + radius: circle; + padAll: @slider-handle-pad; +} + +arc_track { + arcColor: @arc-track-color; + arcWidth: @arc-track-width; +} + +arc_indicator { + arcColor: @arc-indicator-color; + arcWidth: @arc-indicator-width; + arcRounded: true; +} + +arc_handle { + bgColor: @arc-handle-color; + bgOpa: cover; + radius: circle; + padAll: @arc-handle-pad; +} + +field { + bgColor: @field-color; + bgOpa: cover; + textColor: @field-text-color; + borderColor: @field-border-color; + borderWidth: @field-border-width; + radius: @field-shape; + padAll: @field-pad; + padGap: @field-pad; +} + +field_focused { + borderWidth: @field-focused-border-width; +} + +field_cursor { + borderColor: @field-cursor-color; + borderWidth: @field-cursor-width; + borderSide: left; + bgOpa: transp; + animDuration: @motion-duration-long; +} + +field_selection { + bgColor: @field-selection-color; + bgOpa: cover; + textColor: @field-selection-text-color; +} + +menu { + bgColor: @menu-color; + bgOpa: cover; + borderColor: @menu-border-color; + borderWidth: @border; + radius: @menu-shape; + textLineSpace: @menu-item-line-space; + maxHeight: @menu-max-height; + clipCorner: true; +} + +menu_item { + bgOpa: transp; +} + +roller { + bgColor: @color-surface; + bgOpa: cover; + textColor: @color-on-surface; + borderColor: @color-outline; + borderWidth: @border; + radius: @shape-container; + textLineSpace: @roller-line-space; + textAlign: center; + animDuration: @motion-duration-medium; +} + +keys { + bgOpa: transp; + borderWidth: 0; + padAll: @keys-pad; + padGap: @keys-gap; +} + +key { + bgColor: @key-color; + bgOpa: cover; + textColor: @key-text-color; + borderColor: @key-border-color; + borderWidth: @border; + radius: @key-shape; +} + +list { + bgColor: @color-surface; + bgOpa: cover; + borderColor: @color-outline; + borderWidth: @border; + radius: @shape-container; + padHor: 0; + padVer: @list-pad; + padGap: 0; + clipCorner: true; +} + +list_item { + bgOpa: transp; + textColor: @color-on-surface; + borderColor: @list-divider-color; + borderWidth: @border; + borderSide: bottom; + radius: 0; + padHor: @list-item-pad-hor; + padVer: @list-item-pad-ver; + padColumn: @list-item-gap; +} + +list_subheader { + textFont: $fontSmall; + textDecor: underline; + padHor: @list-item-pad-hor; + padTop: @list-item-pad-ver; + padBottom: @list-pad; +} + +tabs { + bgOpa: transp; + borderColor: @tabs-divider-color; + borderWidth: @border; + borderSide: bottom; +} + +tab { + bgOpa: transp; + textColor: @color-on-surface; + borderWidth: 0; + radius: 0; +} + +tab_selected { + borderColor: @tab-indicator-color; + borderWidth: @tab-indicator-width; + borderSide: bottom; +} + +tab_page { + padAll: @tab-page-pad; +} + +dialog { + bgColor: @dialog-color; + bgOpa: cover; + textColor: @color-on-surface; + borderColor: @dialog-border-color; + borderWidth: @dialog-border-width; + radius: @dialog-shape; + padAll: 0; + padGap: 0; + clipCorner: true; +} + +dialog_header { + padHor: @dialog-pad; + padTop: @dialog-pad; + padBottom: @dialog-gap; + padGap: @dialog-gap; +} + +dialog_title { + textFont: $fontLarge; +} + +dialog_content { + padHor: @dialog-pad; + padVer: @dialog-gap; +} + +dialog_actions { + padAll: @dialog-pad; + padTop: @dialog-gap; + padGap: @dialog-gap; +} + +settings { + bgColor: @color-surface; + bgOpa: cover; + padAll: 0; + padGap: 0; + radius: 0; + clipCorner: true; +} + +settings_sidebar { + padAll: 0; + padGap: 0; + borderColor: @color-outline; + borderWidth: @border; + borderSide: right; +} + +settings_content { + padAll: 0; + padGap: 0; +} + +settings_page { + padHor: 0; + padGap: 0; +} + +settings_header { + padHor: @density-list-item-pad-hor; + padVer: @density-list-pad; + padGap: @density-container-gap; +} + +settings_section { + bgColor: @color-surface; + bgOpa: cover; + borderColor: @color-outline; + borderWidth: @border; + radius: @shape-container; + clipCorner: true; +} + +settings_item { + padHor: @density-list-item-pad-hor; + padVer: @density-list-item-pad-ver; + padGap: @density-container-gap; +} + +settings_separator { + bgOpa: transp; + padVer: @density-list-pad; +} + +window_header { + bgColor: @color-surface; + bgOpa: cover; + borderColor: @window-header-border-color; + borderWidth: @border; + borderSide: bottom; + padAll: @window-header-pad; + padGap: @window-header-pad; +} + +window_title { + textFont: $fontLarge; +} + +window_content { + bgOpa: transp; + padAll: @window-content-pad; + padGap: @density-container-gap; +} + +table { + bgColor: @color-surface; + bgOpa: cover; + borderColor: @color-outline; + borderWidth: @border; + radius: 0; + padAll: 0; +} + +table_cell { + bgOpa: transp; + textColor: @color-on-surface; + padAll: @table-cell-pad; + borderColor: @table-divider-color; + borderWidth: @border; + borderSide: bottom; +} + +chart { + bgColor: @color-surface; + bgOpa: cover; + borderColor: @color-outline; + borderWidth: @border; + radius: @shape-container; + padAll: @density-container-pad; + lineColor: @chart-grid-color; +} + +chart_series { + lineColor: @color-on-surface; + lineWidth: @line-medium; + size: @chart-point-size, @chart-point-size; +} + +chart_indicator { + bgColor: @color-primary; + bgOpa: cover; + radius: circle; + size: @chart-point-size, @chart-point-size; +} + +calendar { + bgColor: @color-surface; + bgOpa: cover; + borderColor: @color-outline; + borderWidth: @border; + radius: @shape-container; + padAll: @calendar-pad; + padGap: @calendar-pad; +} + +calendar_day { + bgOpa: transp; + borderWidth: 0; + radius: circle; +} + +scale { + lineColor: @scale-color; + lineWidth: @border; + arcColor: @scale-color; + arcWidth: @border; + textColor: @scale-color; + length: @scale-tick-length; +} + +divider { + lineColor: @divider-color; + lineWidth: @border; +} + +led { + bgColor: @color-on-surface; + bgOpa: cover; + radius: circle; + borderColor: @color-outline; + borderWidth: @border; +} + +// ============================================================ +// Apply rules +// ============================================================ + +apply(null > *) { + screen: default; + scrollbar: part.scrollbar; +} + +// Base object + +apply(tabview > obj[1]) { + // Tab content area: no styles +} + +apply(tabview > obj[0]) { + tabs: default; +} + +apply(tabview > * > obj) { + tab_page: default; + scrollbar: part.scrollbar; +} + +apply(win > obj[0]) { + window_header: default; +} + +apply(win > obj[1]) { + window_content: default; + scrollbar: part.scrollbar; +} + +apply(calendar > obj) { + // No styles +} + +apply(toolbar > obj) { + app_bar_actions: default; +} + +apply(obj) { + container: default; + scrollbar: part.scrollbar; +} + +// Buttons + +apply(tabview > *[0] > button) { + tab: default; + inverted: state.pressed; + tab_selected: state.checked; + focus_ring: state.focusKey; + edit_ring: state.edited; +} + +apply(toolbar > icon_button, toolbar > obj > icon_button) { + icon_button: default; + app_bar_button: default; + inverted: state.pressed; + app_bar_focus_ring: state.focusKey; + app_bar_focus_ring: state.edited; + disabled: state.disabled; +} + +apply(toolbar) { + app_bar: default; +} + +apply(icon_button) { + icon_button: default; + icon_button_selected: state.checked; + inverted: state.pressed; + focus_ring_inset: state.focusKey; + edit_ring_inset: state.edited; + disabled: state.disabled; +} + +apply(icon_button_filled) { + icon_button_filled: default; + icon_button_filled_pressed: state.pressed; + focus_ring: state.focusKey; + edit_ring: state.edited; + disabled: state.disabled; +} + +apply(icon_button_tonal) { + icon_button_tonal: default; + inverted: state.pressed; + focus_ring: state.focusKey; + edit_ring: state.edited; + disabled: state.disabled; +} + +apply(menu_sidebar_header_cont > button, menu_main_header_cont > button, win > obj[0] > button, msgbox_header_button) { + icon_button: default; + inverted: state.pressed; + focus_ring: state.focusKey; +} + +apply(button, msgbox_footer_button) { + button: default; + inverted: state.pressed; + inverted: state.checked; + focus_ring: state.focusKey; + edit_ring: state.edited; + disabled: state.disabled; +} + +// Labels + +apply(msgbox_header > label) { + dialog_title: default; +} + +apply(win > obj[0] > label) { + window_title: default; +} + +apply(textarea > label) { + field_selection: part.selected; +} + +// Selection controls + +apply(switch) { + switch_track: default; + switch_indicator_selected: part.indicator, state.checked; + switch_handle: part.knob; + switch_handle_selected: part.knob, state.checked; + focus_ring: state.focusKey; + edit_ring: state.edited; + disabled: state.disabled; +} + +apply(checkbox) { + checkbox: default; + focus_ring: state.focusKey; + checkbox_box: part.indicator; + checkbox_box_selected: part.indicator, state.checked; + disabled: part.indicator, state.disabled; +} + +apply(card) { + card: default; + scrollbar: part.scrollbar; +} + +apply(sliderbox) { + sliderbox: default; +} + +apply(slider) { + slider_track: default; + slider_track_size: default; + slider_knob_room: default; + focus_ring: state.focusKey; + edit_ring: state.edited; + slider_active_track: part.indicator; + slider_handle: part.knob; +} + +apply(bar) { + slider_track: default; + slider_active_track: part.indicator; + focus_ring: state.focusKey; +} + +apply(arc) { + arc_track: default; + arc_indicator: part.indicator; + arc_handle: part.knob; + focus_ring: state.focusKey; + edit_ring: state.edited; +} + +apply(spinner) { + arc_track: default; + arc_indicator: part.indicator; +} + +// Text input + +apply(textarea) { + field: default; + field_focused: state.focused; + focus_ring: state.focusKey; + edit_ring: state.edited; + disabled: state.disabled; + scrollbar: part.scrollbar; + field_cursor: part.cursor, state.focused; +} + +apply(spinbox) { + field: default; + field_focused: state.focused; + focus_ring: state.focusKey; + edit_ring: state.edited; + inverted: part.cursor; +} + +apply(dropdown) { + field: default; + inverted: state.pressed; + focus_ring: state.focusKey; + edit_ring: state.edited; + disabled: state.disabled; +} + +apply(dropdownlist) { + menu: default; + scrollbar: part.scrollbar; + menu_item: part.selected; + inverted: part.selected, state.checked; + inverted: part.selected, state.pressed; +} + +apply(roller) { + roller: default; + inverted: part.selected; + focus_ring: state.focusKey; + edit_ring: state.edited; +} + +// Button matrix and keyboard + +apply(calendar > buttonmatrix) { + keys: default; + calendar_day: part.items; + inverted: part.items, state.pressed; + inverted: part.items, state.checked; + focus_ring: part.items, state.focusKey; + disabled: part.items, state.disabled; +} + +apply(buttonmatrix, keyboard) { + keys: default; + focus_ring: state.focusKey; + edit_ring: state.edited; + key: part.items; + inverted: part.items, state.pressed; + inverted: part.items, state.checked; + focus_ring: part.items, state.focusKey; + disabled: part.items, state.disabled; +} + +// Lists + +apply(list) { + list: default; + scrollbar: part.scrollbar; +} + +apply(list_text) { + list_subheader: default; +} + +apply(list_button) { + list_item: default; + inverted: state.pressed; + inverted: state.focusKey; + disabled: state.disabled; +} + +apply(table) { + table: default; + focus_ring: state.focusKey; + edit_ring: state.edited; + scrollbar: part.scrollbar; + table_cell: part.items; + inverted: part.items, state.pressed; + inverted: part.items, state.focusKey; +} + +// Settings menu + +apply(menu) { + settings: default; +} + +apply(menu_sidebar_cont) { + settings_sidebar: default; + scrollbar: part.scrollbar; +} + +apply(menu_main_cont) { + settings_content: default; + scrollbar: part.scrollbar; +} + +apply(menu_page) { + settings_page: default; + scrollbar: part.scrollbar; +} + +apply(menu_sidebar_header_cont, menu_main_header_cont) { + settings_header: default; +} + +apply(menu_section) { + settings_section: default; +} + +apply(menu_cont) { + settings_item: default; + inverted: state.pressed; + inverted: state.checked; + inverted: state.focusKey; +} + +apply(menu_separator) { + settings_separator: default; +} + +// Dialog + +apply(msgbox) { + dialog: default; +} + +apply(msgbox_backdrop) { + // A 1-bit display can't show a scrim +} + +apply(msgbox_header) { + dialog_header: default; +} + +apply(msgbox_content) { + dialog_content: default; + scrollbar: part.scrollbar; +} + +apply(msgbox_footer) { + dialog_actions: default; +} + +// Containers + +apply(tabview, win) { + page: default; +} + +apply(tileview) { + page: default; + scrollbar: part.scrollbar; +} + +apply(tileview_tile) { + scrollbar: part.scrollbar; +} + +// Data display + +apply(chart) { + chart: default; + scrollbar: part.scrollbar; + chart_series: part.items; + chart_indicator: part.indicator; + chart_series: part.cursor; +} + +apply(calendar) { + calendar: default; +} + +apply(scale) { + scale: part.main; + scale: part.indicator; + scale: part.items; +} + +apply(line) { + divider: default; +} + +apply(led) { + led: default; +} diff --git a/Tactility/Private/Tactility/app/AppGrid.h b/Tactility/Private/Tactility/app/AppGrid.h index fd2b6a5ed..5fc0ed736 100644 --- a/Tactility/Private/Tactility/app/AppGrid.h +++ b/Tactility/Private/Tactility/app/AppGrid.h @@ -68,7 +68,6 @@ private: static void onTileClicked(lv_event_t* e); static void onTileLongPressed(lv_event_t* e); static void onTileKey(lv_event_t* e); - static void onTileFocusChanged(lv_event_t* e); }; } diff --git a/Tactility/Source/app/AppGrid.cpp b/Tactility/Source/app/AppGrid.cpp index 4af67ffa9..33c0717b5 100644 --- a/Tactility/Source/app/AppGrid.cpp +++ b/Tactility/Source/app/AppGrid.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -12,8 +13,6 @@ namespace tt::app { namespace { 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 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) { return static_cast(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); } -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) { keepSelectionOnRepopulate = keepSelection; 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(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(lv_obj_get_parent(prevButton), 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_set_flag(prevButton, LV_OBJ_FLAG_HIDDEN, page_count <= 1); + lv_obj_set_flag(nextButton, LV_OBJ_FLAG_HIDDEN, page_count <= 1); lv_obj_t* focusedTile = nullptr; - const bool monochrome = isMonochrome(grid); const uint32_t first = page * layout.pageSize; const uint32_t last = std::min(item_count, first + layout.pageSize); for (uint32_t i = first; i < last; 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_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_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_align(tile, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_user_data(tile, const_cast(&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_align(icon, LV_TEXT_ALIGN_CENTER, LV_STATE_DEFAULT); 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_obj_t* label = lv_label_create(tile); @@ -209,10 +182,6 @@ void AppGrid::populate() { if (callbacks.onKey != nullptr) { 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) { focusedTile = tile; @@ -247,7 +216,7 @@ void AppGrid::createWidgets(lv_obj_t* parent, lv_obj_t* toolbar) { 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)) { lv_group_focus_obj(nextButton); lv_obj_add_state(nextButton, LV_STATE_FOCUS_KEY); diff --git a/Tactility/Source/app/audiosettings/AudioSettings.cpp b/Tactility/Source/app/audiosettings/AudioSettings.cpp index ab5653346..82ada4079 100644 --- a/Tactility/Source/app/audiosettings/AudioSettings.cpp +++ b/Tactility/Source/app/audiosettings/AudioSettings.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -77,23 +78,26 @@ void onOutputVolumeSlider(lv_event_t* event) { } lv_obj_t* createSection(lv_obj_t* parent, const char* title) { - auto* wrapper = lv_obj_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); + auto* title_label = lv_label_create(parent); 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) { - auto* row = lv_obj_create(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 = createRow(parent); auto* row_label = lv_label_create(row); 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) { - auto* row = lv_obj_create(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 = createRow(parent); auto* row_label = lv_label_create(row); lv_label_set_text(row_label, label); diff --git a/Tactility/Source/app/btmanage/View.cpp b/Tactility/Source/app/btmanage/View.cpp index 1ef9baa1f..b089f1750 100644 --- a/Tactility/Source/app/btmanage/View.cpp +++ b/Tactility/Source/app/btmanage/View.cpp @@ -163,11 +163,17 @@ void View::updateScanning() { void View::updatePeerList() { lv_obj_clean(peers_list); - // Enable on boot row - auto* enable_on_boot_wrapper = lv_obj_create(peers_list); + // Enable on boot + + // 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_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); 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_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(); using enum bluetooth::RadioState; diff --git a/Tactility/Source/app/display/Display.cpp b/Tactility/Source/app/display/Display.cpp index e23623f9f..2423fdc31 100644 --- a/Tactility/Source/app/display/Display.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -177,7 +177,6 @@ void createWidgets(lv_obj_t* parent, void* userData) { auto* ctx = static_cast(userData); ctx->displaySettings = settings::display::loadOrGetDefault(); - auto ui_density = lvgl_get_ui_density(); bool has_imu = device_exists_of_type(&IMU_TYPE); 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_style_pad_hor(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); lv_label_set_text(brightness_label, "Brightness"); diff --git a/Tactility/Source/app/files/View.cpp b/Tactility/Source/app/files/View.cpp index 90e8840e4..d510ca6b6 100644 --- a/Tactility/Source/app/files/View.cpp +++ b/Tactility/Source/app/files/View.cpp @@ -552,15 +552,15 @@ void View::update(size_t start_index) { }); 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 { - 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) { - 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 { - 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(); @@ -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_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); - 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); lv_obj_set_width(wrapper, LV_PCT(100)); diff --git a/Tactility/Source/app/gpssettings/GpsSettings.cpp b/Tactility/Source/app/gpssettings/GpsSettings.cpp index df6c658b5..d1a2a09b1 100644 --- a/Tactility/Source/app/gpssettings/GpsSettings.cpp +++ b/Tactility/Source/app/gpssettings/GpsSettings.cpp @@ -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_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"); // 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_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); 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_grow(ctx->deviceListWrapper, 1); 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); updateDeviceStates(ctx); diff --git a/Tactility/Source/app/launcher/Launcher.cpp b/Tactility/Source/app/launcher/Launcher.cpp index a969eee56..e04317169 100644 --- a/Tactility/Source/app/launcher/Launcher.cpp +++ b/Tactility/Source/app/launcher/Launcher.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -32,19 +33,30 @@ constexpr auto* TAG = "Launcher"; 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) { const int32_t usable = std::max(0, available_span - (3 * total_button_size)); return std::min(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(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) { auto* appId = static_cast(lv_event_get_user_data(e)); 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_padding = getButtonPadding(uiDensity, button_size); - auto* apps_button = lv_button_create(parent); - - lv_obj_set_style_pad_all(apps_button, static_cast(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); + auto* apps_button = lvgl_icon_button_create_variant(parent, variant); // create the image first 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_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) 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_event_get_user_data(e)); 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 vertical_px = lv_display_get_vertical_resolution(display); 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); - - 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); - } + applyButtonMargins(buttons_wrapper, is_landscape_display); } void createWidgets(lv_obj_t* parent, void*) { 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_set_size(buttons_wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT); 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); } - const int32_t margin = is_landscape_display - ? computeButtonMargin(lv_display_get_horizontal_resolution(display), total_button_size) - : computeButtonMargin(lv_display_get_vertical_resolution(display), total_button_size); - - auto* app_list_button = createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_APPS, "tactility.applist", margin, 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 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"); + 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"); + applyButtonMargins(buttons_wrapper, is_landscape_display); // 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 @@ -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 // button stays in the launcher; the confirmation flow lives in the PowerOff app. 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_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_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); 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 diff --git a/Tactility/Source/app/power/Power.cpp b/Tactility/Source/app/power/Power.cpp index 81a7c7bd7..1b6662e60 100644 --- a/Tactility/Source/app/power/Power.cpp +++ b/Tactility/Source/app/power/Power.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -170,10 +171,14 @@ void createWidgets(lv_obj_t* parent, void* userData) { entry.device = device; 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)) { - 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_height(switch_container, LV_SIZE_CONTENT); 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)) { - 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_height(qc_container, LV_SIZE_CONTENT); lv_obj_set_style_pad_all(qc_container, 0, 0); @@ -215,8 +220,7 @@ void createWidgets(lv_obj_t* parent, void* userData) { PowerSupplyPropertyValue value; for (auto property : DISPLAYED_PROPERTIES) { if (power_supply_get_property(device, property, &value) == ERROR_NONE) { - lv_obj_t* label = lv_label_create(wrapper); - lv_obj_set_style_margin_left(label, 24, LV_STATE_DEFAULT); + lv_obj_t* label = lv_label_create(card); setPropertyLabelText(label, property, value); entry.propertyWidgets.push_back({ property, label }); } diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index 0d8ba505d..f5c4cb5e8 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -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_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}; } diff --git a/Tactility/Source/app/trackball/TrackballSettings.cpp b/Tactility/Source/app/trackball/TrackballSettings.cpp index 152500e0b..a1fa6c8de 100644 --- a/Tactility/Source/app/trackball/TrackballSettings.cpp +++ b/Tactility/Source/app/trackball/TrackballSettings.cpp @@ -149,7 +149,6 @@ void createWidgets(lv_obj_t* parent, void* userData) { auto* ctx = static_cast(userData); ctx->tbSettings = settings::trackball::loadOrGetDefault(); - auto ui_density = lvgl_get_ui_density(); ctx->updated = false; 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_style_pad_hor(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); 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_style_pad_hor(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); lv_label_set_text(ptr_sens_label, "Pointer Speed"); diff --git a/Tactility/Source/app/wifimanage/View.cpp b/Tactility/Source/app/wifimanage/View.cpp index 0c9c2f31e..ff51025d5 100644 --- a/Tactility/Source/app/wifimanage/View.cpp +++ b/Tactility/Source/app/wifimanage/View.cpp @@ -142,10 +142,15 @@ void View::updateNetworkList() { // 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_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); 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_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(); switch (state->getRadioState()) { diff --git a/Tactility/Source/app/wifimanage/WifiManage.cpp b/Tactility/Source/app/wifimanage/WifiManage.cpp index bbd295b68..de009a531 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.cpp +++ b/Tactility/Source/app/wifimanage/WifiManage.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include diff --git a/Tactility/Source/lvgl/Lvgl.cpp b/Tactility/Source/lvgl/Lvgl.cpp index 9a34ed553..2be29297a 100644 --- a/Tactility/Source/lvgl/Lvgl.cpp +++ b/Tactility/Source/lvgl/Lvgl.cpp @@ -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_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_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_radius(vertical_container, 0, LV_STATE_DEFAULT); lv_obj_remove_flag(vertical_container, LV_OBJ_FLAG_SCROLLABLE); diff --git a/Tactility/Source/lvgl/Statusbar.cpp b/Tactility/Source/lvgl/Statusbar.cpp index 9199e05d0..5709c3fde 100644 --- a/Tactility/Source/lvgl/Statusbar.cpp +++ b/Tactility/Source/lvgl/Statusbar.cpp @@ -168,21 +168,12 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) { auto* statusbar = reinterpret_cast(obj); - const auto fg_color = STATUSBAR_COLORS_INVERTED ? lv_color_black() : lv_color_white(); - 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 + // The statusbar has no theme styles: it shows the themed container background behind it and inherits its text colour if (STATUSBAR_COLORS_INVERTED) { - // Show border as a horizontal line on the bottom - lv_obj_set_style_border_color(obj, fg_color, LV_STATE_DEFAULT); + // A horizontal line separates the statusbar from the rest of the UI below it + 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_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)); @@ -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); 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); update_time(statusbar); @@ -213,7 +203,6 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) { auto* image = lv_image_create(obj); 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_color(image, fg_color, LV_STATE_DEFAULT); lv_obj_set_style_pad_all(image, 0, LV_STATE_DEFAULT); statusbar->icons[i] = image; diff --git a/Tactility/Source/lvgl/wrappers/button.cpp b/Tactility/Source/lvgl/wrappers/button.cpp deleted file mode 100644 index 6d9c0d718..000000000 --- a/Tactility/Source/lvgl/wrappers/button.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifdef ESP_PLATFORM - -#include - -#include - -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 \ No newline at end of file diff --git a/Tactility/Source/lvgl/wrappers/dropdown.cpp b/Tactility/Source/lvgl/wrappers/dropdown.cpp deleted file mode 100644 index ad15e4704..000000000 --- a/Tactility/Source/lvgl/wrappers/dropdown.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifdef ESP_PLATFORM - -#include - -#include - -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 \ No newline at end of file diff --git a/Tactility/Source/lvgl/wrappers/list.cpp b/Tactility/Source/lvgl/wrappers/list.cpp deleted file mode 100644 index d297f5292..000000000 --- a/Tactility/Source/lvgl/wrappers/list.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifdef ESP_PLATFORM - -#include - -#include - -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 \ No newline at end of file diff --git a/Tactility/Source/lvgl/wrappers/obj.cpp b/Tactility/Source/lvgl/wrappers/obj.cpp deleted file mode 100644 index 5df116501..000000000 --- a/Tactility/Source/lvgl/wrappers/obj.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifdef ESP_PLATFORM - -#include - -#include - -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 \ No newline at end of file diff --git a/Tactility/Source/lvgl/wrappers/switch.cpp b/Tactility/Source/lvgl/wrappers/switch.cpp deleted file mode 100644 index f11d8eb0e..000000000 --- a/Tactility/Source/lvgl/wrappers/switch.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifdef ESP_PLATFORM - -#include - -#include - -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 diff --git a/Tactility/Source/lvgl/wrappers/textarea.cpp b/Tactility/Source/lvgl/wrappers/textarea.cpp index f37f0f5d3..2327fcaeb 100644 --- a/Tactility/Source/lvgl/wrappers/textarea.cpp +++ b/Tactility/Source/lvgl/wrappers/textarea.cpp @@ -10,23 +10,14 @@ extern lv_obj_t* __real_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); - 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(); if (software_keyboard != nullptr) { 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; } } -#endif // ESP_PLATFORM \ No newline at end of file +#endif // ESP_PLATFORM diff --git a/Tactility/idf_component.yml b/Tactility/idf_component.yml index ea8299b45..d06a00ed6 100644 --- a/Tactility/idf_component.yml +++ b/Tactility/idf_component.yml @@ -81,7 +81,7 @@ dependencies: rules: - if: "target in [esp32s3, esp32p4]" espressif/esp_lvgl_port: "2.7.2" - lvgl/lvgl: "9.3.0" + lvgl/lvgl: "9.4.0" epdiy: git: https://github.com/vroland/epdiy.git version: 2.1.3