feat(l2): light theme

Settings > Display > Theme: Dark / Light (NVS mc_ui/light). The colour
tokens are set once before anything is built, so a change restarts the
device. Text on an accent fill is the new ON_ACCENT (it was BG, white
in the light theme); each accent has a deeper hand-picked light variant
that reads on white and takes white text. The map's empty-tile fill and
grid and the areas' outlines follow the theme too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-10-14 22:55:47 +02:00
co-authored by Claude Opus 5.5
parent e5a55c3b1f
commit 76e96f7eaa
15 changed files with 82 additions and 50 deletions
+2 -1
View File
@@ -91,7 +91,8 @@ if your mesh agrees on it.
> [!NOTE]
> **Wio Tracker L2:** one list of pages:
>
> - **Device**: Display (including the lock and **Tap to wake**), Home apps,
> - **Device**: Display (including the lock, **Tap to wake**, the **Theme**,
> dark or light, which restarts the device, and the accent colour), Home apps,
> Power (battery, GPS, and the **Battery curve** as a chart whose points you
> drag), Sound (with the melody editor), Keyboard, Messages
> & contacts.
@@ -68,14 +68,14 @@ static void onCalendarDraw(lv_event_t* e) {
}
snprintf(buf, sizeof(buf), "%d", day);
td.text = buf;
td.color = lv_color_hex(today ? theme::BG : k % 7 >= 5 ? theme::TEXT_MUTED : theme::TEXT);
td.color = lv_color_hex(today ? theme::ON_ACCENT : k % 7 >= 5 ? theme::TEXT_MUTED : theme::TEXT);
lv_area_t la = { cx, cy + (ch - fh) / 2, cx + cw - 1, cy + (ch + fh) / 2 };
lv_draw_label(layer, &td, &la);
if (s_cal_marks & (1UL << (day - 1))) { // under the number
lv_draw_rect_dsc_t md;
lv_draw_rect_dsc_init(&md);
md.radius = 1;
md.bg_color = lv_color_hex(today ? theme::BG : theme::ACCENT);
md.bg_color = lv_color_hex(today ? theme::ON_ACCENT : theme::ACCENT);
lv_area_t ma = { cx + cw / 2 - 6, cy + ch - 2, cx + cw / 2 + 6, cy + ch - 1 };
lv_draw_rect(layer, &md, &ma);
}
@@ -404,12 +404,12 @@ void UITask::accentRow(lv_obj_t* body) { // a row of a group: the label over t
lv_obj_t* sw = lv_button_create(row);
lv_obj_set_size(sw, 32, 32);
lv_obj_set_style_radius(sw, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_bg_color(sw, lv_color_hex(theme::ACCENTS[i].col), 0);
lv_obj_set_style_bg_color(sw, lv_color_hex(theme::s_light ? theme::ACCENTS[i].light : theme::ACCENTS[i].col), 0);
bool cur = i == theme::s_accent;
lv_obj_set_style_border_width(sw, cur ? 3 : 0, 0);
lv_obj_set_style_border_color(sw, lv_color_hex(theme::TEXT), 0);
lv_obj_add_event_cb(sw, onAccent, LV_EVENT_CLICKED, (void*)(uintptr_t)i);
if (cur) lv_obj_center(label(sw, LV_SYMBOL_OK, THEME_FONT_SMALL, theme::BG));
if (cur) lv_obj_center(label(sw, LV_SYMBOL_OK, THEME_FONT_SMALL, theme::ON_ACCENT));
}
}
@@ -372,7 +372,7 @@ static void editTile(lv_obj_t* box, int pos) {
lv_obj_set_style_bg_opa(b, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(on ? theme::SURFACE_2 : theme::ACCENT), 0);
lv_obj_align(b, LV_ALIGN_TOP_RIGHT, 2, -2);
lv_obj_center(label(b, on ? LV_SYMBOL_MINUS : LV_SYMBOL_PLUS, THEME_FONT_SMALL, on ? theme::TEXT : theme::BG));
lv_obj_center(label(b, on ? LV_SYMBOL_MINUS : LV_SYMBOL_PLUS, THEME_FONT_SMALL, on ? theme::TEXT : theme::ON_ACCENT));
}
} // namespace home
@@ -479,7 +479,7 @@ void UITask::buildHome() {
lv_obj_set_style_bg_color(b, lv_color_hex(theme::ACCENT), 0);
lv_obj_align(b, LV_ALIGN_BOTTOM_RIGHT, 0, 24);
lv_obj_add_event_cb(b, home::onEditDone, LV_EVENT_CLICKED, NULL);
lv_obj_center(label(b, "Done", THEME_FONT_SMALL, theme::BG));
lv_obj_center(label(b, "Done", THEME_FONT_SMALL, theme::ON_ACCENT));
}
setHomePage(home::s_page);
}
@@ -635,7 +635,7 @@ void UITask::buildHomeMap(lv_obj_t* box) {
using namespace home::mini;
lv_obj_set_layout(box, LV_LAYOUT_NONE);
s_area = lv_obj_create(box);
styleSurface(s_area, 0x1A1A1E); // unloaded / missing tiles
styleSurface(s_area, theme::SURFACE); // unloaded / missing tiles
lv_obj_set_size(s_area, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_radius(s_area, theme::RADIUS, 0);
lv_obj_set_style_clip_corner(s_area, true, 0);
@@ -1010,6 +1010,9 @@ static void setVectorOn(bool on) { nvs::putBool("mc_ui", "vector", on); }
// Accent colour (Settings > Display): an index into theme::ACCENTS.
static int loadAccent() { return nvs::getU8("mc_ui", "accent", 0); }
static void saveAccent(int idx) { nvs::putU8("mc_ui", "accent", idx); }
// Settings > Display > Theme: light (else dark), applied at start.
static bool loadLight() { return nvs::getU8("mc_ui", "light", 0) != 0; }
static void saveLight(bool on) { nvs::putU8("mc_ui", "light", on ? 1 : 0); }
// Settings > Storage > Kept per conversation (an index into histstore::KEEP).
static int loadHistKeep() { return nvs::getI8("mc_ui", "hkeep", -1); }
static void saveHistKeep(int idx) { nvs::putI8("mc_ui", "hkeep", idx); }
+1 -1
View File
@@ -119,7 +119,7 @@ static void drawLayer(lv_event_t* e) {
lv_draw_rect_dsc_init(&d);
d.bg_opa = LV_OPA_TRANSP;
d.border_width = i == s_show ? 2 : 1;
d.border_color = lv_color_hex(i == s_show ? theme::ACCENT : 0x9A958C);
d.border_color = lv_color_hex(i == s_show ? theme::ACCENT : theme::TEXT_MUTED);
d.border_opa = LV_OPA_COVER;
lv_draw_rect(layer, &d, &r);
}
+3 -3
View File
@@ -126,7 +126,7 @@ static void drawGrid(lv_event_t* e) {
lv_obj_get_coords(o, &a);
lv_draw_line_dsc_t d;
lv_draw_line_dsc_init(&d);
d.color = lv_color_hex(0x3C3C46);
d.color = lv_color_hex(theme::SURFACE_2);
d.width = 1;
lv_layer_t* layer = lv_event_get_layer(e);
int w = lv_area_get_width(&a), h = lv_area_get_height(&a);
@@ -555,7 +555,7 @@ static void drawMarks(lv_event_t* e) {
lv_draw_label_dsc_t bl;
lv_draw_label_dsc_init(&bl);
bl.font = THEME_FONT_SMALL;
bl.color = lv_color_hex(theme::BG);
bl.color = lv_color_hex(theme::ON_ACCENT);
bl.align = LV_TEXT_ALIGN_CENTER;
for (int k = 0; k < s_mark_count; k++) {
const Mark& m = s_marks[k];
@@ -759,7 +759,7 @@ void UITask::buildMap() {
lv_obj_set_layout(body, LV_LAYOUT_NONE);
lv_obj_set_style_pad_all(body, 0, 0);
lv_obj_remove_flag(body, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_bg_color(body, lv_color_hex(0x1A1A1E), 0); // unloaded / missing tiles
lv_obj_set_style_bg_color(body, lv_color_hex(theme::SURFACE), 0); // unloaded / missing tiles
lv_obj_set_style_bg_opa(body, LV_OPA_COVER, 0);
lv_obj_add_event_cb(body, mapview::drawGrid, LV_EVENT_DRAW_MAIN_END, &mapview::s_grid);
lv_obj_add_event_cb(body, mapview::tiledCovers, LV_EVENT_COVER_CHECK, &mapview::s_grid);
@@ -102,7 +102,7 @@ static void onPagesScrolled(lv_event_t* e) {
// A tile's look: on in the accent with dark text, off on the surface;
// `dim`: nothing to switch on this device.
static void tileSet(int t, const char* icon, const char* cap, bool on, bool dim = false) {
const uint32_t fg = on ? theme::BG : dim ? theme::TEXT_MUTED : theme::TEXT;
const uint32_t fg = on ? theme::ON_ACCENT : dim ? theme::TEXT_MUTED : theme::TEXT;
const uint8_t look = 1 | (on ? 2 : 0);
if (s_look[t] != look) { // LVGL restyles, and redraws, on every set (the refresh is each second)
s_look[t] = look;
@@ -127,6 +127,14 @@ void UITask::setBrightnessPct(uint8_t pct, bool save) {
if (save) prefsSave();
}
// Every screen is built in the theme's colours: a new one starts afresh.
static void onThemeChoice(lv_event_t* e) {
const bool light = choiceSelected((lv_obj_t*)lv_event_get_target(e)) == 1;
if (light == theme::s_light) return;
lvport::saveLight(light);
restartScreen(light ? "Starting in the light theme." : "Starting in the dark theme.");
}
static void onTapWake(lv_event_t* e) {
s_ui->setTapWake(lv_obj_has_state((lv_obj_t*)lv_event_get_target(e), LV_STATE_CHECKED));
}
@@ -158,7 +166,9 @@ void UITask::buildSchemaSettings() {
g = group(body, "LOCK");
schemaRow(g, SETTING(auto_lock));
listRow(g, "Screen PIN", pinSet() ? "On - asked when the screen wakes" : "Off", onPinSetup, NULL);
accentRow(group(body, "LOOK")); // DeviceScreen.h
g = group(body, "LOOK");
choiceRow(g, "Theme", "Changing it restarts the device", "Dark\nLight", theme::s_light ? 1 : 0, onThemeChoice);
accentRow(g); // DeviceScreen.h
return;
}
case PG_POWER: {
@@ -39,8 +39,8 @@ static void styleCell(int i, bool sel) {
const bool hl = i == s_hl;
lv_obj_set_style_bg_color(s_cells[i], lv_color_hex(hl ? theme::ACCENT : theme::SURFACE), 0);
lv_obj_set_style_border_width(s_cells[i], sel ? 2 : 0, 0);
setTextColor(lv_obj_get_child(s_cells[i], 0), hl ? theme::BG : theme::TEXT);
setTextColor(lv_obj_get_child(s_cells[i], 1), hl ? theme::BG : theme::TEXT_MUTED);
setTextColor(lv_obj_get_child(s_cells[i], 0), hl ? theme::ON_ACCENT : theme::TEXT);
setTextColor(lv_obj_get_child(s_cells[i], 1), hl ? theme::ON_ACCENT : theme::TEXT_MUTED);
}
// Moves the "sounding" light to note `idx` (-1: none).
@@ -462,7 +462,7 @@ static void usbPanel(const char* title, const char* text, bool buttons) {
lv_obj_set_style_radius(b, theme::RADIUS, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(i ? theme::ACCENT : theme::SURFACE_2), 0);
lv_obj_add_event_cb(b, onUsbTap, LV_EVENT_CLICKED, i ? (void*)1 : nullptr);
lv_obj_center(label(b, i ? "USB drive" : "Keep card", THEME_FONT_BODY, i ? theme::BG : theme::TEXT));
lv_obj_center(label(b, i ? "USB drive" : "Keep card", THEME_FONT_BODY, i ? theme::ON_ACCENT : theme::TEXT));
}
}
+35 -18
View File
@@ -4,25 +4,31 @@
//
// The accent (amber by default; Settings > Display > Accent colour)
// on near-black, soft corners. The accent means one of three things:
// - ACCENT fill, dark text: the primary action of a screen or popup
// - ACCENT fill, ON_ACCENT text: the primary action of a screen or popup
// (Download, Install, Save, Go) -- stylePrimary() in UITask.cpp -- and
// something selected / on (a tab, a chip, a segment, a mode that is
// running, own bubbles) -- styleSelected();
// - ACCENT text or icon: a name, a count, a background mode, a value.
// No accent mixed toward black for "selected": it came out brown.
// Everything else is BG / SURFACE / SURFACE_2 with TEXT or TEXT_MUTED.
//
// Two themes, dark (the default) and light (Settings > Display > Theme). The
// colours are set once, before anything is built (setLight()), so changing
// the theme restarts the device.
#include <src/themes/lv_theme_private.h> // lv_theme_t's fields, to layer a theme
namespace theme {
// Colours (0xRRGGBB)
static const uint32_t BG = 0x0B0B0D; // screen background
static const uint32_t SURFACE = 0x1A1A1F; // cards, rows, bubbles (incoming)
static const uint32_t SURFACE_2 = 0x27272E; // pressed / raised
static uint32_t ACCENT = 0xFFB000; // primary actions, names, badges, modes (setAccent)
static const uint32_t TEXT = 0xF2EEE6;
static const uint32_t TEXT_MUTED= 0x9A958C;
static const uint32_t OK = 0x6FCF6F; // delivered / relayed
static const uint32_t FAIL = 0xE5534B; // not delivered
// Colours (0xRRGGBB), the dark theme's; setLight() sets the light one's.
static uint32_t BG = 0x0B0B0D; // screen background
static uint32_t SURFACE = 0x1A1A1F; // cards, rows, bubbles (incoming)
static uint32_t SURFACE_2 = 0x27272E; // pressed / raised
static uint32_t ACCENT = 0xFFB000; // primary actions, names, badges, modes (setAccent)
static uint32_t ON_ACCENT = 0x0B0B0D; // text and icons on an ACCENT fill
static uint32_t TEXT = 0xF2EEE6;
static uint32_t TEXT_MUTED= 0x9A958C;
static uint32_t OK = 0x6FCF6F; // delivered / relayed
static uint32_t FAIL = 0xE5534B; // not delivered
static bool s_light = false;
// Spacing (px)
static const int PAD = 8;
@@ -67,11 +73,12 @@ namespace theme {
#define UI_SYMBOL_SUN "\xEF\x86\x85" // U+F185, quick panel: brightness
#define UI_SYMBOL_BULB "\xEF\x83\xAB" // U+F0EB, Flashlight
// Accent choices; the fill is the colour at 42% over the background.
struct Accent { const char* name; uint32_t col; };
// Accent choices: on the dark theme, and deeper on the light one, to read
// as text on white and take white text on it.
struct Accent { const char* name; uint32_t col, light; };
static const Accent ACCENTS[] = {
{ "Amber", 0xFFB000 }, { "Orange", 0xFF7A2E }, { "Coral", 0xFF6B7A },
{ "Violet", 0xB18CFF }, { "Cyan", 0x3CC8E8 }, { "Lime", 0xB5E04A },
{ "Amber", 0xFFB000, 0xC77800 }, { "Orange", 0xFF7A2E, 0xD9561A }, { "Coral", 0xFF6B7A, 0xD9455A },
{ "Violet", 0xB18CFF, 0x7A55E0 }, { "Cyan", 0x3CC8E8, 0x0E8AA8 }, { "Lime", 0xB5E04A, 0x5E8A12 },
};
static const int ACCENT_COUNT = sizeof(ACCENTS) / sizeof(ACCENTS[0]);
static int s_accent = 0;
@@ -88,11 +95,21 @@ namespace theme {
static void setAccent(int idx) {
if (idx < 0 || idx >= ACCENT_COUNT) idx = 0;
s_accent = idx;
ACCENT = ACCENTS[idx].col;
ACCENT = s_light ? ACCENTS[idx].light : ACCENTS[idx].col;
}
// Before anything is built (the colours are copied into what is).
static void setLight(bool on) {
s_light = on;
if (on) {
BG = 0xF2F0EC; SURFACE = 0xFFFFFF; SURFACE_2 = 0xE2DFD9;
ON_ACCENT = 0xFFFFFF; TEXT = 0x1C1B19; TEXT_MUTED = 0x6E6A62;
OK = 0x2E9A3E; FAIL = 0xD23A32;
}
setAccent(s_accent);
}
// Over LVGL's default theme, for every widget: a checked button is
// "selected" (the accent, dark text), buttons
// "selected" (the accent, ON_ACCENT text), buttons
// have the card radius and give a little under the finger (the default
// theme's transitions animate it both ways). Scrollbars are thin and hug
// the edge, inside the padding every panel and list has, instead of the
@@ -120,7 +137,7 @@ namespace theme {
lv_style_set_transform_width(&s_press, -2);
lv_style_set_transform_height(&s_press, -2);
lv_style_init(&s_checked);
lv_style_set_text_color(&s_checked, lv_color_hex(BG));
lv_style_set_text_color(&s_checked, lv_color_hex(ON_ACCENT));
lv_style_init(&s_sbar);
lv_style_set_width(&s_sbar, 3);
lv_style_set_pad_right(&s_sbar, 2); // vertical bar: 2 px off the edge
@@ -129,7 +146,7 @@ namespace theme {
lv_style_set_pad_left(&s_sbar, RADIUS / 2);
}
lv_style_set_bg_color(&s_checked, lv_color_hex(ACCENT));
lv_theme_t* base = lv_theme_default_init(disp, lv_color_hex(ACCENT), lv_color_hex(ACCENT), true, THEME_FONT_BODY);
lv_theme_t* base = lv_theme_default_init(disp, lv_color_hex(ACCENT), lv_color_hex(ACCENT), !s_light, THEME_FONT_BODY);
s_theme = *base;
lv_theme_set_parent(&s_theme, base);
lv_theme_set_apply_cb(&s_theme, applyTheme);
@@ -289,9 +289,9 @@ static void msgLocButton(lv_obj_t* parent, const char* text, bool save, bool acc
lv_obj_set_style_radius(b, theme::RADIUS, 0);
lv_obj_set_style_shadow_width(b, 0, 0);
// on our own amber bubble the main one goes dark instead
lv_obj_set_style_bg_color(b, lv_color_hex(accent ? (own ? theme::BG : theme::ACCENT) : theme::SURFACE_2), 0);
lv_obj_set_style_bg_color(b, lv_color_hex(accent ? (own ? theme::ON_ACCENT : theme::ACCENT) : theme::SURFACE_2), 0);
lv_obj_add_event_cb(b, onMsgLoc, LV_EVENT_CLICKED, (void*)(uintptr_t)(save ? 1 : 0));
lv_obj_center(label(b, text, THEME_FONT_SMALL, accent ? (own ? theme::ACCENT : theme::BG) : theme::TEXT));
lv_obj_center(label(b, text, THEME_FONT_SMALL, accent ? (own ? theme::ACCENT : theme::ON_ACCENT) : theme::TEXT));
}
// A message's text, wrapped at `max_w` and shrunk to fit. "@[nick]" mentions
@@ -300,7 +300,7 @@ static void msgLocButton(lv_obj_t* parent, const char* text, bool save, bool acc
// ours (*mentions_me set). Plain text stays a label.
static lv_obj_t* msgText(lv_obj_t* parent, const char* text, bool own, int max_w, bool* mentions_me) {
*mentions_me = false;
const uint32_t col = own ? theme::BG : theme::TEXT;
const uint32_t col = own ? theme::ON_ACCENT : theme::TEXT;
if (!strstr(text, "@[")) {
lv_obj_t* t = label(parent, text, THEME_FONT_BODY, col);
lv_label_set_long_mode(t, LV_LABEL_LONG_WRAP);
@@ -337,7 +337,7 @@ static lv_obj_t* msgText(lv_obj_t* parent, const char* text, bool own, int max_w
lv_span_t* sp = lv_spangroup_new_span(sg);
lv_span_set_text(sp, part);
lv_style_t* st = lv_span_get_style(sp);
lv_style_set_text_color(st, lv_color_hex(own ? theme::BG : theme::ACCENT));
lv_style_set_text_color(st, lv_color_hex(own ? theme::ON_ACCENT : theme::ACCENT));
if (mine) { lv_style_set_text_decor(st, LV_TEXT_DECOR_UNDERLINE); *mentions_me = true; }
p = close + 1;
}
@@ -377,7 +377,7 @@ static lv_obj_t* bubble(lv_obj_t* list, const char* from, const char* text, bool
lv_obj_set_size(b, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_max_width(b, 250, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(own ? theme::ACCENT : theme::SURFACE), 0);
const uint32_t muted = own ? theme::mix(theme::BG, theme::ACCENT, 60) : theme::TEXT_MUTED;
const uint32_t muted = own ? theme::mix(theme::ON_ACCENT, theme::ACCENT, 60) : theme::TEXT_MUTED;
lv_obj_set_style_border_width(b, 0, 0);
lv_obj_set_style_radius(b, theme::RADIUS, 0);
lv_obj_set_style_pad_all(b, 6, 0);
@@ -394,7 +394,7 @@ static lv_obj_t* bubble(lv_obj_t* list, const char* from, const char* text, bool
if (from && from[0]) {
lv_obj_t* hdr = flexBox(b, LV_FLEX_FLOW_ROW);
lv_obj_set_style_pad_column(hdr, 8, 0);
label(hdr, from, THEME_FONT_SMALL, own ? theme::BG : theme::ACCENT); // names are <= 31 chars: fits the bubble
label(hdr, from, THEME_FONT_SMALL, own ? theme::ON_ACCENT : theme::ACCENT); // names are <= 31 chars: fits the bubble
if (meta_in_header) *age_out = label(hdr, meta, THEME_FONT_SMALL, muted);
}
bool mentions_me;
@@ -427,11 +427,11 @@ static lv_obj_t* bubble(lv_obj_t* list, const char* from, const char* text, bool
lv_obj_set_style_bg_opa(c, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(c, lv_color_hex(theme::OK), 0);
lv_obj_set_style_radius(c, LV_RADIUS_CIRCLE, 0);
lv_obj_t* n = label(c, "", THEME_FONT_SMALL, theme::BG);
lv_obj_t* n = label(c, "", THEME_FONT_SMALL, theme::ON_ACCENT);
lv_label_set_text_fmt(n, "%d", relays);
lv_obj_center(n);
} else if (status && status[0]) {
label(line, status, THEME_FONT_SMALL, own ? theme::BG : status_col);
label(line, status, THEME_FONT_SMALL, own ? theme::ON_ACCENT : status_col);
}
}
return row;
@@ -177,6 +177,7 @@ void UITask::begin(DisplayDriver* display_drv, SensorManager* sensors, NodePrefs
return;
}
theme::setAccent(lvport::loadAccent());
theme::setLight(lvport::loadLight());
theme::install(lv_display_get_default());
lv_display_add_event_cb(lv_display_get_default(), [](lv_event_t*) { rowsFitPending(); }, LV_EVENT_REFR_START, NULL);
+7 -7
View File
@@ -972,7 +972,7 @@ static lv_obj_t* segmented(lv_obj_t* parent, const char** map, int sel, int w, i
lv_obj_set_style_bg_color(m, lv_color_hex(item), LV_PART_ITEMS);
lv_obj_set_style_bg_color(m, lv_color_hex(theme::ACCENT), LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_set_style_text_color(m, lv_color_hex(theme::TEXT), LV_PART_ITEMS);
lv_obj_set_style_text_color(m, lv_color_hex(theme::BG), LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_set_style_text_color(m, lv_color_hex(theme::ON_ACCENT), LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_set_style_text_font(m, THEME_FONT_SMALL, LV_PART_ITEMS);
lv_obj_set_style_shadow_width(m, 0, LV_PART_ITEMS);
lv_obj_set_style_radius(m, theme::RADIUS, LV_PART_ITEMS);
@@ -1016,16 +1016,16 @@ static void onByteCapInsert(lv_event_t* e) {
if (ins && strlen(lv_textarea_get_text(ta)) + strlen(ins) > cap) lv_textarea_set_insert_replace(ta, "");
}
// The primary action of a screen or popup: full amber, dark text (Theme.h);
// The primary action of a screen or popup: full accent, ON_ACCENT text (Theme.h);
// call after its label is made.
static void stylePrimary(lv_obj_t* b) {
lv_obj_set_style_bg_color(b, lv_color_hex(theme::ACCENT), 0);
for (uint32_t i = 0; i < lv_obj_get_child_count(b); i++)
lv_obj_set_style_text_color(lv_obj_get_child(b, i), lv_color_hex(theme::BG), 0);
lv_obj_set_style_text_color(lv_obj_get_child(b, i), lv_color_hex(theme::ON_ACCENT), 0);
}
// Something selected / on (a tab, a chip, a row): the accent with dark text,
// or back to the surface with light text. Every label under it follows.
// Something selected / on (a tab, a chip, a row): the accent with ON_ACCENT
// text, or back to the surface with TEXT. Every label under it follows.
static void textUnder(lv_obj_t* o, uint32_t col) {
for (uint32_t i = 0; i < lv_obj_get_child_count(o); i++) {
lv_obj_t* c = lv_obj_get_child(o, i);
@@ -1036,7 +1036,7 @@ static void textUnder(lv_obj_t* o, uint32_t col) {
static void styleSelected(lv_obj_t* b, bool on) {
lv_obj_set_style_bg_color(b, lv_color_hex(on ? theme::ACCENT : theme::SURFACE), 0);
lv_obj_set_style_bg_color(b, lv_color_hex(on ? theme::mix(theme::ACCENT, theme::BG, 75) : theme::SURFACE_2), LV_STATE_PRESSED);
textUnder(b, on ? theme::BG : theme::TEXT);
textUnder(b, on ? theme::ON_ACCENT : theme::TEXT);
}
// A row of equal buttons at the foot of a popup or screen: buttonBar(), then
@@ -1137,7 +1137,7 @@ static void badge(lv_obj_t* parent, int n, bool overflow) {
lv_obj_set_style_pad_hor(b, 7, 0);
lv_obj_set_style_pad_ver(b, 0, 0);
lv_obj_align(b, LV_ALIGN_RIGHT_MID, -theme::PAD, 0);
lv_obj_t* l = label(b, "", THEME_FONT_SMALL, theme::BG);
lv_obj_t* l = label(b, "", THEME_FONT_SMALL, theme::ON_ACCENT);
lv_label_set_text_fmt(l, "%d%s", n, overflow ? "+" : "");
lv_obj_center(l);
}