From 33e96bb92d491e95f7395473e30308d2592a47fa Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Sun, 4 Oct 2026 01:25:45 +0200 Subject: [PATCH 1/2] epaper improvements --- .../lvgl-module/source/devices/display.cpp | 37 +++++++++++++++++++ Tactility/Source/app/AppGrid.cpp | 25 ++++++++++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/Modules/lvgl-module/source/devices/display.cpp b/Modules/lvgl-module/source/devices/display.cpp index 82d9eb529..022ca2ad9 100644 --- a/Modules/lvgl-module/source/devices/display.cpp +++ b/Modules/lvgl-module/source/devices/display.cpp @@ -1,4 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 +#define LV_USE_PRIVATE_API 1 // For the lv_theme_t declaration + #include #include @@ -18,6 +20,37 @@ 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; + } + lv_theme_t* mono_theme = lv_display_get_theme(disp); + if (mono_theme == nullptr || mono_theme == &mono_theme_extension) { + return; + } + mono_theme_extension = *mono_theme; + lv_theme_set_parent(&mono_theme_extension, mono_theme); + lv_theme_set_apply_cb(&mono_theme_extension, mono_theme_extension_apply); + lv_display_set_theme(disp, &mono_theme_extension); +} + +#endif + struct LvglDisplayCtx { void* buf1; void* buf2; @@ -470,6 +503,10 @@ 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 + ctx->render_mode = render_mode; lv_display_set_color_format(disp, lv_color_format); lv_display_set_buffers(disp, ctx->buf1, ctx->buf2, buf_size_bytes, render_mode); diff --git a/Tactility/Source/app/AppGrid.cpp b/Tactility/Source/app/AppGrid.cpp index 8e788c3b2..6a53850b0 100644 --- a/Tactility/Source/app/AppGrid.cpp +++ b/Tactility/Source/app/AppGrid.cpp @@ -52,6 +52,11 @@ lv_color_t getIconColor(bool focused, bool highlighted) { 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))); } @@ -156,6 +161,7 @@ void AppGrid::populate() { lv_obj_set_flag(lv_obj_get_parent(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++) { @@ -167,8 +173,10 @@ void AppGrid::populate() { 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); - lv_obj_set_style_outline_width(tile, 0, LV_STATE_FOCUSED); - lv_obj_set_style_outline_width(tile, 0, LV_STATE_FOCUS_KEY); + 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)); @@ -177,13 +185,16 @@ void AppGrid::populate() { lv_obj_set_style_text_font(icon, lvgl_get_shared_icon_font_2x(), LV_STATE_DEFAULT); lv_obj_set_style_text_align(icon, LV_TEXT_ALIGN_CENTER, LV_STATE_DEFAULT); lv_obj_set_size(icon, layout.iconSize, layout.iconSize); - lv_obj_set_style_text_color(icon, getIconColor(false, item.highlighted), LV_STATE_DEFAULT); + 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); lv_obj_set_style_text_font(label, lvgl_get_text_font(FONT_SIZE_SMALL), LV_STATE_DEFAULT); lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, LV_STATE_DEFAULT); - lv_obj_set_width(label, LV_PCT(100)); + // Dots mode needs a fixed height, otherwise the label wraps instead of truncating + lv_obj_set_size(label, LV_PCT(100), lv_font_get_line_height(lvgl_get_text_font(FONT_SIZE_SMALL))); lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_DOTS); lv_label_set_text(label, item.manifest.name); @@ -194,8 +205,10 @@ void AppGrid::populate() { if (callbacks.onKey != nullptr) { lv_obj_add_event_cb(tile, onTileKey, LV_EVENT_KEY, this); } - lv_obj_add_event_cb(tile, onTileFocusChanged, LV_EVENT_FOCUSED, nullptr); - lv_obj_add_event_cb(tile, onTileFocusChanged, LV_EVENT_DEFOCUSED, nullptr); + 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; From ccc76dc819fb6be5deb3b4d3a0204c4207a2e5eb Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Sun, 4 Oct 2026 11:23:11 +0200 Subject: [PATCH 2/2] Fix for mono theme check --- Modules/lvgl-module/source/devices/display.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/lvgl-module/source/devices/display.cpp b/Modules/lvgl-module/source/devices/display.cpp index 022ca2ad9..1c88ef8d2 100644 --- a/Modules/lvgl-module/source/devices/display.cpp +++ b/Modules/lvgl-module/source/devices/display.cpp @@ -39,8 +39,9 @@ static void lvgl_display_extend_mono_theme(lv_display_t* disp) { 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 == &mono_theme_extension) { + if (mono_theme == nullptr || mono_theme != lv_theme_mono_get()) { return; } mono_theme_extension = *mono_theme;