mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-02 10:51:57 +00:00
ACCENT_DIM (the accent mixed toward black) came out brown on selected tabs, chips, segments and own bubbles. They take the accent with dark text now, as primary actions and the quick panel tiles do: styleSelected() sets the fill, the pressed shade and every label under it. Own bubbles: dark text, age and delivery mark (green or red can't be read on amber); the melody editor's selected note is outlined, the sounding one filled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
134 lines
6.4 KiB
C++
134 lines
6.4 KiB
C++
#pragma once
|
|
// Design tokens for ui-lvgl -- the one place colours, spacing and type live --
|
|
// and the rules every widget follows (install() below).
|
|
//
|
|
// 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
|
|
// (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.
|
|
#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
|
|
|
|
// Spacing (px)
|
|
static const int PAD = 8;
|
|
static const int GAP = 6;
|
|
static const int RADIUS = 10; // cards, rows, buttons (pills use LV_RADIUS_CIRCLE)
|
|
static const int RADIUS_SM = 6; // small cells inside those
|
|
static const int STATUS_H = 22; // top status bar
|
|
static const int ROW_H = 44; // list row (touch target)
|
|
|
|
// Type -- Noto Sans, generated by fonts/generate.sh (declared in lv_conf.h).
|
|
// ui_font_40 holds digits / ':' / '-' / '.' only (clock, timer, stopwatch).
|
|
#define THEME_FONT_SMALL (&ui_font_12)
|
|
#define THEME_FONT_BODY (&ui_font_14)
|
|
#define THEME_FONT_TITLE (&ui_font_16)
|
|
#define THEME_FONT_LARGE (&ui_font_20)
|
|
#define THEME_FONT_CLOCK (&ui_font_40)
|
|
// Status bar icons, refitted to one size (fonts/status_icons.py); any other
|
|
// character falls back to ui_font_12.
|
|
#define THEME_FONT_ICONS (&ui_icons_14)
|
|
|
|
// Extra FontAwesome glyphs baked into ui_font_12..20 beyond LVGL's LV_SYMBOL_*.
|
|
#define UI_SYMBOL_STAR "\xEF\x80\x85" // U+F005, favourite
|
|
#define UI_SYMBOL_MAP "\xEF\x89\xB9" // U+F279
|
|
#define UI_SYMBOL_USERS "\xEF\x83\x80" // U+F0C0, Nearby
|
|
#define UI_SYMBOL_COMPASS "\xEF\x85\x8E" // U+F14E, Navigate
|
|
#define UI_SYMBOL_FLAG "\xEF\x80\xA4" // U+F024, waypoint
|
|
#define UI_SYMBOL_PIN "\xEF\x8F\x85" // U+F3C5, mark here (a place on the map)
|
|
#define UI_SYMBOL_TACK "\xEF\x82\x8D" // U+F08D, thumbtack: pin to the Home dial
|
|
#define UI_SYMBOL_CLOCK "\xEF\x80\x97" // U+F017, Clock tools
|
|
#define UI_SYMBOL_STOPWATCH "\xEF\x8B\xB2" // U+F2F2
|
|
#define UI_SYMBOL_RADIO "\xEF\x94\x99" // U+F519, broadcast tower
|
|
#define UI_SYMBOL_CHART "\xEF\x82\x80" // U+F080, diagnostics
|
|
#define UI_SYMBOL_KEY "\xEF\x82\x84" // U+F084, admin
|
|
#define UI_SYMBOL_ROUTE "\xEF\x93\x97" // U+F4D7, trail
|
|
#define UI_SYMBOL_MUTE "\xEF\x9A\xA9" // U+F6A9, muted (speaker with a cross)
|
|
#define UI_SYMBOL_LOCK "\xEF\x80\xA3" // U+F023, quick panel: lock the screen
|
|
#define UI_SYMBOL_SUN "\xEF\x86\x85" // U+F185, quick panel: brightness
|
|
|
|
// Accent choices; the fill is the colour at 42% over the background.
|
|
struct Accent { const char* name; uint32_t col; };
|
|
static const Accent ACCENTS[] = {
|
|
{ "Amber", 0xFFB000 }, { "Orange", 0xFF7A2E }, { "Coral", 0xFF6B7A },
|
|
{ "Violet", 0xB18CFF }, { "Cyan", 0x3CC8E8 }, { "Lime", 0xB5E04A },
|
|
};
|
|
static const int ACCENT_COUNT = sizeof(ACCENTS) / sizeof(ACCENTS[0]);
|
|
static int s_accent = 0;
|
|
// `fg` at `pct` % over `bg`, worked out once: an opaque fill in the colour
|
|
// a translucent one would blend to, without blending it every frame.
|
|
static uint32_t mix(uint32_t fg, uint32_t bg, uint32_t pct) {
|
|
uint32_t out = 0;
|
|
for (int sh = 0; sh <= 16; sh += 8) {
|
|
uint32_t c = (fg >> sh) & 0xFF, b = (bg >> sh) & 0xFF;
|
|
out |= ((c * pct + b * (100 - pct)) / 100) << sh;
|
|
}
|
|
return out;
|
|
}
|
|
static void setAccent(int idx) {
|
|
if (idx < 0 || idx >= ACCENT_COUNT) idx = 0;
|
|
s_accent = idx;
|
|
ACCENT = ACCENTS[idx].col;
|
|
}
|
|
|
|
// Over LVGL's default theme, for every widget: a checked button is
|
|
// "selected" (the accent, dark 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
|
|
// default's 5 px bar 7 px in, which ran over right-aligned text.
|
|
static lv_theme_t s_theme;
|
|
static lv_style_t s_btn, s_press, s_checked, s_sbar;
|
|
static void applyTheme(lv_theme_t* th, lv_obj_t* obj) {
|
|
(void)th;
|
|
lv_obj_add_style(obj, &s_sbar, LV_PART_SCROLLBAR);
|
|
if (lv_obj_check_type(obj, &lv_button_class)) {
|
|
lv_obj_add_style(obj, &s_btn, 0);
|
|
lv_obj_add_style(obj, &s_press, LV_STATE_PRESSED);
|
|
lv_obj_add_style(obj, &s_checked, LV_STATE_CHECKED);
|
|
}
|
|
}
|
|
// (Re)builds the default theme in the current accent with this layer over it.
|
|
static void install(lv_display_t* disp) {
|
|
static bool styled = false;
|
|
if (!styled) {
|
|
styled = true;
|
|
lv_style_init(&s_btn);
|
|
lv_style_set_radius(&s_btn, RADIUS);
|
|
lv_style_set_shadow_width(&s_btn, 0);
|
|
lv_style_init(&s_press);
|
|
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_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
|
|
lv_style_set_pad_bottom(&s_sbar, 2); // horizontal bar likewise
|
|
lv_style_set_pad_top(&s_sbar, RADIUS / 2);
|
|
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);
|
|
s_theme = *base;
|
|
lv_theme_set_parent(&s_theme, base);
|
|
lv_theme_set_apply_cb(&s_theme, applyTheme);
|
|
lv_display_set_theme(disp, &s_theme);
|
|
lv_obj_report_style_change(&s_checked);
|
|
}
|
|
}
|