v3 M1b: runtime per-provider theming (firmware) + accent push (daemon)
The watch recolors to the active provider's brand, live, and it's cheap to add more providers later. Firmware: - theme.h: expose THEME_ACCENT_HEX; ui.cpp: COL_ACCENT becomes a runtime lv_color_t backed by a shared style (s_accent_style). Static accent widgets (launcher tile icons, usage status line, session tokens) attach the shared style, so ui_set_theme() recolors them instantly via lv_obj_report_style_change — no widget tracking, no recreation. - ui_set_theme(pv, accent_rgb): swaps the accent + a small logo table keyed by provider id (fallback to the Claude mark until a provider ships a logo). Change- guarded, so the ~3s payload cadence doesn't churn. - main.cpp: parse "pv" (id) + "ac" (0xRRGGBB) from the payload and apply. - Adding a provider needs zero firmware color edits (accent is data-driven) and at most one logo-table row. Daemon: push "ac" = active provider's brand accent alongside "pv". Test fix: test_providers used asyncio.run(), which closed the suite's shared event loop and broke every later get_event_loop() test (Py3.13) — mirror the repo's _run() helper instead. Verified on the 2.06: forcing an OpenAI-green theme recolored the live launcher icons (report_style_change path). 216 builds clean; daemon suite 94 passed.
This commit is contained in:
+53
-4
@@ -100,7 +100,13 @@ static void compute_layout(const BoardCaps& c) {
|
||||
#define COL_PANEL THEME_PANEL
|
||||
#define COL_TEXT THEME_TEXT
|
||||
#define COL_DIM THEME_DIM
|
||||
#define COL_ACCENT THEME_ACCENT
|
||||
// COL_ACCENT is runtime (v3): the daemon pushes each provider's brand accent and
|
||||
// ui_set_theme() swaps it live. Widgets that read COL_ACCENT directly pick up the
|
||||
// new value on their next update; static widgets attach s_accent_style so a
|
||||
// provider switch recolors them instantly (lv_obj_report_style_change).
|
||||
static lv_color_t g_accent = lv_color_hex(THEME_ACCENT_HEX);
|
||||
static lv_style_t s_accent_style;
|
||||
#define COL_ACCENT g_accent
|
||||
#define COL_GREEN THEME_GREEN
|
||||
#define COL_AMBER THEME_AMBER
|
||||
#define COL_RED THEME_RED
|
||||
@@ -477,7 +483,7 @@ static void init_usage_screen(lv_obj_t* scr) {
|
||||
lbl_anim = lv_label_create(usage_container);
|
||||
lv_label_set_text(lbl_anim, "");
|
||||
lv_obj_set_style_text_font(lbl_anim, &font_mono_32, 0);
|
||||
lv_obj_set_style_text_color(lbl_anim, COL_ACCENT, 0);
|
||||
lv_obj_add_style(lbl_anim, &s_accent_style, 0); // v3: recolors on provider switch
|
||||
lv_obj_align(lbl_anim, LV_ALIGN_BOTTOM_MID, 0, -15);
|
||||
}
|
||||
|
||||
@@ -516,7 +522,7 @@ static lv_obj_t* make_tile(lv_obj_t* parent, const AppEntry* app, int w, int h)
|
||||
lv_obj_t* icon = lv_label_create(tile);
|
||||
lv_label_set_text(icon, app->symbol);
|
||||
lv_obj_set_style_text_font(icon, &lv_font_montserrat_28, 0);
|
||||
lv_obj_set_style_text_color(icon, COL_ACCENT, 0);
|
||||
lv_obj_add_style(icon, &s_accent_style, 0); // v3: recolors on provider switch
|
||||
|
||||
lv_obj_t* lbl = lv_label_create(tile);
|
||||
lv_label_set_text(lbl, app->label);
|
||||
@@ -712,7 +718,7 @@ static void init_session_screen(lv_obj_t* scr) {
|
||||
sess_tokens_lbl = lv_label_create(session_container);
|
||||
lv_label_set_text(sess_tokens_lbl, "\xE2\x80\x94 tokens");
|
||||
lv_obj_set_style_text_font(sess_tokens_lbl, &font_styrene_28, 0);
|
||||
lv_obj_set_style_text_color(sess_tokens_lbl, COL_ACCENT, 0);
|
||||
lv_obj_add_style(sess_tokens_lbl, &s_accent_style, 0); // v3: recolors on provider switch
|
||||
lv_obj_align(sess_tokens_lbl, LV_ALIGN_TOP_MID, 0, L.content_y + 152);
|
||||
|
||||
sess_gen_lbl = lv_label_create(session_container);
|
||||
@@ -1282,6 +1288,11 @@ static void battery_screen_refresh(void) {
|
||||
void ui_init(void) {
|
||||
compute_layout(board_caps());
|
||||
|
||||
// Shared accent style (v3 theming) — MUST be initialized before any screen is
|
||||
// built, so widgets can attach it. ui_set_theme() recolors it live.
|
||||
lv_style_init(&s_accent_style);
|
||||
lv_style_set_text_color(&s_accent_style, g_accent);
|
||||
|
||||
lv_obj_t* scr = lv_screen_active();
|
||||
lv_obj_set_style_bg_color(scr, COL_BG, 0);
|
||||
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
|
||||
@@ -1584,6 +1595,44 @@ screen_t ui_get_current_screen(void) {
|
||||
return current_screen;
|
||||
}
|
||||
|
||||
// ---- v3 provider theming --------------------------------------------------
|
||||
// Per-provider logo on the usage screen. Only the Claude mark ships today; adding
|
||||
// a provider's logo is one row here plus an RGB565A8 asset. Unknown / not-yet-
|
||||
// added providers fall back to the Claude mark, so a switch still recolors.
|
||||
struct ProviderLogo { const char* pv; const lv_image_dsc_t* dsc; };
|
||||
static const ProviderLogo s_provider_logos[] = {
|
||||
{ "anthropic", &logo_dsc },
|
||||
// { "openai", &logo_openai_dsc }, // add when the asset lands
|
||||
// { "zai", &logo_zai_dsc },
|
||||
};
|
||||
static const lv_image_dsc_t* logo_for(const char* pv) {
|
||||
if (pv)
|
||||
for (const auto& e : s_provider_logos)
|
||||
if (strcmp(e.pv, pv) == 0) return e.dsc;
|
||||
return &logo_dsc; // fallback: Claude mark until the provider ships a logo
|
||||
}
|
||||
|
||||
// Apply a provider's brand: recolor the shared accent style (every widget using
|
||||
// it repaints via report_style_change), retint the few non-text accent widgets,
|
||||
// and swap the logo. accent_rgb == 0 keeps the current accent (logo-only change).
|
||||
void ui_set_theme(const char* pv, uint32_t accent_rgb) {
|
||||
// Change-guarded: the daemon stamps pv/ac on every write (~3s), so only act
|
||||
// on an actual switch — otherwise report_style_change would churn constantly.
|
||||
static uint32_t last_accent = THEME_ACCENT_HEX;
|
||||
static char last_pv[16] = "";
|
||||
if (accent_rgb && accent_rgb != last_accent) {
|
||||
last_accent = accent_rgb;
|
||||
g_accent = lv_color_hex(accent_rgb);
|
||||
lv_style_set_text_color(&s_accent_style, g_accent);
|
||||
lv_obj_report_style_change(&s_accent_style);
|
||||
if (dim_arc) lv_obj_set_style_arc_color(dim_arc, g_accent, LV_PART_INDICATOR);
|
||||
}
|
||||
if (pv && strncmp(pv, last_pv, sizeof(last_pv)) != 0) {
|
||||
strlcpy(last_pv, pv, sizeof(last_pv));
|
||||
if (logo_img) lv_image_set_src(logo_img, logo_for(pv));
|
||||
}
|
||||
}
|
||||
|
||||
void ui_update_ble_status(ble_state_t state, const char* name, const char* mac) {
|
||||
(void)name; (void)mac;
|
||||
bool was_connected = s_ble_connected;
|
||||
|
||||
Reference in New Issue
Block a user