смена темы дисплея в рантайме: страница THEME, образцы цветов, сохранение в prefs
Build and deploy Docs site to GitHub Pages / github-pages (push) Canceled after 0s
Run Unit Tests / test (push) Canceled after 0s
PR Build Check / build (Heltec_v3_companion_radio_ble) (push) Canceled after 0s
PR Build Check / build (Heltec_v3_repeater) (push) Canceled after 0s
PR Build Check / build (Heltec_v3_room_server) (push) Canceled after 0s
PR Build Check / build (LilyGo_Tlora_C6_repeater_) (push) Canceled after 0s
PR Build Check / build (PicoW_repeater) (push) Canceled after 0s
PR Build Check / build (RAK_4631_companion_radio_ble) (push) Canceled after 0s
PR Build Check / build (RAK_4631_companion_radio_ethernet) (push) Canceled after 0s
PR Build Check / build (RAK_4631_repeater) (push) Canceled after 0s
PR Build Check / build (RAK_4631_repeater_ethernet) (push) Canceled after 0s
PR Build Check / build (RAK_4631_room_server) (push) Canceled after 0s
PR Build Check / build (RAK_4631_room_server_ethernet) (push) Canceled after 0s
PR Build Check / build (Tbeam_SX1276_repeater) (push) Canceled after 0s
PR Build Check / build (wio-e5-mini_repeater) (push) Canceled after 0s
PR Build Check / build (wio_wm1110_repeater) (push) Canceled after 0s
Build and deploy Docs site to GitHub Pages / github-pages (push) Canceled after 0s
Run Unit Tests / test (push) Canceled after 0s
PR Build Check / build (Heltec_v3_companion_radio_ble) (push) Canceled after 0s
PR Build Check / build (Heltec_v3_repeater) (push) Canceled after 0s
PR Build Check / build (Heltec_v3_room_server) (push) Canceled after 0s
PR Build Check / build (LilyGo_Tlora_C6_repeater_) (push) Canceled after 0s
PR Build Check / build (PicoW_repeater) (push) Canceled after 0s
PR Build Check / build (RAK_4631_companion_radio_ble) (push) Canceled after 0s
PR Build Check / build (RAK_4631_companion_radio_ethernet) (push) Canceled after 0s
PR Build Check / build (RAK_4631_repeater) (push) Canceled after 0s
PR Build Check / build (RAK_4631_repeater_ethernet) (push) Canceled after 0s
PR Build Check / build (RAK_4631_room_server) (push) Canceled after 0s
PR Build Check / build (RAK_4631_room_server_ethernet) (push) Canceled after 0s
PR Build Check / build (Tbeam_SX1276_repeater) (push) Canceled after 0s
PR Build Check / build (wio-e5-mini_repeater) (push) Canceled after 0s
PR Build Check / build (wio_wm1110_repeater) (push) Canceled after 0s
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user