diff --git a/examples/companion_radio/AbstractUITask.h b/examples/companion_radio/AbstractUITask.h index b25b1442fc..d4b661f8ea 100644 --- a/examples/companion_radio/AbstractUITask.h +++ b/examples/companion_radio/AbstractUITask.h @@ -43,4 +43,6 @@ class AbstractUITask { virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0; virtual void notify(UIEventType t = UIEventType::none) = 0; virtual void loop() = 0; + virtual bool applyTheme(const char* theme) { return false; } + virtual int getThemeCount() { return 0; } }; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 2c33406632..9f56702146 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -2050,6 +2050,19 @@ void MyMesh::checkCLIRescueCmd() { _prefs.ble_pin = atoi(&config[4]); savePrefs(); Serial.printf(" > pin is now %06d\n", _prefs.ble_pin); + } else if (memcmp(config, "theme ", 6) == 0) { + if (_ui != NULL && _ui->getThemeCount() > 0) { + if (_ui->applyTheme(&config[6])) { + strncpy(_prefs.ui_theme, &config[6], sizeof(_prefs.ui_theme) - 1); + _prefs.ui_theme[sizeof(_prefs.ui_theme) - 1] = 0; + savePrefs(); + Serial.printf(" > theme is now %s\n", _prefs.ui_theme); + } else { + Serial.println(" Error: theme not found or not supported by this display"); + } + } else { + Serial.println(" Error: this device has no theme-capable display"); + } } else { Serial.printf(" Error: unknown config: %s\n", config); } diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 453f28d5d1..ed0838640f 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -38,6 +38,7 @@ class NodePrefs : public ConfigSerializer { // persisted to file uint8_t _client_repeat = 0; // DEPRECATED -> use repeat.disable_fwd uint8_t path_hash_mode = 0; // which path mode to use when sending uint8_t autoadd_max_hops = 0; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) + char ui_theme[16]; // UI theme, if supported by the display driver, e.g. "default" (light), "legacy" (dark) char default_scope_name[31]; uint8_t default_scope_key[16]; @@ -111,6 +112,7 @@ class NodePrefs : public ConfigSerializer { // persisted to file def("tel_base", _parent->telemetry_mode_base); def("tel_loc", _parent->telemetry_mode_loc); def("tel_env", _parent->telemetry_mode_env); + def("theme", _parent->ui_theme, sizeof(_parent->ui_theme)); } public: CompanionPrefs(NodePrefs* parent) : _parent(parent) { } @@ -134,6 +136,7 @@ class NodePrefs : public ConfigSerializer { // persisted to file node_name[0] = 0; default_scope_name[0] = 0; memset(default_scope_key, 0, sizeof(default_scope_key)); + strcpy(ui_theme, "default"); } // new accessor methods bool isRepeatEn() const { return repeat.disable_fwd == 0; } diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 051f3b31ea..95e8865cc9 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -592,6 +592,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no _node_prefs = node_prefs; if (_display != NULL) { + _display->applyTheme(_node_prefs->ui_theme); _display->turnOn(); } @@ -614,6 +615,14 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no setCurrScreen(splash); } +bool UITask::applyTheme(const char* theme) { + return _display != NULL ? _display->applyTheme(theme) : false; +} + +int UITask::getThemeCount() { + return _display != NULL ? _display->getThemeCount() : 0; +} + void UITask::showAlert(const char* text, int duration_millis) { strcpy(_alert, text); _alert_expiry = millis() + duration_millis; diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index 52d3ffa13c..439fe0616a 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -96,6 +96,8 @@ class UITask : public AbstractUITask { void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override; void notify(UIEventType t = UIEventType::none) override; void loop() override; + bool applyTheme(const char* theme) override; + int getThemeCount() override; void shutdown(bool restart = false); }; diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index b76a1b6ca0..486dc2fcea 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -29,6 +29,8 @@ class DisplayDriver { virtual void startFrame(ColorVal bkg = UIColor::window_bkg) = 0; virtual void setTextSize(int sz) = 0; virtual void setColor(ColorVal c) = 0; + virtual bool applyTheme(const char* theme) { return false; } + virtual int getThemeCount() { return 0; } virtual void setCursor(int x, int y) = 0; virtual void print(const char* str) = 0; virtual void printWordWrap(const char* str, int max_width) { print(str); } // fallback to basic print() if no override diff --git a/src/helpers/ui/LGFXDisplay.cpp b/src/helpers/ui/LGFXDisplay.cpp index 7bdd52a251..98bafc7e94 100644 --- a/src/helpers/ui/LGFXDisplay.cpp +++ b/src/helpers/ui/LGFXDisplay.cpp @@ -11,6 +11,33 @@ ColorVal UIColor::popup_bkg = 0x07FF; // CYAN ColorVal UIColor::popup_txt = 0x0000; ColorVal UIColor::corp_blue = 0x001A; +bool LGFXDisplay::applyTheme(const char* theme) { + if (strcmp(theme, "legacy") == 0) { + UIColor::window_bkg = 0x0000; + UIColor::title_bkg = 0x0000; + UIColor::title_txt = 0x07E0; + UIColor::primary_txt = 0x07E0; + UIColor::secondary_txt = 0xFFE0; + UIColor::warning_txt = 0xF800; + UIColor::popup_bkg = 0x0000; + UIColor::popup_txt = 0xFFFF; + UIColor::corp_blue = 0x001F; + return true; + } else if (strcmp(theme, "default") == 0) { + UIColor::window_bkg = 0xFFFF; + UIColor::title_bkg = 0x001F; + UIColor::title_txt = 0xFFFF; + UIColor::primary_txt = 0x0000; + UIColor::secondary_txt = (18 << 11) | (36 << 5) | 18; + UIColor::warning_txt = 0xFD20; + UIColor::popup_bkg = 0x07FF; + UIColor::popup_txt = 0x0000; + UIColor::corp_blue = 0x001A; + return true; + } + return false; +} + bool LGFXDisplay::begin() { turnOn(); display->init(); diff --git a/src/helpers/ui/LGFXDisplay.h b/src/helpers/ui/LGFXDisplay.h index a2d660b22e..b69c2b850b 100644 --- a/src/helpers/ui/LGFXDisplay.h +++ b/src/helpers/ui/LGFXDisplay.h @@ -35,5 +35,7 @@ class LGFXDisplay : public DisplayDriver { void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; uint16_t getTextWidth(const char* str) override; void endFrame() override; + bool applyTheme(const char* theme) override; + int getThemeCount() override { return 2; } virtual bool getTouch(int *x, int *y); }; diff --git a/src/helpers/ui/NV3001BDisplay.cpp b/src/helpers/ui/NV3001BDisplay.cpp index 1ff7765cbb..56e58bf9c0 100644 --- a/src/helpers/ui/NV3001BDisplay.cpp +++ b/src/helpers/ui/NV3001BDisplay.cpp @@ -107,6 +107,33 @@ ColorVal UIColor::popup_bkg = 0x07FF; // CYAN ColorVal UIColor::popup_txt = 0x0000; ColorVal UIColor::corp_blue = 0x001A; +bool NV3001BDisplay::applyTheme(const char* theme) { + if (strcmp(theme, "legacy") == 0) { + UIColor::window_bkg = 0x0000; + UIColor::title_bkg = 0x0000; + UIColor::title_txt = 0x07E0; + UIColor::primary_txt = 0x07E0; + UIColor::secondary_txt = 0xFFE0; + UIColor::warning_txt = 0xF800; + UIColor::popup_bkg = 0x0000; + UIColor::popup_txt = 0xFFFF; + UIColor::corp_blue = 0x001F; + return true; + } else if (strcmp(theme, "default") == 0) { + UIColor::window_bkg = 0xFFFF; + UIColor::title_bkg = 0x001F; + UIColor::title_txt = 0xFFFF; + UIColor::primary_txt = 0x0000; + UIColor::secondary_txt = (18 << 11) | (36 << 5) | 18; + UIColor::warning_txt = 0xFD20; + UIColor::popup_bkg = 0x07FF; + UIColor::popup_txt = 0x0000; + UIColor::corp_blue = 0x001A; + return true; + } + return false; +} + static int scaleX(int x) { return (int)(x * DISPLAY_SCALE_X); } diff --git a/src/helpers/ui/NV3001BDisplay.h b/src/helpers/ui/NV3001BDisplay.h index 76505b5d26..24cfa0b888 100644 --- a/src/helpers/ui/NV3001BDisplay.h +++ b/src/helpers/ui/NV3001BDisplay.h @@ -65,4 +65,6 @@ class NV3001BDisplay : public DisplayDriver { void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; uint16_t getTextWidth(const char* str) override; void endFrame() override; + bool applyTheme(const char* theme) override; + int getThemeCount() override { return 2; } }; diff --git a/src/helpers/ui/ST7735Display.cpp b/src/helpers/ui/ST7735Display.cpp index 344f5b1d9d..472d94e733 100644 --- a/src/helpers/ui/ST7735Display.cpp +++ b/src/helpers/ui/ST7735Display.cpp @@ -428,6 +428,33 @@ ColorVal UIColor::popup_bkg = ST77XX_CYAN; ColorVal UIColor::popup_txt = ST77XX_BLACK; ColorVal UIColor::corp_blue = 0x001A; +bool ST7735Display::applyTheme(const char* theme) { + if (strcmp(theme, "legacy") == 0) { + UIColor::window_bkg = ST77XX_BLACK; + UIColor::title_bkg = ST77XX_BLACK; + UIColor::title_txt = ST77XX_GREEN; + UIColor::primary_txt = ST77XX_GREEN; + UIColor::secondary_txt = ST77XX_YELLOW; + UIColor::warning_txt = ST77XX_RED; + UIColor::popup_bkg = ST77XX_BLACK; + UIColor::popup_txt = ST77XX_WHITE; + UIColor::corp_blue = ST77XX_BLUE; + return true; + } else if (strcmp(theme, "default") == 0) { + UIColor::window_bkg = ST77XX_WHITE; + UIColor::title_bkg = ST77XX_BLUE; + UIColor::title_txt = ST77XX_WHITE; + UIColor::primary_txt = ST77XX_BLACK; + UIColor::secondary_txt = (18 << 11) | (36 << 5) | 18; + UIColor::warning_txt = ST77XX_ORANGE; + UIColor::popup_bkg = ST77XX_CYAN; + UIColor::popup_txt = ST77XX_BLACK; + UIColor::corp_blue = 0x001A; + return true; + } + return false; +} + bool ST7735Display::begin() { if (!sprite) { // alloc offscreen canvas diff --git a/src/helpers/ui/ST7735Display.h b/src/helpers/ui/ST7735Display.h index 6e289b2193..5474ec32db 100644 --- a/src/helpers/ui/ST7735Display.h +++ b/src/helpers/ui/ST7735Display.h @@ -43,6 +43,8 @@ class ST7735Display : public DisplayDriver { void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; uint16_t getTextWidth(const char* str) override; void endFrame() override; + bool applyTheme(const char* theme) override; + int getThemeCount() override { return 2; } protected: void _resetAndInit(); diff --git a/src/helpers/ui/ST7789LCDDisplay.cpp b/src/helpers/ui/ST7789LCDDisplay.cpp index a9f30dd56a..843c691ef6 100644 --- a/src/helpers/ui/ST7789LCDDisplay.cpp +++ b/src/helpers/ui/ST7789LCDDisplay.cpp @@ -34,6 +34,33 @@ ColorVal UIColor::popup_bkg = ST77XX_CYAN; ColorVal UIColor::popup_txt = ST77XX_BLACK; ColorVal UIColor::corp_blue = 0x001A; +bool ST7789LCDDisplay::applyTheme(const char* theme) { + if (strcmp(theme, "legacy") == 0) { + UIColor::window_bkg = ST77XX_BLACK; + UIColor::title_bkg = ST77XX_BLACK; + UIColor::title_txt = ST77XX_GREEN; + UIColor::primary_txt = ST77XX_GREEN; + UIColor::secondary_txt = ST77XX_YELLOW; + UIColor::warning_txt = ST77XX_RED; + UIColor::popup_bkg = ST77XX_BLACK; + UIColor::popup_txt = ST77XX_WHITE; + UIColor::corp_blue = ST77XX_BLUE; + return true; + } else if (strcmp(theme, "default") == 0) { + UIColor::window_bkg = ST77XX_WHITE; + UIColor::title_bkg = ST77XX_BLUE; + UIColor::title_txt = ST77XX_WHITE; + UIColor::primary_txt = ST77XX_BLACK; + UIColor::secondary_txt = (18 << 11) | (36 << 5) | 18; + UIColor::warning_txt = ST77XX_ORANGE; + UIColor::popup_bkg = ST77XX_CYAN; + UIColor::popup_txt = ST77XX_BLACK; + UIColor::corp_blue = 0x001A; + return true; + } + return false; +} + bool ST7789LCDDisplay::begin() { if (!_isOn) { if (_peripher_power) _peripher_power->claim(); diff --git a/src/helpers/ui/ST7789LCDDisplay.h b/src/helpers/ui/ST7789LCDDisplay.h index b5127d3503..539afea425 100644 --- a/src/helpers/ui/ST7789LCDDisplay.h +++ b/src/helpers/ui/ST7789LCDDisplay.h @@ -57,4 +57,6 @@ class ST7789LCDDisplay : public DisplayDriver { void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; uint16_t getTextWidth(const char* str) override; void endFrame() override; + bool applyTheme(const char* theme) override; + int getThemeCount() override { return 2; } };