d60a0f122b
An alert from esp_screens_show_alert takes a `camera` (a camera.* or image.* entity): a Guition with firmware 0.2.57 shows that moment's picture across the top of the card, and a tap opens the camera full screen. A camera or image tile opens the same full-screen view, refreshed every four seconds at a steady pace. ESP Screen Manager fetches the snapshot with its own access, sizes it for the screen and serves a 24-bit BMP under a random, short-lived link on port 8098; the screen loads it with ESPHome's online_image and holds no Home Assistant token. BMP because ESPHome decodes it per chunk, where a full-screen JPEG held the loop 0.6 s. The CYD keeps its alerts without the picture and gets no camera tiles. The Guition also builds with ESPHome 2026.8+ again: backlight_fade.h derived from ESPHome's LEDC output, which 2026.8 made final, so the backlight now dims through ESPHome's own light transition. Tested on the bench boards and a local add-on on the Home Assistant Yellow; docs/TEST_RESULTS_0266.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
358 lines
19 KiB
C++
358 lines
19 KiB
C++
#pragma once
|
|
// Every colour the screens choose themselves, in one table (firmware 0.2.54+).
|
|
//
|
|
// A role is what a colour is for: the page, a card, the text on it, a key under a finger. Each role has its light value,
|
|
// the look the screens have always had, and its dark value, for a screen beside a bed: a black page, graphite cards,
|
|
// soft white text, and the colours of Home Assistant's states where they matter. Nothing else in the firmware or in the
|
|
// board profiles writes a colour of its own; the profiles name a paint (a shared LVGL style filled from this table) and
|
|
// the firmware asks for a role. docs/THEME.md is the recipe for a new colour.
|
|
//
|
|
// Home Assistant's state colours (the amber of a light that is on, the orange of heating) are not roles: they are data
|
|
// and pass through state() on their way to the glass. The values below are free of LVGL, so tests/test_theme.cpp checks
|
|
// the table and the contrast of both looks on a PC; the LVGL half is behind THEME_TEST.
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
namespace theme {
|
|
|
|
enum Role : uint8_t {
|
|
// ---- surfaces
|
|
PAGE, // the page behind the tiles, every card and the settings
|
|
PAGE_SOFT, // the page of the light's colour controls, a shade lighter
|
|
PAGE_PRESSED, // the Previous and Next bars under a finger
|
|
CARD, // tiles, cards, rows, the alert
|
|
CARD_PRESSED, // a settings row under a finger
|
|
LINE, // the hairline around a card
|
|
RAISED, // a card above everything else: the alert
|
|
RAISED_LINE,
|
|
KEY, // round keys (back, close), a white key under a finger
|
|
KEY_PRESSED, // a round key under a finger
|
|
TRACK, // segment tracks and grey keys inside a card, an unavailable tile's circle, empty history
|
|
GRID, // the lines across a history graph
|
|
TICK, // the small marks under a history graph
|
|
BUTTON, // the plain keys of a simple card (select options, media, timer)
|
|
SETTING_KEY, // -/+ keys and choice chips on the settings page
|
|
SETTING_KEY_PRESSED,
|
|
TOGGLE_OFF, // a switch on the settings page while off
|
|
SWITCH_OFF, // the switch on a history card while off
|
|
PANEL_TOGGLE_OFF, // the toggle on a wide tile while off
|
|
PANEL_TOGGLE_OFF_PRESSED,
|
|
KNOB, // handles and knobs
|
|
SLIDER_KNOB, // the round knob of a card's number or volume slider
|
|
SCRIM, // behind an alert
|
|
VEIL, // the sheet over a busy tile, the haze over the value card
|
|
SPINNER_TRACK, // the ring of the busy spinner
|
|
// ---- text
|
|
INK, // names, values, words on keys
|
|
INK_SOFT, // the alert's second line
|
|
MUTED, // secondary words: states, times beside a value
|
|
SUBTLE, // quiet words: axis labels, notes, "Target"
|
|
SLATE, // the top bar, units, a value on a coloured card, mode key icons
|
|
ROW_ICON, // icons at the start of a settings row
|
|
CHEVRON, // the arrow at the end of a settings row
|
|
OFF, // something that is off: its icon, its circle and the fill of its slider
|
|
ON_ACCENT, // words and icons on a blue key and on the alert's key
|
|
// ---- keys that stand out
|
|
ACCENT, // the screen's own blue: a chosen segment, the main key, a switch that is on
|
|
ACCENT_PRESSED,
|
|
ACCENT_BRIGHT, // the value card's fill and a chosen climate chip
|
|
ACCENT_TINT, // pale blue: a choice under a finger, the value card's track, climate power on
|
|
ACCENT_ICON, // the climate power icon while on
|
|
ICON_OFF, // the climate power icon while off
|
|
BUTTON_DARK, // the alert's key
|
|
BUTTON_DARK_PRESSED,
|
|
// ---- drawings
|
|
ROBOT_BODY, // the vacuum seen from above
|
|
ROBOT_RIM,
|
|
ROBOT_TOP,
|
|
ROBOT_LENS,
|
|
BATTERY, // a battery's outline and cap
|
|
SUN_PATH, // the arc the sun travels
|
|
MOON, // the sun below the horizon
|
|
AMBER_TRACK, // the track of a light's brightness slider
|
|
HISTORY_OFF, // an off or away stretch on a timeline (the add-on's CFCFCF)
|
|
HISTORY_EMPTY, // a stretch without data on a timeline (the add-on's E6E6E6)
|
|
// ---- a camera image: dark in both looks, like a photo viewer (firmware 0.2.57+)
|
|
CAMERA_PAGE, // behind the image full screen and in the alert's image frame
|
|
CAMERA_INK, // the camera's name over the image
|
|
CAMERA_NOTE, // "Loading image", "No image from this camera"
|
|
// ---- the CYD's touch calibration: dark in both looks, a yellow crosshair
|
|
CALIBRATION_PAGE,
|
|
CALIBRATION_INK,
|
|
CALIBRATION_MARK,
|
|
ROLE_COUNT
|
|
};
|
|
|
|
struct Pair { uint32_t light, dark; };
|
|
|
|
// light: the design as it always was. dark: RGB565-friendly values (a grey whose red, green and blue land on the same
|
|
// step of the panel), so dark greys stay neutral on both boards instead of turning green.
|
|
inline constexpr Pair ROLES[ROLE_COUNT] = {
|
|
/* PAGE */ {0xE7E7E7, 0x000000},
|
|
/* PAGE_SOFT */ {0xF8F8F8, 0x101010},
|
|
/* PAGE_PRESSED */ {0xE3E3E3, 0x1A1A1A},
|
|
/* CARD */ {0xFFFFFF, 0x1A1A1A},
|
|
/* CARD_PRESSED */ {0xF1F1F1, 0x2A2A2A},
|
|
/* LINE */ {0xDDDDDD, 0x292929},
|
|
/* RAISED */ {0xFFFFFF, 0x282828},
|
|
/* RAISED_LINE */ {0xDDDDDD, 0x393939},
|
|
/* KEY */ {0xEEEEEE, 0x2A2A2A},
|
|
/* KEY_PRESSED */ {0xDDDDDD, 0x3A3A3A},
|
|
/* TRACK */ {0xF1F1F1, 0x2A2A2A},
|
|
/* GRID */ {0xEEEEEE, 0x292929},
|
|
/* TICK */ {0xCCCCCC, 0x4A4A4A},
|
|
/* BUTTON */ {0xD9E6F0, 0x222B32},
|
|
/* SETTING_KEY */ {0xE9EFF4, 0x2A2F34},
|
|
/* SETTING_KEY_PRESSED */ {0xC3D5E3, 0x3A4652},
|
|
/* TOGGLE_OFF */ {0xC4C4C4, 0x484848},
|
|
/* SWITCH_OFF */ {0xB0B0B0, 0x484848},
|
|
/* PANEL_TOGGLE_OFF */ {0xD7DADF, 0x393C41},
|
|
/* PANEL_TOGGLE_OFF_PRESSED */ {0xC5C9CF, 0x494D52},
|
|
/* KNOB */ {0xFFFFFF, 0xE8E8E8},
|
|
/* SLIDER_KNOB */ {0x111111, 0xE8E8E8},
|
|
/* SCRIM */ {0x101820, 0x000000},
|
|
/* VEIL */ {0xFFFFFF, 0x1A1A1A},
|
|
/* SPINNER_TRACK */ {0xD9DDE2, 0x393D42},
|
|
/* INK */ {0x1B1B1B, 0xDADADA},
|
|
/* INK_SOFT */ {0x3A3A3C, 0xBABABA},
|
|
/* MUTED */ {0x616161, 0x999999},
|
|
/* SUBTLE */ {0x6B6B6B, 0x8A8A8A},
|
|
/* SLATE */ {0x46525E, 0x9AA6B2},
|
|
/* ROW_ICON */ {0x4A4A4A, 0xB8B8B8},
|
|
/* CHEVRON */ {0xB0B0B0, 0x5A5A5A},
|
|
/* OFF */ {0x9E9E9E, 0x787878},
|
|
/* ON_ACCENT */ {0xFFFFFF, 0xFFFFFF},
|
|
/* ACCENT */ {0x009FE3, 0x0A93D2},
|
|
/* ACCENT_PRESSED */ {0x0075B0, 0x0A6FA3},
|
|
/* ACCENT_BRIGHT */ {0x00A6ED, 0x0A9ADB},
|
|
/* ACCENT_TINT */ {0xD5EEFC, 0x12384A},
|
|
/* ACCENT_ICON */ {0x0B6E99, 0x6CCBF5},
|
|
/* ICON_OFF */ {0x8A8A8A, 0x7A7A7A},
|
|
/* BUTTON_DARK */ {0x1B1B1B, 0x484848},
|
|
/* BUTTON_DARK_PRESSED */ {0x3A3A3C, 0x585858},
|
|
/* ROBOT_BODY */ {0xFFFFFF, 0x383D42},
|
|
/* ROBOT_RIM */ {0xCEDDE6, 0x5A6972},
|
|
/* ROBOT_TOP */ {0xE3EBEF, 0x4A5358},
|
|
/* ROBOT_LENS */ {0xA8BCC8, 0x7E93A1},
|
|
/* BATTERY */ {0x7D858D, 0x8C959D},
|
|
/* SUN_PATH */ {0xCFD8DC, 0x3E494E},
|
|
/* MOON */ {0xB0BEC5, 0x90A4AE},
|
|
/* AMBER_TRACK */ {0xFDEFC3, 0x483B16},
|
|
/* HISTORY_OFF */ {0xCFCFCF, 0x4A4A4A},
|
|
/* HISTORY_EMPTY */ {0xE6E6E6, 0x2A2A2A},
|
|
/* CAMERA_PAGE */ {0x000000, 0x000000},
|
|
/* CAMERA_INK */ {0xF2F2F2, 0xDADADA},
|
|
/* CAMERA_NOTE */ {0x9E9E9E, 0x8A8A8A},
|
|
/* CALIBRATION_PAGE */ {0x101820, 0x101820},
|
|
/* CALIBRATION_INK */ {0xFFFFFF, 0xFFFFFF},
|
|
/* CALIBRATION_MARK */ {0xFFD34D, 0xFFD34D},
|
|
};
|
|
|
|
// The look on screen. The board sets it through set_dark() (below) from the Dark mode setting; the table and the
|
|
// helpers only read it.
|
|
inline bool dark = false;
|
|
inline uint32_t hex(Role role) { return dark ? ROLES[role].dark : ROLES[role].light; }
|
|
|
|
// ---- mixing, exactly as LVGL 9 mixes two colours (lv_color_mix), so a value here is the value on the glass
|
|
inline uint32_t mix(uint32_t first, uint32_t second, uint8_t amount) {
|
|
uint32_t out = 0;
|
|
for (int shift = 16; shift >= 0; shift -= 8) {
|
|
const uint32_t a = first >> shift & 0xFF, b = second >> shift & 0xFF;
|
|
out |= (((a * amount + b * (255 - amount)) * 0x8081U) >> 23) << shift;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// ---- named card colours: what a tile's background or an alert's colour can be
|
|
// The add-on sends a name; a tile keeps the light value as the colour's identity (tile_palette.h), and surface() turns it
|
|
// into what this look draws. Light: the pastels chosen for dark text. Dark: the same hue deep enough for light text.
|
|
struct Swatch { const char *name; uint32_t light, dark; };
|
|
inline constexpr Swatch SWATCHES[] = {
|
|
{"red", 0xFADADD, 0x4A2422},
|
|
{"orange", 0xFFE1C6, 0x472F17},
|
|
{"yellow", 0xFFF0C2, 0x3E3516},
|
|
{"green", 0xD9EEDC, 0x243A26},
|
|
{"mint", 0xD5F0EA, 0x163631},
|
|
{"blue", 0xD9EAFB, 0x1C3448},
|
|
{"purple", 0xE9DDF5, 0x342C40},
|
|
{"pink", 0xF7DDEC, 0x46202E},
|
|
{"gray", 0xE5E7EB, 0x2A2D32},
|
|
};
|
|
inline constexpr size_t SWATCH_COUNT = sizeof(SWATCHES) / sizeof(SWATCHES[0]);
|
|
inline uint32_t swatch(const std::string &name) {
|
|
for (const auto &s : SWATCHES) if (name == s.name) return s.light;
|
|
return 0;
|
|
}
|
|
// A card's own colour (a swatch's light value, or 0 for none) as this look draws it.
|
|
inline uint32_t surface(uint32_t own) {
|
|
if (!own) return hex(CARD);
|
|
if (!dark) return own;
|
|
for (const auto &s : SWATCHES) if (s.light == own) return s.dark;
|
|
return hex(CARD);
|
|
}
|
|
// The hairline around a card: the card's own colour a little darker in light, a little lighter in dark.
|
|
inline uint32_t outline(uint32_t own) {
|
|
if (!own) return hex(LINE);
|
|
return dark ? mix(surface(own), 0xFFFFFF, 232) : mix(own, 0x000000, 220);
|
|
}
|
|
|
|
// ---- Home Assistant's state colours
|
|
// What a state looks like in Home Assistant's frontend (its --state-*-color and --*-color variables), the same in both
|
|
// looks: an amber light is amber at night too. They are data, drawn through state(), foreground(), tint() and icon().
|
|
namespace ha {
|
|
constexpr uint32_t AMBER = 0xFFC107; // a light, switch or binary sensor that is on; lux
|
|
constexpr uint32_t ORANGE = 0xFF9800; // the sun above the horizon, a paused timer or robot, drying
|
|
constexpr uint32_t DEEP_ORANGE = 0xFF6F22; // temperature, heating
|
|
constexpr uint32_t RED = 0xF44336; // a robot in trouble
|
|
constexpr uint32_t PURPLE = 0x926BC7; // covers, scenes and scripts, energy
|
|
constexpr uint32_t DEEP_PURPLE = 0x6E41AB; // a clear night, the sun below the horizon
|
|
constexpr uint32_t INDIGO = 0x3F51B5; // selects
|
|
constexpr uint32_t BLUE = 0x2196F3; // cooling, a robot on its way back, the settings card, everything else
|
|
constexpr uint32_t LIGHT_BLUE = 0x03A9F4; // media players, the weather
|
|
constexpr uint32_t CYAN = 0x00BCD4; // fans
|
|
constexpr uint32_t TEAL = 0x009688; // numbers, percentages, a running timer, a robot at work
|
|
constexpr uint32_t GREEN = 0x4CAF50; // someone at home, auto mode
|
|
constexpr uint32_t GREY = 0x9E9E9E; // anything off or away
|
|
constexpr uint32_t SKY = 0x00A6ED; // a robot that is docked or idle
|
|
constexpr uint32_t SWITCH_ON = 0xFFB900; // the switch on a history card while on
|
|
constexpr uint32_t PURPLE_PRESSED = 0x7552A8; // a chosen cover key under a finger
|
|
// The weather card's conditions and the sun card.
|
|
constexpr uint32_t SUNNY = 0xFFB300, RAIN = 0x1E88E5, SNOW = 0x4FC3F7, LIGHTNING = 0xFFA000;
|
|
constexpr uint32_t PARTLY_CLOUDY = 0x7E9BB5, CLOUDY = 0x78909C, NIGHT_SKY = 0x5C6BC0;
|
|
// Warnings and good news drawn on a card: a low battery, the analog clock's second hand; charging.
|
|
constexpr uint32_t ALARM = 0xE53935, CHARGING = 0x43A047;
|
|
} // namespace ha
|
|
|
|
// ---- Home Assistant's colours on their way to the glass
|
|
// Grey is what Home Assistant draws for something off; the add-on's timelines add a lighter grey for off and one for no
|
|
// data. In dark those greys come from the table; every other state colour stays itself.
|
|
constexpr uint32_t STATE_OFF = ha::GREY;
|
|
inline uint32_t state(uint32_t color) {
|
|
if (!dark) return color;
|
|
if (color == STATE_OFF) return ROLES[OFF].dark;
|
|
if (color == ROLES[HISTORY_OFF].light) return ROLES[HISTORY_OFF].dark;
|
|
if (color == ROLES[HISTORY_EMPTY].light) return ROLES[HISTORY_EMPTY].dark;
|
|
return color;
|
|
}
|
|
// Relative lightness 0-255 (Rec. 601 weights): enough to tell a colour that sinks into a dark card.
|
|
inline uint32_t lightness(uint32_t color) {
|
|
return ((color >> 16 & 0xFF) * 299 + (color >> 8 & 0xFF) * 587 + (color & 0xFF) * 114) / 1000;
|
|
}
|
|
// A state colour drawn as a glyph, a line or words on the page or a card. Dark lifts the deep ones (indigo, purple)
|
|
// until they read on graphite.
|
|
inline uint32_t foreground(uint32_t color) {
|
|
color = state(color);
|
|
if (!dark) return color;
|
|
const uint32_t l = lightness(color);
|
|
return l >= 110 ? color : mix(color, 0xFFFFFF, static_cast<uint8_t>(255 - (110 - l) * 3 / 2));
|
|
}
|
|
// The pale circle behind a tile's icon, a halo, a slider's track: the colour at `amount` over white in light, and
|
|
// over the card in dark, where the same share would vanish, a little more.
|
|
inline uint32_t tint(uint32_t color, uint8_t amount) {
|
|
color = state(color);
|
|
if (!dark) return mix(color, 0xFFFFFF, amount);
|
|
const unsigned stronger = amount + amount / 3 + 8;
|
|
return mix(color, hex(CARD), static_cast<uint8_t>(stronger > 255 ? 255 : stronger));
|
|
}
|
|
// A tile's icon in its state colour: a touch darker in light, so a pale bulb still shows; a touch lighter in dark.
|
|
inline uint32_t icon(uint32_t color) {
|
|
color = state(color);
|
|
return dark ? mix(foreground(color), 0xFFFFFF, 230) : mix(color, 0x333333, 205);
|
|
}
|
|
// The soft area under a graph line: a fifth of the line's colour in light; less over graphite, where the same share
|
|
// turns a large brown or purple block.
|
|
inline uint8_t fill_opacity() { return dark ? 38 : 51; }
|
|
// Keys drawn inside a card of any colour (a wide tile's direct controls): a shade away from the card, towards the
|
|
// middle, so they read on white, on a pastel and on graphite alike.
|
|
inline uint32_t key_on(uint32_t card, uint8_t depth) {
|
|
return dark ? mix(card, 0xFFFFFF, static_cast<uint8_t>(255 - (255 - depth) * 3 / 2)) : mix(card, 0x000000, depth);
|
|
}
|
|
|
|
} // namespace theme
|
|
|
|
#ifndef THEME_TEST
|
|
#include "lvgl.h"
|
|
#include <array>
|
|
|
|
namespace theme {
|
|
|
|
inline lv_color_t color(Role role) { return lv_color_hex(hex(role)); }
|
|
inline lv_color_t rgb(uint32_t value) { return lv_color_hex(value); }
|
|
|
|
// ---- paints: shared styles filled from the table
|
|
// A board profile gives a widget a paint instead of a colour (`styles: paint_card`). Its style definitions carry no
|
|
// colour: the profile's `theme::paints` fills each one with fill(), at boot and after every change of look. The list
|
|
// is code, not a table in RAM, and a shared style costs a widget one style reference, about what the local colour it
|
|
// replaces cost. The firmware's own long-lived widgets take the same paints through style().
|
|
constexpr uint8_t NONE = 0xFF;
|
|
enum class Paint : uint8_t {
|
|
page, page_soft, page_pressed, card, raised, key, key_pressed, track, ink, ink_soft, muted, subtle, slate,
|
|
scrim, veil, accent_tint, accent_bright, button_dark, button_dark_pressed, knob, spinner, calibration, camera, COUNT
|
|
};
|
|
struct PaintRoles { uint8_t bg, border, text, line, arc; };
|
|
inline constexpr PaintRoles PAINTS[static_cast<size_t>(Paint::COUNT)] = {
|
|
/* page */ {PAGE, NONE, INK, NONE, NONE},
|
|
/* page_soft */ {PAGE_SOFT, NONE, INK, NONE, NONE},
|
|
/* page_pressed */ {PAGE_PRESSED, NONE, NONE, NONE, NONE},
|
|
/* card */ {CARD, LINE, INK, NONE, NONE},
|
|
/* raised */ {RAISED, RAISED_LINE, INK, NONE, NONE},
|
|
/* key */ {KEY, NONE, INK, NONE, NONE},
|
|
/* key_pressed */ {KEY_PRESSED, NONE, NONE, NONE, NONE},
|
|
/* track */ {TRACK, NONE, INK, NONE, NONE},
|
|
/* ink */ {NONE, NONE, INK, NONE, NONE},
|
|
/* ink_soft */ {NONE, NONE, INK_SOFT, NONE, NONE},
|
|
/* muted */ {NONE, NONE, MUTED, NONE, NONE},
|
|
/* subtle */ {NONE, NONE, SUBTLE, NONE, NONE},
|
|
/* slate */ {NONE, SLATE, SLATE, SLATE, NONE},
|
|
/* scrim */ {SCRIM, NONE, NONE, NONE, NONE},
|
|
/* veil */ {VEIL, NONE, NONE, NONE, NONE},
|
|
/* accent_tint */ {ACCENT_TINT, NONE, NONE, NONE, NONE},
|
|
/* accent_bright */ {ACCENT_BRIGHT, NONE, NONE, NONE, NONE},
|
|
/* button_dark */ {BUTTON_DARK, NONE, ON_ACCENT, NONE, NONE},
|
|
/* button_dark_pressed */ {BUTTON_DARK_PRESSED, NONE, NONE, NONE, NONE},
|
|
/* knob */ {KNOB, NONE, NONE, NONE, NONE},
|
|
/* spinner */ {NONE, NONE, NONE, NONE, SPINNER_TRACK},
|
|
/* calibration */ {CALIBRATION_PAGE, NONE, CALIBRATION_INK, NONE, NONE},
|
|
/* camera */ {CAMERA_PAGE, NONE, CAMERA_NOTE, NONE, NONE},
|
|
};
|
|
// Fill a style with a paint in the current look. Setting a property the style already has allocates nothing.
|
|
inline void fill(lv_style_t *style, Paint paint) {
|
|
if (!style) return;
|
|
const PaintRoles &p = PAINTS[static_cast<size_t>(paint)];
|
|
if (p.bg != NONE) lv_style_set_bg_color(style, color(static_cast<Role>(p.bg)));
|
|
if (p.border != NONE) lv_style_set_border_color(style, color(static_cast<Role>(p.border)));
|
|
if (p.text != NONE) lv_style_set_text_color(style, color(static_cast<Role>(p.text)));
|
|
if (p.line != NONE) lv_style_set_line_color(style, color(static_cast<Role>(p.line)));
|
|
if (p.arc != NONE) lv_style_set_arc_color(style, color(static_cast<Role>(p.arc)));
|
|
}
|
|
// The board profile's styles and the paint of each, as fill() calls. Set and run once in on_boot.
|
|
inline void (*paints)() = nullptr;
|
|
// The firmware's own shared style for a paint, made on first use.
|
|
inline std::array<lv_style_t *, static_cast<size_t>(Paint::COUNT)> own{};
|
|
inline lv_style_t *style(Paint paint) {
|
|
auto *&s = own[static_cast<size_t>(paint)];
|
|
if (!s) {
|
|
s = new lv_style_t;
|
|
lv_style_init(s);
|
|
fill(s, paint);
|
|
}
|
|
return s;
|
|
}
|
|
// What the board draws again after a change of look: the tiles, an open card, the settings page. Set by the profile.
|
|
inline void (*redraw)() = nullptr;
|
|
// The one way the look changes. Every paint is filled again and LVGL is told once; what code painted itself follows
|
|
// through redraw(). A look the screen already has costs nothing, so the setting may be applied on every change.
|
|
inline void set_dark(bool on) {
|
|
if (on == dark) return;
|
|
dark = on;
|
|
if (paints) paints();
|
|
for (size_t i = 0; i < own.size(); ++i) if (own[i]) fill(own[i], static_cast<Paint>(i));
|
|
// One walk over every widget instead of one per paint.
|
|
lv_obj_report_style_change(nullptr);
|
|
if (redraw) redraw();
|
|
}
|
|
|
|
} // namespace theme
|
|
#endif
|