diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 21766de8..f6aa9912 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -40,6 +40,7 @@ public: uint8_t autoadd_max_hops = 0; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) char default_scope_name[31]; uint8_t default_scope_key[16]; + char ui_theme[12]; // имя темы дисплея; пусто = тема по умолчанию для платы private: class RadioPrefs : public ConfigSerializer { // COPIED from CommonCLI (for now) @@ -112,6 +113,7 @@ private: def("vibe_q", _parent->vibe_quiet); def("auto_add", _parent->autoadd_config); // bitmask for auto-add contacts config def("man_add", _parent->manual_add_contacts); + def("theme", _parent->ui_theme, sizeof(_parent->ui_theme)); // имя темы дисплея def("tel_base", _parent->telemetry_mode_base); def("tel_loc", _parent->telemetry_mode_loc); def("tel_env", _parent->telemetry_mode_env); @@ -137,6 +139,7 @@ public: NodePrefs() : radio(this), gps(this), companion(this) { node_name[0] = 0; default_scope_name[0] = 0; + ui_theme[0] = 0; memset(default_scope_key, 0, sizeof(default_scope_key)); } // new accessor methods diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 4bca679d..43c90829 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -121,6 +121,7 @@ class HomeScreen : public UIScreen { #if UI_SENSORS_PAGE == 1 SENSORS, #endif + THEME, // выбор темы дисплея (только если драйвер поддерживает темы) SHUTDOWN, Count // keep as last }; @@ -162,6 +163,46 @@ class HomeScreen : public UIScreen { const char* radioRowName(uint8_t row); void renderRadioEditor(DisplayDriver& display); + // выбор темы дисплея (страница THEME) + bool _theme_edit; + uint8_t _theme_sel; + unsigned long _theme_last_activity; + + void themeEditEnter() { + DisplayDriver* d = _task->getDisplay(); + if (d == NULL || d->getThemeCount() == 0) return; + _task->clearAlert(); + _theme_edit = true; + _theme_sel = d->getTheme(); // начинаем с активной темы + _theme_last_activity = millis(); + } + void themeStore() { // запомнить текущую (предпросмотренную) тему в prefs + DisplayDriver* d = _task->getDisplay(); + if (d == NULL) return; + const char* name = d->getThemeName(d->getTheme()); + strncpy(_node_prefs->ui_theme, name, sizeof(_node_prefs->ui_theme) - 1); + _node_prefs->ui_theme[sizeof(_node_prefs->ui_theme) - 1] = 0; + the_mesh.savePrefs(); + } + void themeEditLeave(bool save) { + _theme_edit = false; + if (save) { + themeStore(); + _task->showAlert("Saved", 1000); + } + _task->showPendingMessages(); + } + void themeRowStep(int8_t dir) { + DisplayDriver* d = _task->getDisplay(); + if (d == NULL) return; + uint8_t rows = d->getThemeCount() + 1; // темы + "Save & exit" + _theme_sel = (_theme_sel + rows + dir) % rows; + if (_theme_sel < d->getThemeCount()) d->setTheme(_theme_sel); // живой предпросмотр + } + void renderThemePage(DisplayDriver& display); + void renderThemeEditor(DisplayDriver& display); + uint8_t pageStep(int8_t dir); + void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) { // Convert millivolts to percentage @@ -234,10 +275,11 @@ public: HomeScreen(UITask* task, mesh::RTCClock* rtc, SensorManager* sensors, NodePrefs* node_prefs) : _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0), _shutdown_init(false), _radio_edit(false), _radio_val_edit(false), _radio_sel(0), - _radio_last_activity(0), sensors_lpp(200) { } + _radio_last_activity(0), _theme_edit(false), _theme_sel(0), _theme_last_activity(0), + sensors_lpp(200) { } - // в меню настройки радио входящие сообщения не всплывают поверх экрана - bool suppressAlerts() override { return _radio_edit; } + // в меню настройки (радио или темы) входящие сообщения не всплывают поверх экрана + bool suppressAlerts() override { return _radio_edit || _theme_edit; } void poll() override { if (_radio_edit) { @@ -248,6 +290,14 @@ public: _task->keepDisplayOn(); // пока работаем — экран не гасим по таймауту } } + if (_theme_edit) { + if (millis() - _theme_last_activity >= UI_RADIO_MENU_TIMEOUT_MILLIS) { + themeEditLeave(false); + _task->blankDisplay(); + } else { + _task->keepDisplayOn(); + } + } if (_shutdown_init && !_task->isButtonPressed()) { // must wait for USR button to be released _task->shutdown(); } @@ -260,6 +310,10 @@ public: renderRadioEditor(display); return 5000; } + if (_theme_edit) { + renderThemeEditor(display); + return 5000; + } display.setColor(UIColor::title_bkg); display.fillRect(0, 0, display.width(), 12); char tmp[80]; @@ -493,6 +547,8 @@ public: if (sensors_scroll) sensors_scroll_offset = (sensors_scroll_offset+1)%sensors_nb; else sensors_scroll_offset = 0; #endif + } else if (_page == HomePage::THEME) { + renderThemePage(display); } else if (_page == HomePage::SHUTDOWN) { display.setColor(UIColor::corp_blue); display.setTextSize(1); @@ -531,12 +587,38 @@ public: } return true; // остальные события в этом режиме игнорируем } + if (_theme_edit) { // меню выбора темы + _theme_last_activity = millis(); + if (c == KEY_NEXT || c == KEY_RIGHT) { + themeRowStep(+1); + return true; + } + if (c == KEY_PREV || c == KEY_LEFT) { + themeRowStep(-1); + return true; + } + if (c == KEY_ENTER) { + DisplayDriver* d = _task->getDisplay(); + if (d != NULL) { + if (_theme_sel >= d->getThemeCount()) { + themeEditLeave(true); // строка "Save & exit" — сохранить и выйти + } else { + themeStore(); // длинное нажатие на теме — запомнить её сейчас + char msg[28]; + snprintf(msg, sizeof(msg), "theme %s OK", d->getThemeName(d->getTheme())); + _task->showAlert(msg, 1200); + } + } + return true; + } + return true; + } if (c == KEY_LEFT || c == KEY_PREV) { - _page = (_page + HomePage::Count - 1) % HomePage::Count; + _page = pageStep(-1); return true; } if (c == KEY_NEXT || c == KEY_RIGHT) { - _page = (_page + 1) % HomePage::Count; + _page = pageStep(+1); if (_page == HomePage::RECENT) { _task->showAlert("Recent adverts", 800); } @@ -548,6 +630,10 @@ public: _task->showAlert("Nodes must match", 1500); return true; } + if (c == KEY_ENTER && _page == HomePage::THEME) { + themeEditEnter(); + return true; + } if (c == KEY_ENTER && _page == HomePage::BLUETOOTH) { if (_task->isBluetoothEnabled()) { // toggle Bluetooth on/off _task->disableBluetooth(); @@ -705,6 +791,60 @@ void HomeScreen::renderRadioEditor(DisplayDriver& display) { } } +uint8_t HomeScreen::pageStep(int8_t dir) { + uint8_t p = _page; + for (uint8_t i = 0; i < HomePage::Count; i++) { + p = (p + HomePage::Count + dir) % HomePage::Count; + // страница тем есть только у драйверов, которые поддерживают палитры + if (p != HomePage::THEME || _task->displayHasThemes()) return p; + } + return _page; +} + +void HomeScreen::renderThemePage(DisplayDriver& display) { + char tmp[32]; + uint8_t n = display.getThemeCount(); + display.setTextSize(1); + display.setColor(UIColor::primary_txt); + snprintf(tmp, sizeof(tmp), "Theme: %s", n > 0 ? display.getThemeName(display.getTheme()) : "-"); + display.setCursor(0, 22); + display.print(tmp); + display.setColor(UIColor::secondary_txt); + display.drawTextCentered(display.width() / 2, 56, "setup: " PRESS_LABEL); +} + +void HomeScreen::renderThemeEditor(DisplayDriver& display) { + uint8_t n = display.getThemeCount(); + uint8_t rows = n + 1; // темы + "Save & exit" + const uint8_t VISIBLE = 6; // 6 строк по 10 px влезают в 64 + uint8_t first = _theme_sel >= VISIBLE ? _theme_sel - VISIBLE + 1 : 0; + + display.setTextSize(1); + int y = 2; + for (uint8_t r = first; r < rows && y <= 52; r++, y += 10) { + const char* label = (r < n) ? display.getThemeName(r) : "Save & exit"; + if (r == _theme_sel) { + display.setColor(UIColor::primary_txt); // выбранная строка — инверсия + display.fillRect(0, y - 1, display.width(), 10); + display.setColor(UIColor::window_bkg); + } else { + display.setColor(UIColor::primary_txt); + } + display.drawTextLeftAlign(1, y, label); + + if (r < n) { + // образцы цветов темы: полоса заголовка, заливка попапа, акцент + int sx = display.width() - 32; + display.setColor(display.getThemeColor(r, DisplayDriver::THEME_COLOR_TITLE)); + display.fillRect(sx, y, 12, 8); + display.setColor(display.getThemeColor(r, DisplayDriver::THEME_COLOR_POPUP)); + display.fillRect(sx + 13, y, 8, 8); + display.setColor(display.getThemeColor(r, DisplayDriver::THEME_COLOR_ACCENT)); + display.fillRect(sx + 22, y, 10, 8); + } + } +} + class MsgPreviewScreen : public UIScreen { UITask* _task; mesh::RTCClock* _rtc; @@ -811,6 +951,11 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no _node_prefs = node_prefs; if (_display != NULL) { + // применить сохранённую тему дисплея (если драйвер их поддерживает) + if (_display->getThemeCount() > 0) { + uint8_t idx = _display->findTheme(_node_prefs->ui_theme); + if (idx != 0xFF) _display->setTheme(idx); + } _display->turnOn(); } diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index 90904681..eea0a31d 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -81,6 +81,8 @@ public: void showPendingMessages(); // открыть предпросмотр, если сообщение пришло «в фоне» int getMsgCount() const { return _msgcount; } bool hasDisplay() const { return _display != NULL; } + DisplayDriver* getDisplay() { return _display; } + bool displayHasThemes() const { return _display != NULL && _display->getThemeCount() > 0; } bool isButtonPressed() const; bool isBuzzerQuiet() { diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index 5cf21684..a3fb5f68 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -172,4 +172,31 @@ public: } virtual void endFrame() = 0; + + // Темы (палитры). По умолчанию тем нет — так у монохромных панелей + // (SSD1306 / SH1106 / e-ink), где все цвета и так белые. + virtual uint8_t getThemeCount() { return 0; } + virtual const char* getThemeName(uint8_t i) { (void)i; return ""; } + virtual uint8_t getTheme() { return 0; } + virtual void setTheme(uint8_t i) { (void)i; } + + // Роли цветов темы — для образцов в меню выбора темы + enum ThemeColorRole { + THEME_COLOR_WINDOW = 0, // фон + THEME_COLOR_TITLE, // полоса заголовка + THEME_COLOR_POPUP, // заливка попапа + THEME_COLOR_ACCENT, // акцент (предупреждения) + THEME_COLOR_TEXT, // основной текст + }; + virtual ColorVal getThemeColor(uint8_t theme, uint8_t role) { (void)theme; (void)role; return 0; } + + // Поиск темы по имени (имена хранятся в prefs — устойчиво к перестановке тем в таблице). + // Возвращает 0xFF, если имени нет. + uint8_t findTheme(const char* name) { + if (name == NULL || name[0] == 0) return 0xFF; + for (uint8_t i = 0; i < getThemeCount(); i++) { + if (strcmp(getThemeName(i), name) == 0) return i; + } + return 0xFF; + } }; diff --git a/src/helpers/ui/ST7735Display.cpp b/src/helpers/ui/ST7735Display.cpp index 99eafaee..9da841b7 100644 --- a/src/helpers/ui/ST7735Display.cpp +++ b/src/helpers/ui/ST7735Display.cpp @@ -422,97 +422,112 @@ bool ST7735Display::i2c_probe(TwoWire& wire, uint8_t addr) { // Color scheme // +// Палитры вынесены в таблицу, поэтому тема переключается в рантайме (UIColor::* — +// обычные статические переменные, не const). Выбранная тема хранится в prefs по имени +// и применяется при старте; в меню ui-new есть страница THEME с живым предпросмотром. +// // Тема по умолчанию зависит от платы: // - Heltec T096 — AMBER (графит + янтарь); // - остальные платы на этом драйвере (heltec_t1 / tracker / tracker_v2) — -// официальная схема MeshCore, её же можно включить явно: ST7735_THEME_CLASSIC. -// Любую тему можно включить на любой плате флагом ST7735_THEME_* в platformio.ini -// варианта (см. variants/heltec_t096). +// официальная схема MeshCore (CLASSIC). +// Флаг ST7735_THEME_* в platformio.ini варианта переопределяет этот дефолт. // -// Тема CLASSIC взята из upstream: github.com/meshcore-dev/MeshCore, ветка main, -// src/helpers/ui/ST7735Display.cpp. Это светлая схема: белый фон, чёрный текст. +// CLASSIC взята из upstream: github.com/meshcore-dev/MeshCore, ветка main, +// src/helpers/ui/ST7735Display.cpp — светлая схема (белый фон, чёрный текст). // В форке она была перекрашена в тёмную вручную, здесь восстановлен оригинал. #if (defined(ST7735_THEME_CLASSIC) + defined(ST7735_THEME_AMBER) + defined(ST7735_THEME_TEAL) + defined(ST7735_THEME_SIGNAL) + defined(ST7735_THEME_NEON)) > 1 #error "ST7735Display: выбрана более чем одна тема ST7735_THEME_*" #endif -#define MC_CLASSIC_WINDOW_BKG ST77XX_WHITE -#define MC_CLASSIC_TITLE_BKG ST77XX_BLUE -#define MC_CLASSIC_TITLE_TXT ST77XX_WHITE -#define MC_CLASSIC_PRIMARY_TXT ST77XX_BLACK #define MC_CLASSIC_SECONDARY_TXT ((18 << 11) | (36 << 5) | 18) // mid-gray -#define MC_CLASSIC_WARNING_TXT ST77XX_ORANGE -#define MC_CLASSIC_POPUP_BKG ST77XX_CYAN -#define MC_CLASSIC_POPUP_TXT ST77XX_BLACK -#define MC_CLASSIC_CORP_BLUE 0x001A -#if defined(ST7735_THEME_CLASSIC) // официальная схема MeshCore (светлая) - ColorVal UIColor::window_bkg = MC_CLASSIC_WINDOW_BKG; - ColorVal UIColor::title_bkg = MC_CLASSIC_TITLE_BKG; - ColorVal UIColor::title_txt = MC_CLASSIC_TITLE_TXT; - ColorVal UIColor::primary_txt = MC_CLASSIC_PRIMARY_TXT; - ColorVal UIColor::secondary_txt = MC_CLASSIC_SECONDARY_TXT; - ColorVal UIColor::warning_txt = MC_CLASSIC_WARNING_TXT; - ColorVal UIColor::popup_bkg = MC_CLASSIC_POPUP_BKG; - ColorVal UIColor::popup_txt = MC_CLASSIC_POPUP_TXT; - ColorVal UIColor::corp_blue = MC_CLASSIC_CORP_BLUE; +struct ColorTheme { + const char* name; + ColorVal window_bkg, title_bkg, title_txt, primary_txt, + secondary_txt, warning_txt, popup_bkg, popup_txt, corp_blue; +}; -#elif defined(ST7735_THEME_TEAL) // B. ночная бирюза - ColorVal UIColor::window_bkg = 0x0883; // #081018 - ColorVal UIColor::title_bkg = 0x1187; // #103038 - ColorVal UIColor::title_txt = 0xEFDF; // #E8F8F8 - ColorVal UIColor::primary_txt = 0xF7DF; // #F0F8F8 - ColorVal UIColor::secondary_txt = 0x8515; // #80A0A8 - ColorVal UIColor::warning_txt = 0xFE4A; // #FFC850 - ColorVal UIColor::popup_bkg = 0x1696; // #10D0B0 - ColorVal UIColor::popup_txt = 0x0881; // #081008 - ColorVal UIColor::corp_blue = 0x45DF; // #40B8F8 +// Порядок в таблице = порядок в меню выбора темы. +// constexpr — чтобы палитра инициализировалась статически (иначе значения уехали бы +// в .bss и заполнялись динамической инициализацией C++). +static constexpr ColorTheme THEMES[] = { + { "amber", 0x1082, 0x2146, 0xF79E, 0xFFDF, 0x9493, 0xFD84, 0xFD84, 0x10A3, 0x551F }, // #101010 / #202830 / #FFB020 + { "teal", 0x0883, 0x1187, 0xEFDF, 0xF7DF, 0x8515, 0xFE4A, 0x1696, 0x0881, 0x45DF }, // #081018 / #103038 / #10D0B0 + { "signal", 0x0000, 0xEB07, 0x1082, 0xFFDF, 0x9D15, 0xFE8C, 0xFB46, 0x1082, 0x5D5F }, // #000000 / #E86038 / #FF6830 + { "neon", 0x0000, 0x0000, 0xEF5D, 0xFFFF, 0x8C52, 0xFEA0, 0x18C3, 0xFEA0, 0x555F }, // #000000 / #000000 / #FFD400 + { "classic", ST77XX_WHITE, ST77XX_BLUE, ST77XX_WHITE, ST77XX_BLACK, MC_CLASSIC_SECONDARY_TXT, + ST77XX_ORANGE, ST77XX_CYAN, ST77XX_BLACK, 0x001A }, // официальная светлая +}; +#define THEME_COUNT ((uint8_t)(sizeof(THEMES) / sizeof(THEMES[0]))) -#elif defined(ST7735_THEME_SIGNAL) // C. чёрный + оранжевый сигнал - ColorVal UIColor::window_bkg = 0x0000; // #000000 - ColorVal UIColor::title_bkg = 0xEB07; // #E86038 - ColorVal UIColor::title_txt = 0x1082; // #101010 - ColorVal UIColor::primary_txt = 0xFFDF; // #F8F8F8 - ColorVal UIColor::secondary_txt = 0x9D15; // #98A0A8 - ColorVal UIColor::warning_txt = 0xFE8C; // #FFD060 - ColorVal UIColor::popup_bkg = 0xFB46; // #FF6830 - ColorVal UIColor::popup_txt = 0x1082; // #101010 - ColorVal UIColor::corp_blue = 0x5D5F; // #58A8FF - -#elif defined(ST7735_THEME_NEON) // D. неон на чёрном - ColorVal UIColor::window_bkg = 0x0000; // #000000 - ColorVal UIColor::title_bkg = 0x0000; // #000000 (полоса слита с фоном) - ColorVal UIColor::title_txt = 0xEF5D; // #E8E8E8 - ColorVal UIColor::primary_txt = 0xFFFF; // #FFFFFF - ColorVal UIColor::secondary_txt = 0x8C52; // #888890 - ColorVal UIColor::warning_txt = 0xFEA0; // #FFD400 - ColorVal UIColor::popup_bkg = 0x18C3; // #181818 - ColorVal UIColor::popup_txt = 0xFEA0; // #FFD400 (рамка и текст) - ColorVal UIColor::corp_blue = 0x555F; // #50A8FF - -#elif defined(ST7735_THEME_AMBER) || defined(HELTEC_T096) // A. графит + янтарь (по умолчанию для T096) - ColorVal UIColor::window_bkg = 0x1082; // #101010 - ColorVal UIColor::title_bkg = 0x2146; // #202830 - ColorVal UIColor::title_txt = 0xF79E; // #F0F0F0 - ColorVal UIColor::primary_txt = 0xFFDF; // #F8F8F8 - ColorVal UIColor::secondary_txt = 0x9493; // #909098 - ColorVal UIColor::warning_txt = 0xFD84; // #FFB020 - ColorVal UIColor::popup_bkg = 0xFD84; // #FFB020 - ColorVal UIColor::popup_txt = 0x10A3; // #101418 - ColorVal UIColor::corp_blue = 0x551F; // #50A0FF - -#else // остальные платы: официальная схема MeshCore - ColorVal UIColor::window_bkg = MC_CLASSIC_WINDOW_BKG; - ColorVal UIColor::title_bkg = MC_CLASSIC_TITLE_BKG; - ColorVal UIColor::title_txt = MC_CLASSIC_TITLE_TXT; - ColorVal UIColor::primary_txt = MC_CLASSIC_PRIMARY_TXT; - ColorVal UIColor::secondary_txt = MC_CLASSIC_SECONDARY_TXT; - ColorVal UIColor::warning_txt = MC_CLASSIC_WARNING_TXT; - ColorVal UIColor::popup_bkg = MC_CLASSIC_POPUP_BKG; - ColorVal UIColor::popup_txt = MC_CLASSIC_POPUP_TXT; - ColorVal UIColor::corp_blue = MC_CLASSIC_CORP_BLUE; +static constexpr uint8_t default_theme_index() { +#if defined(ST7735_THEME_CLASSIC) + return 4; +#elif defined(ST7735_THEME_TEAL) + return 1; +#elif defined(ST7735_THEME_SIGNAL) + return 2; +#elif defined(ST7735_THEME_NEON) + return 3; +#elif defined(ST7735_THEME_AMBER) || defined(HELTEC_T096) + return 0; +#else + return 4; // остальные платы: официальная схема MeshCore #endif +} + +static constexpr uint8_t DEFAULT_THEME = default_theme_index(); +static uint8_t active_theme = DEFAULT_THEME; + +ColorVal UIColor::window_bkg = THEMES[DEFAULT_THEME].window_bkg; +ColorVal UIColor::title_bkg = THEMES[DEFAULT_THEME].title_bkg; +ColorVal UIColor::title_txt = THEMES[DEFAULT_THEME].title_txt; +ColorVal UIColor::primary_txt = THEMES[DEFAULT_THEME].primary_txt; +ColorVal UIColor::secondary_txt = THEMES[DEFAULT_THEME].secondary_txt; +ColorVal UIColor::warning_txt = THEMES[DEFAULT_THEME].warning_txt; +ColorVal UIColor::popup_bkg = THEMES[DEFAULT_THEME].popup_bkg; +ColorVal UIColor::popup_txt = THEMES[DEFAULT_THEME].popup_txt; +ColorVal UIColor::corp_blue = THEMES[DEFAULT_THEME].corp_blue; + +uint8_t ST7735Display::getThemeCount() { + return THEME_COUNT; +} + +const char* ST7735Display::getThemeName(uint8_t i) { + return i < THEME_COUNT ? THEMES[i].name : ""; +} + +uint8_t ST7735Display::getTheme() { + return active_theme; +} + +void ST7735Display::setTheme(uint8_t i) { + if (i >= THEME_COUNT) return; + const ColorTheme* t = &THEMES[i]; + UIColor::window_bkg = t->window_bkg; + UIColor::title_bkg = t->title_bkg; + UIColor::title_txt = t->title_txt; + UIColor::primary_txt = t->primary_txt; + UIColor::secondary_txt = t->secondary_txt; + UIColor::warning_txt = t->warning_txt; + UIColor::popup_bkg = t->popup_bkg; + UIColor::popup_txt = t->popup_txt; + UIColor::corp_blue = t->corp_blue; + active_theme = i; +} + +ColorVal ST7735Display::getThemeColor(uint8_t theme, uint8_t role) { + if (theme >= THEME_COUNT) return 0; + const ColorTheme* t = &THEMES[theme]; + switch (role) { + case THEME_COLOR_WINDOW: return t->window_bkg; + case THEME_COLOR_TITLE: return t->title_bkg; + case THEME_COLOR_POPUP: return t->popup_bkg; + case THEME_COLOR_ACCENT: return t->warning_txt; + default: return t->primary_txt; + } +} bool ST7735Display::begin() { if (!sprite) { diff --git a/src/helpers/ui/ST7735Display.h b/src/helpers/ui/ST7735Display.h index 8643fd63..7aa10962 100644 --- a/src/helpers/ui/ST7735Display.h +++ b/src/helpers/ui/ST7735Display.h @@ -56,6 +56,13 @@ public: uint16_t getTextWidth(const char* str) override; void endFrame() override; + // Темы (палитры заданы в таблице THEMES в ST7735Display.cpp) + uint8_t getThemeCount() override; + const char* getThemeName(uint8_t i) override; + uint8_t getTheme() override; + void setTheme(uint8_t i) override; + ColorVal getThemeColor(uint8_t theme, uint8_t role) override; + protected: void _resetAndInit(); };