From 952075a22ecc3d094f9f4912967ce2d7e4d33d69 Mon Sep 17 00:00:00 2001 From: "Michael A. Cojocari" Date: Mon, 31 Aug 2026 09:37:45 -0400 Subject: [PATCH] Day/Night --- deploy/apps/lang/21/bg.lang | 6 +- deploy/apps/lang/21/de.lang | 6 +- deploy/apps/lang/21/el.lang | 6 +- deploy/apps/lang/21/es.lang | 6 +- deploy/apps/lang/21/fr.lang | 6 +- deploy/apps/lang/21/hu.lang | 6 +- deploy/apps/lang/21/it.lang | 6 +- deploy/apps/lang/21/nl.lang | 6 +- deploy/apps/lang/21/pt-br.lang | 6 +- deploy/apps/lang/21/ro.lang | 6 +- deploy/apps/lang/21/ru.lang | 6 +- deploy/apps/lang/21/sr.lang | 6 +- deploy/apps/lang/21/uk.lang | 6 +- deploy/apps/lang/bg.lang | 6 +- deploy/apps/lang/de.lang | 6 +- deploy/apps/lang/el.lang | 6 +- deploy/apps/lang/es.lang | 6 +- deploy/apps/lang/fr.lang | 6 +- deploy/apps/lang/hu.lang | 6 +- deploy/apps/lang/it.lang | 6 +- deploy/apps/lang/nl.lang | 6 +- deploy/apps/lang/pt-br.lang | 6 +- deploy/apps/lang/ro.lang | 6 +- deploy/apps/lang/ru.lang | 6 +- deploy/apps/lang/sr.lang | 6 +- deploy/apps/lang/uk.lang | 6 +- src/helpers/esp32/TouchPrefsSchema.h | 11 +- src/helpers/esp32/TouchPrefsStore.cpp | 15 + src/helpers/esp32/TouchPrefsStore.h | 7 + src/ui-touch/ConsoleUI.cpp | 28 +- src/ui-touch/UITask.cpp | 815 ++++++++++++++++++-------- src/ui-touch/i18n_builtin.h | 78 ++- test/test_touch_prefs_schema.cpp | 34 +- 33 files changed, 836 insertions(+), 308 deletions(-) diff --git a/deploy/apps/lang/21/bg.lang b/deploy/apps/lang/21/bg.lang index cca93b6..258e514 100644 --- a/deploy/apps/lang/21/bg.lang +++ b/deploy/apps/lang/21/bg.lang @@ -32,6 +32,11 @@ About the map Относно картата Accent popups Изскачащи акценти Accent popups: off Изскачащи акценти: изкл Accent popups: on Изскачащи акценти: вкл +Accent colour Акцентен цвят +Appearance Изглед +Day Ден +Night Нощ +Theme save failed Неуспешно запазване на темата Ack Ack Active Активно Add Добави @@ -845,7 +850,6 @@ Temperature Температура Test builds: off Тестови компилации: изкл Test builds: on Тестови компилации: вкл Theme Тема -Theme colour Цвят на темата Then pick a mode: После избери режим: This contact hasn't shared\na GPS position. Този контакт не е споделил\nGPS позиция. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Това устройство може да ползва Wi-Fi и Bluetooth едновременно — като самостоятелно радио и телефонен компаньон (приложение MeshCore).\n\nДвете заедно ползват повече RAM, затова включвай само нужното.\n\nНастрой ги по всяко време в Настройки. diff --git a/deploy/apps/lang/21/de.lang b/deploy/apps/lang/21/de.lang index 58690f9..07a24e3 100644 --- a/deploy/apps/lang/21/de.lang +++ b/deploy/apps/lang/21/de.lang @@ -32,6 +32,11 @@ About the map Über die Karte Accent popups Akzent-Popups Accent popups: off Akzent-Popups: aus Accent popups: on Akzent-Popups: ein +Accent colour Akzentfarbe +Appearance Erscheinungsbild +Day Tag +Night Nacht +Theme save failed Design konnte nicht gespeichert werden Ack Ack Active Aktiv Add Hinzufügen @@ -841,7 +846,6 @@ Temperature Temperatur Test builds: off Test-Builds: aus Test builds: on Test-Builds: an Theme Thema -Theme colour Themenfarbe Then pick a mode: Dann einen Modus wählen: This contact hasn't shared\na GPS position. Dieser Kontakt hat keine\nGPS-Position geteilt. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Dieses Gerät kann Wi-Fi und Bluetooth gleichzeitig als eigenständiges Funkgerät und Telefon-Begleiter (MeshCore-App) betreiben.\n\nBeides gleichzeitig braucht mehr RAM, aktiviere also nur, was du brauchst.\n\nJederzeit in den Einstellungen einrichtbar. diff --git a/deploy/apps/lang/21/el.lang b/deploy/apps/lang/21/el.lang index 44c66c8..567bfd0 100644 --- a/deploy/apps/lang/21/el.lang +++ b/deploy/apps/lang/21/el.lang @@ -32,6 +32,11 @@ About the map Σχετικά με τον χάρτη Accent popups Αναδυόμενα τόνων Accent popups: off Αναδυόμενα τόνων: όχι Accent popups: on Αναδυόμενα τόνων: ναι +Accent colour Χρώμα έμφασης +Appearance Εμφάνιση +Day Ημέρα +Night Νύχτα +Theme save failed Αποτυχία αποθήκευσης θέματος Ack Ack Active Ενεργό Add Προσθήκη @@ -845,7 +850,6 @@ Temperature Θερμοκρασία Test builds: off Δοκιμαστικά builds: off Test builds: on Δοκιμαστικά builds: on Theme Θέμα -Theme colour Χρώμα θέματος Then pick a mode: Μετά επιλέξτε λειτουργία: This contact hasn't shared\na GPS position. Αυτή η επαφή δεν μοιράστηκε\nθέση GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Αυτή η συσκευή μπορεί να χρησιμοποιεί Wi-Fi και Bluetooth ταυτόχρονα, ως αυτόνομος πομποδέκτης και συνοδός τηλεφώνου (εφαρμογή MeshCore).\n\nΚαι τα δύο μαζί χρησιμοποιούν περισσότερη RAM, οπότε ενεργοποιήστε μόνο ό,τι χρειάζεστε.\n\nΡυθμίστε τα οποτεδήποτε στις Ρυθμίσεις. diff --git a/deploy/apps/lang/21/es.lang b/deploy/apps/lang/21/es.lang index c7b8bab..419b3aa 100644 --- a/deploy/apps/lang/21/es.lang +++ b/deploy/apps/lang/21/es.lang @@ -32,6 +32,11 @@ About the map Acerca del mapa Accent popups Ventanas de acentos Accent popups: off Ventanas de acentos: desactivado Accent popups: on Ventanas de acentos: activado +Accent colour Color de acento +Appearance Apariencia +Day Día +Night Noche +Theme save failed No se pudo guardar el tema Ack Ack Active Activo Add Añadir @@ -845,7 +850,6 @@ Temperature Temperatura Test builds: off Compilaciones de prueba: off Test builds: on Compilaciones de prueba: on Theme Tema -Theme colour Color del tema Then pick a mode: Luego elige un modo: This contact hasn't shared\na GPS position. Este contacto no ha compartido\nuna posición GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Este dispositivo puede usar Wi-Fi y Bluetooth a la vez, como radio independiente y compañero del teléfono (app MeshCore).\n\nUsar ambos consume más RAM, así que activa solo lo que necesites.\n\nConfigúralos cuando quieras en Ajustes. diff --git a/deploy/apps/lang/21/fr.lang b/deploy/apps/lang/21/fr.lang index cb92be8..e6f285a 100644 --- a/deploy/apps/lang/21/fr.lang +++ b/deploy/apps/lang/21/fr.lang @@ -32,6 +32,11 @@ About the map À propos de la carte Accent popups Fenêtres d'accents Accent popups: off Fenêtres d'accents : désactivé Accent popups: on Fenêtres d'accents : activé +Accent colour Couleur d'accent +Appearance Apparence +Day Jour +Night Nuit +Theme save failed Échec de l’enregistrement du thème Ack Accusé Active Actif Add Ajouter @@ -844,7 +849,6 @@ Temperature Température Test builds: off Builds de test : off Test builds: on Builds de test : on Theme Thème -Theme colour Couleur du thème Then pick a mode: Puis choisissez un mode : This contact hasn't shared\na GPS position. Ce contact n'a pas partagé\nde position GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Cet appareil peut utiliser Wi-Fi et Bluetooth en même temps, comme radio autonome et compagnon de téléphone (app MeshCore).\n\nLes deux à la fois consomment plus de RAM, n'activez que le nécessaire.\n\nRéglez-les à tout moment dans Réglages. diff --git a/deploy/apps/lang/21/hu.lang b/deploy/apps/lang/21/hu.lang index 5f6dddd..7cfd45a 100644 --- a/deploy/apps/lang/21/hu.lang +++ b/deploy/apps/lang/21/hu.lang @@ -32,6 +32,11 @@ About the map A térképről Accent popups Ékezetes felugró ablakok Accent popups: off Accent popups: kikapcsolva Accent popups: on Accent popups: bekapcsolva +Accent colour Kiemelőszín +Appearance Megjelenés +Day Nappali +Night Éjszakai +Theme save failed Téma mentése sikertelen Ack Ack Active Aktív Add Hozzáad @@ -861,7 +866,6 @@ Temperature Hőmérséklet Test builds: off Tesztverziók: kikapcsolva Test builds: on Tesztverziók: bekapcsolva Theme Téma -Theme colour Téma szine Then pick a mode: Majd válassz módot: This contact hasn't shared\na GPS position. Ez a kapcsolat nem osztotta meg GPS-pozícióját. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Ez az eszköz egyszerre képes Wi-Fi-t és Bluetooth-t futtatni, önálló rádióként és telefonkiegészítőként (MeshCore alkalmazás) is.\n\nA kettő egyidejű futtatása több RAM-ot fogyaszt, ezért csak azt kapcsold be, amire szükséged van.\n\nBármikor beállíthatod őket a Beállításokban. diff --git a/deploy/apps/lang/21/it.lang b/deploy/apps/lang/21/it.lang index f5cc517..b6fff74 100644 --- a/deploy/apps/lang/21/it.lang +++ b/deploy/apps/lang/21/it.lang @@ -32,6 +32,11 @@ About the map Informazioni sulla mappa Accent popups Popup accenti Accent popups: off Popup accenti: disattivi Accent popups: on Popup accenti: attivi +Accent colour Colore accento +Appearance Aspetto +Day Giorno +Night Notte +Theme save failed Salvataggio tema non riuscito Ack Ack Active Attivo Add Aggiungi @@ -844,7 +849,6 @@ Temperature Temperatura Test builds: off Build di test: off Test builds: on Build di test: on Theme Tema -Theme colour Colore tema Then pick a mode: Poi scegli una modalità: This contact hasn't shared\na GPS position. Questo contatto non ha condiviso\nuna posizione GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Questo dispositivo può usare Wi-Fi e Bluetooth insieme, come radio autonoma e compagno del telefono (app MeshCore).\n\nUsarli entrambi consuma più RAM, quindi attiva solo ciò che serve.\n\nConfigurali quando vuoi in Impostazioni. diff --git a/deploy/apps/lang/21/nl.lang b/deploy/apps/lang/21/nl.lang index a2f82b6..31fa771 100644 --- a/deploy/apps/lang/21/nl.lang +++ b/deploy/apps/lang/21/nl.lang @@ -32,6 +32,11 @@ About the map Over de kaart Accent popups Accent-popups Accent popups: off Accent-popups: uit Accent popups: on Accent-popups: aan +Accent colour Accentkleur +Appearance Weergave +Day Dag +Night Nacht +Theme save failed Thema opslaan mislukt Ack Ack Active Actief Add Toevoegen @@ -842,7 +847,6 @@ Temperature Temperatuur Test builds: off Testbuilds: uit Test builds: on Testbuilds: aan Theme Thema -Theme colour Themakleur Then pick a mode: Kies daarna een modus: This contact hasn't shared\na GPS position. Dit contact heeft geen\nGPS-positie gedeeld. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Dit apparaat kan Wi-Fi en Bluetooth tegelijk draaien, als zelfstandige radio én telefoon-companion (MeshCore-app) samen.\n\nBeide tegelijk gebruikt meer RAM, dus zet alleen aan wat je nodig hebt.\n\nStel ze altijd in via Instellingen. diff --git a/deploy/apps/lang/21/pt-br.lang b/deploy/apps/lang/21/pt-br.lang index cb0a884..5b1c66c 100644 --- a/deploy/apps/lang/21/pt-br.lang +++ b/deploy/apps/lang/21/pt-br.lang @@ -32,6 +32,11 @@ About the map Sobre o mapa Accent popups Pop-ups de acento Accent popups: off Pop-ups de acento: desligados Accent popups: on Pop-ups de acento: ligados +Accent colour Cor de destaque +Appearance Aparência +Day Dia +Night Noite +Theme save failed Falha ao salvar o tema Ack Ack Active Ativo Add Adicionar @@ -844,7 +849,6 @@ Temperature Temperatura Test builds: off Builds de teste: off Test builds: on Builds de teste: on Theme Tema -Theme colour Cor do tema Then pick a mode: Depois escolha um modo: This contact hasn't shared\na GPS position. Este contato não compartilhou\numa posição GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Este aparelho pode usar Wi-Fi e Bluetooth ao mesmo tempo, como rádio autônomo e companheiro do celular (app MeshCore).\n\nUsar os dois juntos consome mais RAM, então ligue só o que precisar.\n\nConfigure quando quiser em Ajustes. diff --git a/deploy/apps/lang/21/ro.lang b/deploy/apps/lang/21/ro.lang index 6f56119..f777172 100644 --- a/deploy/apps/lang/21/ro.lang +++ b/deploy/apps/lang/21/ro.lang @@ -32,6 +32,11 @@ About the map Despre hartă Accent popups Popupuri cu accente Accent popups: off Popupuri accente: oprite Accent popups: on Popupuri accente: pornite +Accent colour Culoare de accent +Appearance Aspect +Day Zi +Night Noapte +Theme save failed Salvarea temei a eșuat Ack Ack Active Activ Add Adaugă @@ -845,7 +850,6 @@ Temperature Temperatură Test builds: off Versiuni de test: oprite Test builds: on Versiuni de test: pornite Theme Temă -Theme colour Culoare temă Then pick a mode: Apoi alege un mod: This contact hasn't shared\na GPS position. Acest contact nu a partajat\no poziţie GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Acest dispozitiv poate rula Wi-Fi şi Bluetooth simultan, ca radio de sine stătător şi companion de telefon (aplicaţia MeshCore) în acelaşi timp.\n\nRularea ambelor foloseşte mai multă memorie RAM, deci porneşte doar ce îţi trebuie.\n\nLe poţi configura oricând în Setări. diff --git a/deploy/apps/lang/21/ru.lang b/deploy/apps/lang/21/ru.lang index 8c2f272..c7ed2a3 100644 --- a/deploy/apps/lang/21/ru.lang +++ b/deploy/apps/lang/21/ru.lang @@ -32,6 +32,11 @@ About the map О карте Accent popups Подсказки акцентов Accent popups: off Подсказки акцентов: выкл Accent popups: on Подсказки акцентов: вкл +Accent colour Акцентный цвет +Appearance Оформление +Day День +Night Ночь +Theme save failed Не удалось сохранить тему Ack Подтв. Active Активно Add Добавить @@ -845,7 +850,6 @@ Temperature Температура Test builds: off Тестовые сборки: выкл Test builds: on Тестовые сборки: вкл Theme Тема -Theme colour Цвет темы Then pick a mode: Затем выберите режим: This contact hasn't shared\na GPS position. Этот контакт не делился\nGPS-позицией. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Это устройство может использовать Wi-Fi и Bluetooth одновременно — как автономную рацию и компаньон телефона (приложение MeshCore).\n\nОба сразу используют больше ОЗУ, включайте только нужное.\n\nНастройте их в любой момент в Настройках. diff --git a/deploy/apps/lang/21/sr.lang b/deploy/apps/lang/21/sr.lang index 8e7267b..c493525 100644 --- a/deploy/apps/lang/21/sr.lang +++ b/deploy/apps/lang/21/sr.lang @@ -32,6 +32,11 @@ About the map О мапи Accent popups Искачући акценти Accent popups: off Искачући акценти: искљ Accent popups: on Искачући акценти: укљ +Accent colour Акцентна боја +Appearance Изглед +Day Дан +Night Ноћ +Theme save failed Чување теме није успело Ack Ack Active Активно Add Додај @@ -845,7 +850,6 @@ Temperature Температура Test builds: off Тест верзије: искључено Test builds: on Тест верзије: укључено Theme Тема -Theme colour Боја теме Then pick a mode: Затим изабери режим: This contact hasn't shared\na GPS position. Овај контакт није поделио\nGPS позицију. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Овај уређај може користити Wi-Fi и Bluetooth истовремено — као самостални радио и телефонски пратилац (MeshCore апликација).\n\nОба заједно троше више RAM-а, зато укључи само оно што ти треба.\n\nПодеси их било кад у Подешавањима. diff --git a/deploy/apps/lang/21/uk.lang b/deploy/apps/lang/21/uk.lang index 10d5f67..aaf9caf 100644 --- a/deploy/apps/lang/21/uk.lang +++ b/deploy/apps/lang/21/uk.lang @@ -32,6 +32,11 @@ About the map Про карту Accent popups Підказки акцентів Accent popups: off Підказки акцентів: вимк Accent popups: on Підказки акцентів: увімк +Accent colour Акцентний колір +Appearance Вигляд +Day День +Night Ніч +Theme save failed Не вдалося зберегти тему Ack Підтв. Active Активно Add Додати @@ -845,7 +850,6 @@ Temperature Температура Test builds: off Тестові збірки: вимк Test builds: on Тестові збірки: увімк Theme Тема -Theme colour Колір теми Then pick a mode: Потім виберіть режим: This contact hasn't shared\na GPS position. Цей контакт не ділився\nGPS-позицією. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Цей пристрій може використовувати Wi-Fi і Bluetooth одночасно — як автономну рацію та компаньйон телефона (застосунок MeshCore).\n\nОбидва разом використовують більше ОЗП, вмикайте лише потрібне.\n\nНалаштуйте їх будь-коли в Налаштуваннях. diff --git a/deploy/apps/lang/bg.lang b/deploy/apps/lang/bg.lang index cca93b6..258e514 100644 --- a/deploy/apps/lang/bg.lang +++ b/deploy/apps/lang/bg.lang @@ -32,6 +32,11 @@ About the map Относно картата Accent popups Изскачащи акценти Accent popups: off Изскачащи акценти: изкл Accent popups: on Изскачащи акценти: вкл +Accent colour Акцентен цвят +Appearance Изглед +Day Ден +Night Нощ +Theme save failed Неуспешно запазване на темата Ack Ack Active Активно Add Добави @@ -845,7 +850,6 @@ Temperature Температура Test builds: off Тестови компилации: изкл Test builds: on Тестови компилации: вкл Theme Тема -Theme colour Цвят на темата Then pick a mode: После избери режим: This contact hasn't shared\na GPS position. Този контакт не е споделил\nGPS позиция. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Това устройство може да ползва Wi-Fi и Bluetooth едновременно — като самостоятелно радио и телефонен компаньон (приложение MeshCore).\n\nДвете заедно ползват повече RAM, затова включвай само нужното.\n\nНастрой ги по всяко време в Настройки. diff --git a/deploy/apps/lang/de.lang b/deploy/apps/lang/de.lang index 58690f9..07a24e3 100644 --- a/deploy/apps/lang/de.lang +++ b/deploy/apps/lang/de.lang @@ -32,6 +32,11 @@ About the map Über die Karte Accent popups Akzent-Popups Accent popups: off Akzent-Popups: aus Accent popups: on Akzent-Popups: ein +Accent colour Akzentfarbe +Appearance Erscheinungsbild +Day Tag +Night Nacht +Theme save failed Design konnte nicht gespeichert werden Ack Ack Active Aktiv Add Hinzufügen @@ -841,7 +846,6 @@ Temperature Temperatur Test builds: off Test-Builds: aus Test builds: on Test-Builds: an Theme Thema -Theme colour Themenfarbe Then pick a mode: Dann einen Modus wählen: This contact hasn't shared\na GPS position. Dieser Kontakt hat keine\nGPS-Position geteilt. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Dieses Gerät kann Wi-Fi und Bluetooth gleichzeitig als eigenständiges Funkgerät und Telefon-Begleiter (MeshCore-App) betreiben.\n\nBeides gleichzeitig braucht mehr RAM, aktiviere also nur, was du brauchst.\n\nJederzeit in den Einstellungen einrichtbar. diff --git a/deploy/apps/lang/el.lang b/deploy/apps/lang/el.lang index 44c66c8..567bfd0 100644 --- a/deploy/apps/lang/el.lang +++ b/deploy/apps/lang/el.lang @@ -32,6 +32,11 @@ About the map Σχετικά με τον χάρτη Accent popups Αναδυόμενα τόνων Accent popups: off Αναδυόμενα τόνων: όχι Accent popups: on Αναδυόμενα τόνων: ναι +Accent colour Χρώμα έμφασης +Appearance Εμφάνιση +Day Ημέρα +Night Νύχτα +Theme save failed Αποτυχία αποθήκευσης θέματος Ack Ack Active Ενεργό Add Προσθήκη @@ -845,7 +850,6 @@ Temperature Θερμοκρασία Test builds: off Δοκιμαστικά builds: off Test builds: on Δοκιμαστικά builds: on Theme Θέμα -Theme colour Χρώμα θέματος Then pick a mode: Μετά επιλέξτε λειτουργία: This contact hasn't shared\na GPS position. Αυτή η επαφή δεν μοιράστηκε\nθέση GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Αυτή η συσκευή μπορεί να χρησιμοποιεί Wi-Fi και Bluetooth ταυτόχρονα, ως αυτόνομος πομποδέκτης και συνοδός τηλεφώνου (εφαρμογή MeshCore).\n\nΚαι τα δύο μαζί χρησιμοποιούν περισσότερη RAM, οπότε ενεργοποιήστε μόνο ό,τι χρειάζεστε.\n\nΡυθμίστε τα οποτεδήποτε στις Ρυθμίσεις. diff --git a/deploy/apps/lang/es.lang b/deploy/apps/lang/es.lang index c7b8bab..419b3aa 100644 --- a/deploy/apps/lang/es.lang +++ b/deploy/apps/lang/es.lang @@ -32,6 +32,11 @@ About the map Acerca del mapa Accent popups Ventanas de acentos Accent popups: off Ventanas de acentos: desactivado Accent popups: on Ventanas de acentos: activado +Accent colour Color de acento +Appearance Apariencia +Day Día +Night Noche +Theme save failed No se pudo guardar el tema Ack Ack Active Activo Add Añadir @@ -845,7 +850,6 @@ Temperature Temperatura Test builds: off Compilaciones de prueba: off Test builds: on Compilaciones de prueba: on Theme Tema -Theme colour Color del tema Then pick a mode: Luego elige un modo: This contact hasn't shared\na GPS position. Este contacto no ha compartido\nuna posición GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Este dispositivo puede usar Wi-Fi y Bluetooth a la vez, como radio independiente y compañero del teléfono (app MeshCore).\n\nUsar ambos consume más RAM, así que activa solo lo que necesites.\n\nConfigúralos cuando quieras en Ajustes. diff --git a/deploy/apps/lang/fr.lang b/deploy/apps/lang/fr.lang index cb92be8..e6f285a 100644 --- a/deploy/apps/lang/fr.lang +++ b/deploy/apps/lang/fr.lang @@ -32,6 +32,11 @@ About the map À propos de la carte Accent popups Fenêtres d'accents Accent popups: off Fenêtres d'accents : désactivé Accent popups: on Fenêtres d'accents : activé +Accent colour Couleur d'accent +Appearance Apparence +Day Jour +Night Nuit +Theme save failed Échec de l’enregistrement du thème Ack Accusé Active Actif Add Ajouter @@ -844,7 +849,6 @@ Temperature Température Test builds: off Builds de test : off Test builds: on Builds de test : on Theme Thème -Theme colour Couleur du thème Then pick a mode: Puis choisissez un mode : This contact hasn't shared\na GPS position. Ce contact n'a pas partagé\nde position GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Cet appareil peut utiliser Wi-Fi et Bluetooth en même temps, comme radio autonome et compagnon de téléphone (app MeshCore).\n\nLes deux à la fois consomment plus de RAM, n'activez que le nécessaire.\n\nRéglez-les à tout moment dans Réglages. diff --git a/deploy/apps/lang/hu.lang b/deploy/apps/lang/hu.lang index 5f6dddd..7cfd45a 100644 --- a/deploy/apps/lang/hu.lang +++ b/deploy/apps/lang/hu.lang @@ -32,6 +32,11 @@ About the map A térképről Accent popups Ékezetes felugró ablakok Accent popups: off Accent popups: kikapcsolva Accent popups: on Accent popups: bekapcsolva +Accent colour Kiemelőszín +Appearance Megjelenés +Day Nappali +Night Éjszakai +Theme save failed Téma mentése sikertelen Ack Ack Active Aktív Add Hozzáad @@ -861,7 +866,6 @@ Temperature Hőmérséklet Test builds: off Tesztverziók: kikapcsolva Test builds: on Tesztverziók: bekapcsolva Theme Téma -Theme colour Téma szine Then pick a mode: Majd válassz módot: This contact hasn't shared\na GPS position. Ez a kapcsolat nem osztotta meg GPS-pozícióját. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Ez az eszköz egyszerre képes Wi-Fi-t és Bluetooth-t futtatni, önálló rádióként és telefonkiegészítőként (MeshCore alkalmazás) is.\n\nA kettő egyidejű futtatása több RAM-ot fogyaszt, ezért csak azt kapcsold be, amire szükséged van.\n\nBármikor beállíthatod őket a Beállításokban. diff --git a/deploy/apps/lang/it.lang b/deploy/apps/lang/it.lang index f5cc517..b6fff74 100644 --- a/deploy/apps/lang/it.lang +++ b/deploy/apps/lang/it.lang @@ -32,6 +32,11 @@ About the map Informazioni sulla mappa Accent popups Popup accenti Accent popups: off Popup accenti: disattivi Accent popups: on Popup accenti: attivi +Accent colour Colore accento +Appearance Aspetto +Day Giorno +Night Notte +Theme save failed Salvataggio tema non riuscito Ack Ack Active Attivo Add Aggiungi @@ -844,7 +849,6 @@ Temperature Temperatura Test builds: off Build di test: off Test builds: on Build di test: on Theme Tema -Theme colour Colore tema Then pick a mode: Poi scegli una modalità: This contact hasn't shared\na GPS position. Questo contatto non ha condiviso\nuna posizione GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Questo dispositivo può usare Wi-Fi e Bluetooth insieme, come radio autonoma e compagno del telefono (app MeshCore).\n\nUsarli entrambi consuma più RAM, quindi attiva solo ciò che serve.\n\nConfigurali quando vuoi in Impostazioni. diff --git a/deploy/apps/lang/nl.lang b/deploy/apps/lang/nl.lang index a2f82b6..31fa771 100644 --- a/deploy/apps/lang/nl.lang +++ b/deploy/apps/lang/nl.lang @@ -32,6 +32,11 @@ About the map Over de kaart Accent popups Accent-popups Accent popups: off Accent-popups: uit Accent popups: on Accent-popups: aan +Accent colour Accentkleur +Appearance Weergave +Day Dag +Night Nacht +Theme save failed Thema opslaan mislukt Ack Ack Active Actief Add Toevoegen @@ -842,7 +847,6 @@ Temperature Temperatuur Test builds: off Testbuilds: uit Test builds: on Testbuilds: aan Theme Thema -Theme colour Themakleur Then pick a mode: Kies daarna een modus: This contact hasn't shared\na GPS position. Dit contact heeft geen\nGPS-positie gedeeld. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Dit apparaat kan Wi-Fi en Bluetooth tegelijk draaien, als zelfstandige radio én telefoon-companion (MeshCore-app) samen.\n\nBeide tegelijk gebruikt meer RAM, dus zet alleen aan wat je nodig hebt.\n\nStel ze altijd in via Instellingen. diff --git a/deploy/apps/lang/pt-br.lang b/deploy/apps/lang/pt-br.lang index cb0a884..5b1c66c 100644 --- a/deploy/apps/lang/pt-br.lang +++ b/deploy/apps/lang/pt-br.lang @@ -32,6 +32,11 @@ About the map Sobre o mapa Accent popups Pop-ups de acento Accent popups: off Pop-ups de acento: desligados Accent popups: on Pop-ups de acento: ligados +Accent colour Cor de destaque +Appearance Aparência +Day Dia +Night Noite +Theme save failed Falha ao salvar o tema Ack Ack Active Ativo Add Adicionar @@ -844,7 +849,6 @@ Temperature Temperatura Test builds: off Builds de teste: off Test builds: on Builds de teste: on Theme Tema -Theme colour Cor do tema Then pick a mode: Depois escolha um modo: This contact hasn't shared\na GPS position. Este contato não compartilhou\numa posição GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Este aparelho pode usar Wi-Fi e Bluetooth ao mesmo tempo, como rádio autônomo e companheiro do celular (app MeshCore).\n\nUsar os dois juntos consome mais RAM, então ligue só o que precisar.\n\nConfigure quando quiser em Ajustes. diff --git a/deploy/apps/lang/ro.lang b/deploy/apps/lang/ro.lang index 6f56119..f777172 100644 --- a/deploy/apps/lang/ro.lang +++ b/deploy/apps/lang/ro.lang @@ -32,6 +32,11 @@ About the map Despre hartă Accent popups Popupuri cu accente Accent popups: off Popupuri accente: oprite Accent popups: on Popupuri accente: pornite +Accent colour Culoare de accent +Appearance Aspect +Day Zi +Night Noapte +Theme save failed Salvarea temei a eșuat Ack Ack Active Activ Add Adaugă @@ -845,7 +850,6 @@ Temperature Temperatură Test builds: off Versiuni de test: oprite Test builds: on Versiuni de test: pornite Theme Temă -Theme colour Culoare temă Then pick a mode: Apoi alege un mod: This contact hasn't shared\na GPS position. Acest contact nu a partajat\no poziţie GPS. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Acest dispozitiv poate rula Wi-Fi şi Bluetooth simultan, ca radio de sine stătător şi companion de telefon (aplicaţia MeshCore) în acelaşi timp.\n\nRularea ambelor foloseşte mai multă memorie RAM, deci porneşte doar ce îţi trebuie.\n\nLe poţi configura oricând în Setări. diff --git a/deploy/apps/lang/ru.lang b/deploy/apps/lang/ru.lang index 8c2f272..c7ed2a3 100644 --- a/deploy/apps/lang/ru.lang +++ b/deploy/apps/lang/ru.lang @@ -32,6 +32,11 @@ About the map О карте Accent popups Подсказки акцентов Accent popups: off Подсказки акцентов: выкл Accent popups: on Подсказки акцентов: вкл +Accent colour Акцентный цвет +Appearance Оформление +Day День +Night Ночь +Theme save failed Не удалось сохранить тему Ack Подтв. Active Активно Add Добавить @@ -845,7 +850,6 @@ Temperature Температура Test builds: off Тестовые сборки: выкл Test builds: on Тестовые сборки: вкл Theme Тема -Theme colour Цвет темы Then pick a mode: Затем выберите режим: This contact hasn't shared\na GPS position. Этот контакт не делился\nGPS-позицией. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Это устройство может использовать Wi-Fi и Bluetooth одновременно — как автономную рацию и компаньон телефона (приложение MeshCore).\n\nОба сразу используют больше ОЗУ, включайте только нужное.\n\nНастройте их в любой момент в Настройках. diff --git a/deploy/apps/lang/sr.lang b/deploy/apps/lang/sr.lang index 8e7267b..c493525 100644 --- a/deploy/apps/lang/sr.lang +++ b/deploy/apps/lang/sr.lang @@ -32,6 +32,11 @@ About the map О мапи Accent popups Искачући акценти Accent popups: off Искачући акценти: искљ Accent popups: on Искачући акценти: укљ +Accent colour Акцентна боја +Appearance Изглед +Day Дан +Night Ноћ +Theme save failed Чување теме није успело Ack Ack Active Активно Add Додај @@ -845,7 +850,6 @@ Temperature Температура Test builds: off Тест верзије: искључено Test builds: on Тест верзије: укључено Theme Тема -Theme colour Боја теме Then pick a mode: Затим изабери режим: This contact hasn't shared\na GPS position. Овај контакт није поделио\nGPS позицију. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Овај уређај може користити Wi-Fi и Bluetooth истовремено — као самостални радио и телефонски пратилац (MeshCore апликација).\n\nОба заједно троше више RAM-а, зато укључи само оно што ти треба.\n\nПодеси их било кад у Подешавањима. diff --git a/deploy/apps/lang/uk.lang b/deploy/apps/lang/uk.lang index 10d5f67..aaf9caf 100644 --- a/deploy/apps/lang/uk.lang +++ b/deploy/apps/lang/uk.lang @@ -32,6 +32,11 @@ About the map Про карту Accent popups Підказки акцентів Accent popups: off Підказки акцентів: вимк Accent popups: on Підказки акцентів: увімк +Accent colour Акцентний колір +Appearance Вигляд +Day День +Night Ніч +Theme save failed Не вдалося зберегти тему Ack Підтв. Active Активно Add Додати @@ -845,7 +850,6 @@ Temperature Температура Test builds: off Тестові збірки: вимк Test builds: on Тестові збірки: увімк Theme Тема -Theme colour Колір теми Then pick a mode: Потім виберіть режим: This contact hasn't shared\na GPS position. Цей контакт не ділився\nGPS-позицією. This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings. Цей пристрій може використовувати Wi-Fi і Bluetooth одночасно — як автономну рацію та компаньйон телефона (застосунок MeshCore).\n\nОбидва разом використовують більше ОЗП, вмикайте лише потрібне.\n\nНалаштуйте їх будь-коли в Налаштуваннях. diff --git a/src/helpers/esp32/TouchPrefsSchema.h b/src/helpers/esp32/TouchPrefsSchema.h index 7b2eb3c..ed79932 100644 --- a/src/helpers/esp32/TouchPrefsSchema.h +++ b/src/helpers/esp32/TouchPrefsSchema.h @@ -9,7 +9,7 @@ namespace TouchPrefsSchema { static constexpr uint16_t MAGIC = 0x5743; // 'WC' (WadaCfg) -static constexpr uint8_t CURRENT_VERSION = 53; // v49: fem_lna default flip on the V4-R8; v50: map tile z/x/y line off by default (no new fields); v51: console_mode (boot into the text console; new trailing field, default OFF); v52: console_monitor (show incoming messages in the console, default ON) +static constexpr uint8_t CURRENT_VERSION = 54; // v54: Day/Night firmware theme (new trailing field, default Night) static constexpr uint8_t BROKEN_MID_INSERT_VERSION = 44; // Persisted byte layout. New fields must be appended at the end: older blobs @@ -89,6 +89,7 @@ struct __attribute__((packed)) Config { // a setting to fix it. This is reachable by touch, so it is a way out that // does not need the keyboard that is broken (#341, #351). uint8_t kb_force_legacy; + uint8_t theme_mode; // v54: 0 Night, 1 Day }; static constexpr size_t HEADER_SIZE = offsetof(Config, bright); @@ -96,8 +97,11 @@ static constexpr size_t STABLE_V44_PREFIX_SIZE = offsetof(Config, web_mirror); static_assert(offsetof(Config, web_mirror) == offsetof(Config, rx_queue) + sizeof(Config::rx_queue), "the pre-v44 suffix moved"); -static_assert(offsetof(Config, kb_force_legacy) + sizeof(Config::kb_force_legacy) == sizeof(Config), +static_assert(offsetof(Config, theme_mode) + sizeof(Config::theme_mode) == sizeof(Config), "new preference fields must remain trailing"); +static_assert(offsetof(Config, theme_mode) == + offsetof(Config, kb_force_legacy) + sizeof(Config::kb_force_legacy), + "theme_mode must follow kb_force_legacy"); // lang_file must stay immediately before the tail, or a v48-era blob overlays // onto the wrong bytes. Checking both ends means the next person to append is // told at compile time instead of shipping another v44. @@ -107,6 +111,9 @@ static_assert(offsetof(Config, console_mode) == static_assert(offsetof(Config, console_monitor) == offsetof(Config, console_mode) + sizeof(Config::console_mode), "console_monitor must follow console_mode"); +static_assert(offsetof(Config, kb_force_legacy) == + offsetof(Config, console_monitor) + sizeof(Config::console_monitor), + "kb_force_legacy must follow console_monitor"); // Overlay a persisted blob on caller-provided defaults. Beta 57 wrote v44 with // retry_echo inserted before web_mirror, shifting every later value. That blob diff --git a/src/helpers/esp32/TouchPrefsStore.cpp b/src/helpers/esp32/TouchPrefsStore.cpp index 10a0026..432c993 100644 --- a/src/helpers/esp32/TouchPrefsStore.cpp +++ b/src/helpers/esp32/TouchPrefsStore.cpp @@ -39,6 +39,7 @@ static const char* KEY_CFG = "cfg"; static const uint16_t TOUCH_CFG_MAGIC = TouchPrefsSchema::MAGIC; static const uint8_t TOUCH_CFG_VER = TouchPrefsSchema::CURRENT_VERSION; // v2 sig_probe/poll; v3 tz_zone; v4 hide_node_name; v5 map_night/map_zoom; v6 map text/marker visibility; v7 app_grid_large; v8 ui_scale; v9 tb_keypad; v10 sleep_idle; v11 nav_keys; v12 map_zoom_buttons; v13 nav_dir_keys; v14 home_is_drawer; v15 kbd_nav default ON (one-time migrate); v16 nav_scroll_keys; v17 notify_new_contact; v18 kbd_nav OFF by default (reverses v15; T-Deck/V4 only, Tanmatsu stays on); v19 show_sensors_tab; v20 map_show_links; v21 map_style (0=OSM default, 1=OpenTopoMap); v22 tb_nav; v23 scope_direct (opt-in: scope direct/login floods to the region); v24 tb_nav default OFF (experimental); v25 fem_lna (Heltec V4.3 high-gain FEM LNA, opt-in); v26 msg_flash (flash keyboard backlight + wake screen on a new message, opt-in); v27 flood_adv_hrs + local_adv_min (periodic self-advert intervals, the standard MeshCore flood/local advert on a timer); v28 beta_updates (opt-in to test/beta firmware on the OTA update check + install); v29 ui_scale default -> Large/150% (Tanmatsu; bumps the old 100% default, leaves an explicit Large/Huge choice); v30 boot_advert (opt-in one-shot flood self-advert ~6s after boot, all boards, #76); v31 compact_chat (opt-in IRC-style dense chat rows instead of bubbles); v32 clock_floor (highest epoch handed out — monotonic send-timestamp floor across reboots, #89); v33 rx_queue (buffered LoRa receive: drain task + packet ring, experimental, default OFF); v34 web_mirror (web control panel: mirror the live UI to a phone browser + inject taps, opt-in, default OFF); v35 remote_mode (render the UI off-screen at a web resolution instead of the panel; boot mode, default OFF); v36 remote_landscape (remote mode orientation: landscape 800x480 vs portrait 480x800); v37 remote_landscape now defaults ON (remote mode = landscape/desktop by default; one-time flip of existing installs, portrait stays a toggle); v38 web_terminal (web mesh CLI terminal served on the device IP; runtime toggle, mutually exclusive with VNC, default OFF); v40 hist_sync_after (chat-history flush: consecutive off-thread write failures before the blocking loop-task fallback, 0 = never); v41 p4_antenna (T-Display P4 antenna select; now RESERVED/unused - the choice is session-only so every boot comes up on the on-board antenna); v42 hist_per_chat (max stored messages PER chat, default 250 - a busy public channel used to be able to fill the whole shared ring and drag the UI down); v43 Pager UI-size presets (reset the previously ignored large-screen default to Small once); v44 broken retry_echo mid-struct insertion; v45 moves retry_echo to the actual tail and resets the ambiguous v44 suffix; v46 app_hide; v47 MQTT hidden by default; v48 lang_file; v49 fem_lna default ON on the V4-R8 (KCT8103L FEM; one-time flip of existing installs, no new field); v50 map_show_tilexyz default OFF (tile z/x/y line hidden; one-time flip, no new field) +// v54 appends theme_mode (Night by default). // Defaults (kept identical to the historical per-key defaults). static const uint16_t DEFAULT_SCREEN_TIMEOUT_S = 20; static const uint8_t DEFAULT_BRIGHTNESS = 100; @@ -136,6 +137,7 @@ static void cfgSetDefaults(TouchCfg& c) { c.console_mode = 0; // OFF: boot into the graphical UI (CONSOLE_MODE.md) c.console_monitor = 1; // ON: the console shows messages as they arrive c.kb_force_legacy = 0; // OFF: detect the keyboard protocol automatically + c.theme_mode = 0; // Night: preserves the existing firmware appearance c.compact_chat = 0; // OFF: bubble chat layout (opt-in IRC-style dense rows) c.clock_floor = 0; // no persisted send-timestamp floor yet c.rx_queue = 1; // ON: buffered receive (test-channel default; opt-out toggle in Radio & Mesh) @@ -836,6 +838,19 @@ bool touchPrefsSetAccentColor(uint32_t rgb) { return cfgFlush(); } +uint8_t touchPrefsGetThemeMode() { + if (!s_begun) touchPrefsBegin(); + return s_cfg.theme_mode == TOUCH_THEME_DAY ? TOUCH_THEME_DAY : TOUCH_THEME_NIGHT; +} +bool touchPrefsSetThemeMode(uint8_t mode) { + if (!s_begun) touchPrefsBegin(); + const uint8_t old_mode = s_cfg.theme_mode; + s_cfg.theme_mode = mode == TOUCH_THEME_DAY ? TOUCH_THEME_DAY : TOUCH_THEME_NIGHT; + if (cfgFlush()) return true; + s_cfg.theme_mode = old_mode; + return false; +} + // Quick-reply macros. Stored as NVS strings keyed "qr0".."qr5". // Default factory set on first read so the picker isn't useless out of the // box and the user has examples to edit. diff --git a/src/helpers/esp32/TouchPrefsStore.h b/src/helpers/esp32/TouchPrefsStore.h index e0aed70..219d18e 100644 --- a/src/helpers/esp32/TouchPrefsStore.h +++ b/src/helpers/esp32/TouchPrefsStore.h @@ -9,6 +9,9 @@ #include #include // size_t (blob helpers below) +static constexpr uint8_t TOUCH_THEME_NIGHT = 0; +static constexpr uint8_t TOUCH_THEME_DAY = 1; + void touchPrefsBegin(); // Force a fresh load of the settings blob. Call after SdNvsPrefs::useFile() at // boot: an earlier pref read may have cached the blob from the legacy backend. @@ -27,6 +30,10 @@ bool touchPrefsSetScreenTimeoutSecs(uint16_t seconds); uint8_t touchPrefsGetBrightness(); bool touchPrefsSetBrightness(uint8_t pct); +/** Firmware palette: 0 = Night (default), 1 = Day. Applied after restart. */ +uint8_t touchPrefsGetThemeMode(); +bool touchPrefsSetThemeMode(uint8_t mode); + /** Keyboard backlight mode: 0 = off, 1 = on, 2 = auto (on while typing). Default auto. */ uint8_t touchPrefsGetKbBacklight(); bool touchPrefsSetKbBacklight(uint8_t mode); diff --git a/src/ui-touch/ConsoleUI.cpp b/src/ui-touch/ConsoleUI.cpp index e4a078f..0b25d62 100644 --- a/src/ui-touch/ConsoleUI.cpp +++ b/src/ui-touch/ConsoleUI.cpp @@ -164,11 +164,28 @@ uint8_t ringGetSpl2(int i) { // and the greens were unreadable. Owning both ends removes the dependency on // which definition happens to win at link time. #define RGB565(r, g, b) ((uint16_t)((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | ((b) >> 3))) -constexpr uint16_t kBg = RGB565(0x0E, 0x12, 0x16); // the firmware's near-black +bool s_day_theme = false; +uint16_t consoleBg() { + return s_day_theme ? RGB565(0xF1, 0xF4, 0xF6) : RGB565(0x0E, 0x12, 0x16); +} uint16_t colourFor(uint8_t c) { // e-ink has one ink colour; anything else is invisible or dithered. if (s_disp && s_disp->isEink()) return UIColor::primary_txt; + if (s_day_theme) { + switch (c) { + case CC_DIM: return RGB565(0x56, 0x60, 0x6C); + case CC_ECHO: + case CC_OK: return RGB565(0x27, 0x67, 0x38); + case CC_WARN: return RGB565(0x80, 0x60, 0x00); + case CC_ERR: return RGB565(0xA5, 0x2B, 0x26); + case CC_CHAN: return RGB565(0x1F, 0x5F, 0x9E); + case CC_SENDER: return RGB565(0x8A, 0x5A, 0x00); + case CC_DM: return RGB565(0x68, 0x42, 0xA0); + case CC_HEAD: return RGB565(0x08, 0x77, 0x6D); + default: return RGB565(0x17, 0x20, 0x26); + } + } switch (c) { case CC_DIM: return RGB565(0x7A, 0x7F, 0x87); // grey, the theme's "sub" case CC_ECHO: return RGB565(0x53, 0xC0, 0x6B); // green, the shell-echo convention @@ -321,7 +338,7 @@ void measure() { // ---- render ----------------------------------------------------------------- void render() { if (!s_disp || !s_active) return; - s_disp->startFrame(kBg); + s_disp->startFrame(consoleBg()); s_disp->setTextSize(1); // Oldest-first from the scroll position, newest at the bottom. @@ -410,7 +427,7 @@ void drawInputLine(bool cursor_on) { const int iy = s_disp->height() - s_line_h; #endif // Clear only this row. fillScreen would take the scrollback and keypad with it. - s_disp->setColor(kBg); + s_disp->setColor(consoleBg()); s_disp->fillRect(0, iy, s_disp->width(), s_line_h); s_disp->setTextSize(1); @@ -452,7 +469,7 @@ void drawInputLine(bool cursor_on) { // Toggle just the cursor cell. Two fillRects, no text, no clear. void drawCursorOnly(bool on) { if (!s_disp) return; - s_disp->setColor(on ? colourFor(CC_TEXT) : kBg); + s_disp->setColor(on ? colourFor(CC_TEXT) : consoleBg()); s_disp->fillRect(s_cur_x, s_cur_y, s_char_w, s_line_h); } @@ -704,6 +721,9 @@ void submit() { void consoleBegin(DisplayDriver* d) { s_disp = d; if (!s_disp) return; +#if defined(ESP32) + s_day_theme = touchPrefsGetThemeMode() == TOUCH_THEME_DAY; +#endif if (!s_ring) { const size_t bytes = (size_t)kMaxLines * (kLineCap + 1); #if defined(ESP32) diff --git a/src/ui-touch/UITask.cpp b/src/ui-touch/UITask.cpp index b1f40fa..7d29b11 100644 --- a/src/ui-touch/UITask.cpp +++ b/src/ui-touch/UITask.cpp @@ -429,17 +429,83 @@ static void pushDiagLine(const char* message); // real def near the diag log // and one cool slate accent; the warm/saturated colours (the status // amber + OD green + alert red) live in the status-color constants // below and are used only where state really matters. -constexpr uint32_t COLOR_BG = 0x000000; // pure black, OLED-style -constexpr uint32_t COLOR_PANEL = 0x040506; // essentially black panel +struct TouchPalette { + uint32_t bg; + uint32_t panel; + uint32_t text; + uint32_t sub; + uint32_t sent_bg; + uint32_t recv_bg; + uint32_t mention; + uint32_t mention_bg; + uint32_t status_ok; + uint32_t status_ok_pressed; + uint32_t status_warn; + uint32_t status_danger; + uint32_t status_danger_pressed; + uint32_t status_ok_text; + uint32_t status_warn_text; + uint32_t status_danger_text; + uint32_t status_info; + uint32_t border; + uint32_t field; + uint32_t control; + uint32_t control_disabled; + uint32_t control_pressed; + uint32_t accent_surface; + uint32_t accent_border; + uint32_t chart_grid; + uint32_t chart_tick; + uint32_t raised; + uint32_t secondary_action; + uint32_t track; + uint32_t chart_bg; +}; + +static constexpr TouchPalette kNightPalette = { + 0x000000, 0x040506, 0xE0E3E6, 0x828891, + 0x1D2226, 0x1B1D1F, 0x4FA3FF, 0x16324F, + 0x4A8E4A, 0x3B7039, 0xC8A030, 0xA04040, 0x7A2A2A, + 0x4A8E4A, 0xC8A030, 0xA04040, + 0x4F9DF7, + 0x18191A, 0x0A0B0C, 0x1A1B1C, 0x0C0D0E, 0x141516, + 0x1B2B3A, 0x2A3D52, 0x1A1D1F, 0x2A2E30, + 0x121417, 0x3A4A5C, 0x202428, 0x0B0D0F, +}; + +static constexpr TouchPalette kDayPalette = { + 0xF1F4F6, 0xFFFFFF, 0x172026, 0x52606C, + 0xDCE8EE, 0xE7EAED, 0x1F66A5, 0xDCEEFF, + 0x8FC395, 0x70A978, 0xE1C15A, 0xD99191, 0xC47777, + 0x276738, 0x806000, 0xA52B26, + 0xA8CDF0, + 0xC7D0D7, 0xF8FAFB, 0xE5EAEE, 0xD5DADF, 0xD4DCE2, + 0xD9EEEC, 0x6F9E99, 0xD6DDE2, 0xAAB5BD, + 0xFFFFFF, 0xD8E0E6, 0xD5DDE3, 0xF8FAFB, +}; + +static bool s_theme_day = false; +static uint32_t COLOR_BG = kNightPalette.bg; +static uint32_t COLOR_PANEL = kNightPalette.panel; // Earlier accent was 0x4E5C66 — RGB (78,92,102), cool blue-leaning. Even // at low opacity that tinted every chip and settings row blue. Switched // to a true neutral medium gray (very slight warm) so chip fills read as // "darker gray" rather than "blue". -// Runtime-themeable accent (Settings -> Theme colour). NOT constexpr: the picker +// Runtime-themeable accent (Settings -> Accent colour). NOT constexpr: the picker // rewrites these live and they're reloaded from the saved pref at boot. Every // accent site reads them through lv_color_hex(), so one write re-themes the UI. uint32_t COLOR_ACCENT = 0x15B6A6; // WADAMESH brand teal (default; the logo dots) uint32_t COLOR_ACCENT_PRESS = 0x0D766B; // darker brand teal (default) +static uint32_t COLOR_ON_ACCENT = kNightPalette.text; +static uint32_t COLOR_ON_STATUS_OK = kNightPalette.text; +static uint32_t COLOR_ON_STATUS_DANGER = kNightPalette.text; +static uint32_t COLOR_ON_STATUS_INFO = kNightPalette.text; +static uint32_t COLOR_CHAT_TEXT = kNightPalette.text; +static uint32_t COLOR_CHAT_META = kNightPalette.sub; +static uint32_t COLOR_CHAT_LINK = 0x4EA1FF; +static uint32_t COLOR_CHAT_SENT_BG = kNightPalette.sent_bg; +static uint32_t COLOR_CHAT_RECV_BG = kNightPalette.recv_bg; +static uint32_t COLOR_CHAT_MENTION_BG = kNightPalette.mention_bg; // Perceived luminance 0..255 (keep the accent dark enough for off-white text). static inline uint32_t accentLuma(uint32_t rgb) { @@ -450,28 +516,96 @@ static inline uint32_t accentDarken(uint32_t rgb, int pct) { uint32_t r=((rgb>>16)&0xFF)*pct/100, g=((rgb>>8)&0xFF)*pct/100, b=(rgb&0xFF)*pct/100; return (r<<16)|(g<<8)|b; } -// Clamp a picked accent dark enough that off-white button text stays readable. +// Clamp a picked accent dark enough that text/icons stay readable on solid fills. static inline uint32_t accentClampReadable(uint32_t rgb) { - const uint32_t kMaxLuma = 140; + const uint32_t kMaxLuma = s_theme_day ? 105 : 140; uint32_t L = accentLuma(rgb); if (L > kMaxLuma) return accentDarken(rgb, (int)(kMaxLuma * 100 / L)); return rgb & 0xFFFFFFu; } -constexpr uint32_t COLOR_TEXT = 0xE0E3E6; // clean off-white -constexpr uint32_t COLOR_SUB = 0x828891; // medium neutral gray +static uint32_t COLOR_TEXT = kNightPalette.text; +static uint32_t COLOR_SUB = kNightPalette.sub; // Chat-bubble palette: kept near-monochrome (military comms terminals // don't colour-code direction). Slight luminance + hue lean keeps L/R // readable but neither side gets a "fun" tint. -constexpr uint32_t COLOR_SENT_BG = 0x1D2226; // very subtle steel -constexpr uint32_t COLOR_RECV_BG = 0x1B1D1F; // warmer neutral gray -constexpr uint32_t COLOR_MENTION = 0x4FA3FF; // blue — @mentions of me -constexpr uint32_t COLOR_MENTION_BG = 0x16324F; // blue-tinted bubble for a message that @mentions me +static uint32_t COLOR_SENT_BG = kNightPalette.sent_bg; +static uint32_t COLOR_RECV_BG = kNightPalette.recv_bg; +static uint32_t COLOR_MENTION = kNightPalette.mention; +static uint32_t COLOR_MENTION_BG = kNightPalette.mention_bg; // Functional status colours — use sparingly. These are the ONLY warm/ // saturated colours in the palette, so when they appear the operator // instantly reads them as state, not decoration. -constexpr uint32_t COLOR_STATUS_OK = 0x4A8E4A; // muted go-green -constexpr uint32_t COLOR_STATUS_WARN = 0xC8A030; // dim amber -constexpr uint32_t COLOR_STATUS_DANGER= 0xA04040; // muted red +static uint32_t COLOR_STATUS_OK = kNightPalette.status_ok; +static uint32_t COLOR_STATUS_OK_PRESSED = kNightPalette.status_ok_pressed; +static uint32_t COLOR_STATUS_WARN = kNightPalette.status_warn; +static uint32_t COLOR_STATUS_DANGER = kNightPalette.status_danger; +static uint32_t COLOR_STATUS_DANGER_PRESSED = kNightPalette.status_danger_pressed; +static uint32_t COLOR_STATUS_OK_TEXT = kNightPalette.status_ok_text; +static uint32_t COLOR_STATUS_WARN_TEXT = kNightPalette.status_warn_text; +static uint32_t COLOR_STATUS_DANGER_TEXT = kNightPalette.status_danger_text; +static uint32_t COLOR_STATUS_INFO = kNightPalette.status_info; +static uint32_t COLOR_BORDER = kNightPalette.border; +static uint32_t COLOR_FIELD = kNightPalette.field; +static uint32_t COLOR_CONTROL = kNightPalette.control; +static uint32_t COLOR_CONTROL_DISABLED = kNightPalette.control_disabled; +static uint32_t COLOR_CONTROL_PRESSED = kNightPalette.control_pressed; +static uint32_t COLOR_ACCENT_SURFACE = kNightPalette.accent_surface; +static uint32_t COLOR_ACCENT_BORDER = kNightPalette.accent_border; +static uint32_t COLOR_CHART_GRID = kNightPalette.chart_grid; +static uint32_t COLOR_CHART_TICK = kNightPalette.chart_tick; +static uint32_t COLOR_RAISED = kNightPalette.raised; +static uint32_t COLOR_SECONDARY_ACTION = kNightPalette.secondary_action; +static uint32_t COLOR_TRACK = kNightPalette.track; +static uint32_t COLOR_CHART_BG = kNightPalette.chart_bg; + +static void applyThemeMode(uint8_t mode) { + s_theme_day = mode == TOUCH_THEME_DAY; + const TouchPalette& p = s_theme_day ? kDayPalette : kNightPalette; + COLOR_BG = p.bg; + COLOR_PANEL = p.panel; + COLOR_TEXT = p.text; + COLOR_SUB = p.sub; + COLOR_SENT_BG = p.sent_bg; + COLOR_RECV_BG = p.recv_bg; + COLOR_MENTION = p.mention; + COLOR_MENTION_BG = p.mention_bg; + COLOR_STATUS_OK = p.status_ok; + COLOR_STATUS_OK_PRESSED = p.status_ok_pressed; + COLOR_STATUS_WARN = p.status_warn; + COLOR_STATUS_DANGER = p.status_danger; + COLOR_STATUS_DANGER_PRESSED = p.status_danger_pressed; + COLOR_STATUS_OK_TEXT = p.status_ok_text; + COLOR_STATUS_WARN_TEXT = p.status_warn_text; + COLOR_STATUS_DANGER_TEXT = p.status_danger_text; + COLOR_STATUS_INFO = p.status_info; + COLOR_BORDER = p.border; + COLOR_FIELD = p.field; + COLOR_CONTROL = p.control; + COLOR_CONTROL_DISABLED = p.control_disabled; + COLOR_CONTROL_PRESSED = p.control_pressed; + COLOR_ACCENT_SURFACE = p.accent_surface; + COLOR_ACCENT_BORDER = p.accent_border; + COLOR_CHART_GRID = p.chart_grid; + COLOR_CHART_TICK = p.chart_tick; + COLOR_RAISED = p.raised; + COLOR_SECONDARY_ACTION = p.secondary_action; + COLOR_TRACK = p.track; + COLOR_CHART_BG = p.chart_bg; + COLOR_ON_ACCENT = s_theme_day ? 0xFFFFFFu : p.text; + COLOR_ON_STATUS_OK = s_theme_day ? 0x15351Du : p.text; + COLOR_ON_STATUS_DANGER = s_theme_day ? 0x571515u : p.text; + COLOR_ON_STATUS_INFO = s_theme_day ? 0x103A5Au : p.text; + COLOR_CHAT_TEXT = s_theme_day ? 0xFFFFFFu : p.text; + COLOR_CHAT_META = s_theme_day ? 0xFFFFFFu : p.sub; + COLOR_CHAT_LINK = s_theme_day ? 0x9EDBFFu : 0x4EA1FFu; + COLOR_CHAT_SENT_BG = s_theme_day ? 0x28556Bu : p.sent_bg; + COLOR_CHAT_RECV_BG = s_theme_day ? 0x3C4852u : p.recv_bg; + COLOR_CHAT_MENTION_BG = s_theme_day ? 0x1D5F8Au : p.mention_bg; +} + +static inline uint32_t themeRole(uint32_t night, uint32_t day) { + return s_theme_day ? day : night; +} // LVGL 8.3 / Montserrat doesn't ship a STAR glyph. We carry a small custom // font subset (one glyph: U+2605 BLACK STAR at size 28) in @@ -502,6 +636,7 @@ extern "C" const lv_font_t zoom_font; // glyphs render at any of those sizes (the status-bar sleep icon uses g_font_12; // the battery-chart sleep markers will use g_font_14). extern "C" const lv_font_t sleepicons_font; +#define TOUCH_SYM_SUN "\xEF\x86\x85" /* U+F185 sun */ #define TOUCH_SYM_MOON "\xEF\x86\x86" /* U+F186 moon */ // FontAwesome lock (U+F023) + bell (U+F0F3) + bell-slash (U+F1F6), 16 px — for the // control-center chips (real lock instead of an eye; a dynamic bell that gains a slash @@ -2858,7 +2993,7 @@ static void styleSurface(lv_obj_t* obj, uint32_t bg, lv_coord_t radius = 10) { static void styleCard(lv_obj_t* obj) { styleSurface(obj, COLOR_PANEL, 10); lv_obj_set_style_border_width(obj, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(obj, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(obj, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); } // LVGL draws a text area's PLACEHOLDER from LV_PART_TEXTAREA_PLACEHOLDER, and that @@ -2939,7 +3074,7 @@ static lv_obj_t* addCloseXBadge(lv_obj_t* card, lv_event_cb_t cb, void* user_dat // No fill / border — bare glyph. Pressed state nudges a faint dim so // there's *some* visual feedback on tap. lv_obj_set_style_bg_opa(x, LV_OPA_TRANSP, LV_PART_MAIN); - lv_obj_set_style_bg_color(x, lv_color_hex(0xFFFFFF), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(x, lv_color_hex(themeRole(0xFFFFFF, COLOR_CONTROL_PRESSED)), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_bg_opa(x, LV_OPA_20, LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_radius(x, 12, LV_PART_MAIN); lv_obj_set_style_border_width(x, 0, LV_PART_MAIN); @@ -4936,7 +5071,7 @@ static void chatVirtJumpToLatest(LvChatPanel* p); static void chatVirtScheduleRender(LvChatPanel* p); static void chatVirtApplyPendingScroll(LvChatPanel* p); static void refreshChatList(LvChatPanel& p); -static void applyAccent(uint32_t rgb); // theme accent (Settings -> Theme colour) +static void applyAccent(uint32_t rgb); // theme accent (Settings -> Accent colour) static void openAccentPicker(); static void openAccentPickerCb(lv_event_t* e); static void openChannelScopeModal(int slot, const char* name); // per-channel region scope @@ -5469,8 +5604,9 @@ static void otaButtonRefreshState() { lv_obj_add_flag(s_ota_btn, LV_OBJ_FLAG_CLICKABLE); lv_obj_clear_state(s_ota_btn, LV_STATE_DISABLED); lv_obj_set_style_bg_color(s_ota_btn, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(s_ota_btn, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_ota_btn, LV_OPA_COVER, LV_PART_MAIN); - if (s_ota_btn_lbl) lv_obj_set_style_text_color(s_ota_btn_lbl, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + if (s_ota_btn_lbl) lv_obj_set_style_text_color(s_ota_btn_lbl, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); } else { lv_obj_clear_flag(s_ota_btn, LV_OBJ_FLAG_CLICKABLE); lv_obj_add_state(s_ota_btn, LV_STATE_DISABLED); @@ -6153,9 +6289,9 @@ static void kbMirrorEnsureCreated() { lv_obj_set_size(s_kb_mirror_ta, 224, 30); lv_obj_align(s_kb_mirror_ta, LV_ALIGN_BOTTOM_LEFT, 0, 0); lv_textarea_set_one_line(s_kb_mirror_ta, true); - lv_obj_set_style_bg_color(s_kb_mirror_ta, lv_color_hex(0x0A0B0C), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_kb_mirror_ta, lv_color_hex(COLOR_FIELD), LV_PART_MAIN); lv_obj_set_style_text_color(s_kb_mirror_ta, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); - lv_obj_set_style_border_color(s_kb_mirror_ta, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(s_kb_mirror_ta, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_kb_mirror_ta, 1, LV_PART_MAIN); lv_obj_set_style_text_font(s_kb_mirror_ta, &g_font_14, LV_PART_MAIN); // Live-sync the mirror's text into the bound real textarea on every @@ -6458,7 +6594,8 @@ static void accentPopupHighlight() { for (int i = 0; i < s_acc_n; ++i) { if (!s_acc_cells[i]) continue; const bool sel = (i == s_acc_idx); - lv_obj_set_style_bg_color(s_acc_cells[i], lv_color_hex(sel ? COLOR_ACCENT : 0x10202E), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_acc_cells[i], + lv_color_hex(sel ? COLOR_ACCENT : themeRole(0x10202E, COLOR_ACCENT_SURFACE)), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_acc_cells[i], LV_OPA_COVER, LV_PART_MAIN); } } @@ -6489,7 +6626,7 @@ static void accentPopupShow() { lv_obj_set_style_bg_color(s_acc_popup, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_acc_popup, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(s_acc_popup, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(s_acc_popup, lv_color_hex(0x2A3D52), LV_PART_MAIN); + lv_obj_set_style_border_color(s_acc_popup, lv_color_hex(COLOR_ACCENT_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_acc_popup, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(s_acc_popup, pad, LV_PART_MAIN); lv_obj_set_style_pad_column(s_acc_popup, gap, LV_PART_MAIN); @@ -6704,7 +6841,7 @@ static void accentNavRestyle() { for (uint8_t i = 0; i < s_accbox_cell_n; ++i) { if (!s_accbox_cells[i]) continue; lv_obj_set_style_bg_color(s_accbox_cells[i], - lv_color_hex((int)i == s_accentnav_idx ? COLOR_ACCENT : 0x1B2B3A), LV_PART_MAIN); + lv_color_hex((int)i == s_accentnav_idx ? COLOR_ACCENT : COLOR_ACCENT_SURFACE), LV_PART_MAIN); } } // Encoder's short-click while picking: fire the highlighted cell's own CLICKED @@ -6761,7 +6898,7 @@ static void accentBoxMaybeShow() { lv_obj_set_style_bg_color(s_accbox, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_accbox, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(s_accbox, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(s_accbox, lv_color_hex(0x2A3D52), LV_PART_MAIN); + lv_obj_set_style_border_color(s_accbox, lv_color_hex(COLOR_ACCENT_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_accbox, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(s_accbox, pad, LV_PART_MAIN); lv_obj_set_style_pad_column(s_accbox, gap, LV_PART_MAIN); @@ -6773,7 +6910,7 @@ static void accentBoxMaybeShow() { lv_obj_add_flag(c, NAV_SKIP_FLAG); // tappable, but never a keyboard-nav focus stop lv_obj_set_size(c, cw, ch); lv_obj_set_style_radius(c, 5, LV_PART_MAIN); - lv_obj_set_style_bg_color(c, lv_color_hex(0x1B2B3A), LV_PART_MAIN); + lv_obj_set_style_bg_color(c, lv_color_hex(COLOR_ACCENT_SURFACE), LV_PART_MAIN); lv_obj_set_style_bg_color(c, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_add_event_cb(c, accentBoxCellCb, LV_EVENT_CLICKED, (void*)set->v[i]); #if defined(TLORA_PAGER) @@ -6839,7 +6976,7 @@ static void mentionNavRestyle() { for (uint8_t i = 0; i < s_mentionbox_cell_n; ++i) { if (!s_mentionbox_cells[i]) continue; lv_obj_set_style_bg_color(s_mentionbox_cells[i], - lv_color_hex((int)i == s_mentionnav_idx ? COLOR_ACCENT : 0x1B2B3A), LV_PART_MAIN); + lv_color_hex((int)i == s_mentionnav_idx ? COLOR_ACCENT : COLOR_ACCENT_SURFACE), LV_PART_MAIN); } } static void mentionNavMove(int delta) { @@ -7001,7 +7138,7 @@ static bool mentionBoxMaybeShow(lv_obj_t* ta) { lv_obj_set_style_bg_color(s_mentionbox, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_mentionbox, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(s_mentionbox, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(s_mentionbox, lv_color_hex(0x2A3D52), LV_PART_MAIN); + lv_obj_set_style_border_color(s_mentionbox, lv_color_hex(COLOR_ACCENT_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_mentionbox, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(s_mentionbox, 4, LV_PART_MAIN); lv_obj_set_style_pad_row(s_mentionbox, 3, LV_PART_MAIN); @@ -7013,7 +7150,7 @@ static bool mentionBoxMaybeShow(lv_obj_t* ta) { lv_obj_add_flag(b, NAV_SKIP_FLAG); lv_obj_set_size(b, boxw, rowh); lv_obj_set_style_radius(b, 5, LV_PART_MAIN); - lv_obj_set_style_bg_color(b, lv_color_hex(0x1B2B3A), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_ACCENT_SURFACE), LV_PART_MAIN); lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_add_event_cb(b, mentionBoxCellCb, LV_EVENT_CLICKED, (void*)s_mention_names[i]); #if CAP_KEYPAD_NAV @@ -7196,7 +7333,7 @@ static void openChannelShareModal(const char* channel_name, const uint8_t secret lv_label_set_text(sec_lbl, s_channel_share_secret_hex); lv_obj_set_style_text_color(sec_lbl, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); lv_obj_set_style_text_font(sec_lbl, &g_font_14, LV_PART_MAIN); - lv_obj_set_style_bg_color(sec_lbl, lv_color_hex(0x0A0B0C), LV_PART_MAIN); + lv_obj_set_style_bg_color(sec_lbl, lv_color_hex(COLOR_FIELD), LV_PART_MAIN); lv_obj_set_style_bg_opa(sec_lbl, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_pad_all(sec_lbl, 6, LV_PART_MAIN); lv_obj_set_style_radius(sec_lbl, 4, LV_PART_MAIN); @@ -7212,7 +7349,8 @@ static void openChannelShareModal(const char* channel_name, const uint8_t secret lv_obj_set_pos(b, 2, y); styleButton(b); lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(b, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(b, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(b, channelShareCopyCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* bl = lv_label_create(b); useChainedFont(bl); @@ -7533,7 +7671,7 @@ static void openThreadActionSheet(int thread_idx, const char* name, bool is_chan lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, pad, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); // header stays fixed; the body below scrolls @@ -7600,11 +7738,14 @@ static void openThreadActionSheet(int thread_idx, const char* name, bool is_chan styleButton(b); lv_obj_set_style_pad_ver(b, 0, LV_PART_MAIN); lv_obj_set_style_pad_hor(b, 8, LV_PART_MAIN); - if (bg) lv_obj_set_style_bg_color(b, lv_color_hex(bg), LV_PART_MAIN); + if (bg) lv_obj_set_style_bg_color(b, + lv_color_hex(themeRole(bg, COLOR_STATUS_DANGER)), LV_PART_MAIN); lv_obj_add_event_cb(b, cb, LV_EVENT_CLICKED, nullptr); lv_obj_t* l = lv_label_create(b); lv_label_set_text(l, TR(lbl)); lv_obj_set_style_text_font(l, row_font, LV_PART_MAIN); + if (bg) lv_obj_set_style_text_color(l, + lv_color_hex(COLOR_ON_STATUS_DANGER), LV_PART_MAIN); lv_obj_center(l); y += btn_h + btn_gap; }; @@ -7941,7 +8082,7 @@ static void emojiPaintSelection() { lv_obj_t* b = lv_obj_get_child(s_emoji_grid, i); if (!b) continue; const bool sel = ((int)i == s_emoji_sel); - lv_obj_set_style_bg_color(b, lv_color_hex(sel ? COLOR_MENTION : 0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(sel ? COLOR_MENTION : COLOR_CONTROL), LV_PART_MAIN); lv_obj_set_style_border_width(b, sel ? 2 : 0, LV_PART_MAIN); lv_obj_set_style_border_color(b, lv_color_hex(0xCFE6FF), LV_PART_MAIN); } @@ -8021,7 +8162,7 @@ static void openEmojiPicker(lv_obj_t* ta, const char* const* items = k_emoji_ite lv_obj_set_size(card, cardw, cardh); lv_obj_align(card, LV_ALIGN_CENTER, 0, 0); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 8, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -8071,7 +8212,7 @@ static void openEmojiPicker(lv_obj_t* ta, const char* const* items = k_emoji_ite lv_obj_t* b = lv_btn_create(grid); lv_obj_set_size(b, cell_px, cell_px); styleButton(b); - lv_obj_set_style_bg_color(b, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_set_style_pad_all(b, 0, LV_PART_MAIN); lv_obj_add_event_cb(b, emojiPickCb, LV_EVENT_CLICKED, (void*)(intptr_t)i); #if CAP_LARGE_SCREEN && LV_USE_IMGFONT @@ -8242,7 +8383,7 @@ static void openQuickReplyPicker(LvChatPanel* p) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, pad, LV_PART_MAIN); // The card is clamped to the visible height above, but its rows are absolutely @@ -8273,7 +8414,7 @@ static void openQuickReplyPicker(LvChatPanel* p) { lv_obj_set_size(b, card_w - 2 * pad, btn_h); lv_obj_set_pos(b, 0, title_h); styleButton(b); - lv_obj_set_style_bg_color(b, lv_color_hex(fix ? 0x1A1B1C : 0x0C0D0E), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(fix ? COLOR_CONTROL : COLOR_CONTROL_DISABLED), LV_PART_MAIN); if (fix) lv_obj_add_event_cb(b, qrGpsPickCb, LV_EVENT_CLICKED, nullptr); else lv_obj_add_state(b, LV_STATE_DISABLED); // disabled = LVGL swallows the click lv_obj_t* lbl = lv_label_create(b); @@ -8299,7 +8440,7 @@ static void openQuickReplyPicker(LvChatPanel* p) { lv_obj_set_size(b, col_w, btn_h); lv_obj_set_pos(b, col * (col_w + col_gap), title_h + gps_row_h + row * (btn_h + row_gap)); styleButton(b); - lv_obj_set_style_bg_color(b, lv_color_hex(n > 0 ? 0x1A1B1C : 0x0C0D0E), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(n > 0 ? COLOR_CONTROL : COLOR_CONTROL_DISABLED), LV_PART_MAIN); lv_obj_add_event_cb(b, qrPickCb, LV_EVENT_CLICKED, (void*)(intptr_t)i); lv_obj_t* lbl = lv_label_create(b); lv_label_set_text(lbl, buf); @@ -9008,7 +9149,7 @@ static void txtMenuShow(lv_obj_t* ta) { lv_obj_set_style_bg_color(s_txtmenu, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_txtmenu, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(s_txtmenu, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(s_txtmenu, lv_color_hex(0x2A3D52), LV_PART_MAIN); + lv_obj_set_style_border_color(s_txtmenu, lv_color_hex(COLOR_ACCENT_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_txtmenu, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(s_txtmenu, pad, LV_PART_MAIN); lv_obj_set_style_pad_column(s_txtmenu, gap, LV_PART_MAIN); @@ -9019,7 +9160,7 @@ static void txtMenuShow(lv_obj_t* ta) { lv_obj_t* c = lv_btn_create(s_txtmenu); lv_obj_set_size(c, cw, ch); lv_obj_set_style_radius(c, 5, LV_PART_MAIN); - lv_obj_set_style_bg_color(c, lv_color_hex(0x1B2B3A), LV_PART_MAIN); + lv_obj_set_style_bg_color(c, lv_color_hex(COLOR_ACCENT_SURFACE), LV_PART_MAIN); lv_obj_set_style_bg_color(c, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_add_event_cb(c, txtMenuCellCb, LV_EVENT_CLICKED, reinterpret_cast((intptr_t)i)); lv_obj_t* l = lv_label_create(c); @@ -9176,11 +9317,11 @@ static lv_obj_t* createSettingsModal(const char* title, SettingsModalKind kind) lv_obj_remove_style_all(card); lv_obj_set_width(card, card_w); lv_obj_set_height(card, LV_SIZE_CONTENT); - lv_obj_set_style_bg_color(card, lv_color_hex(0x121417), LV_PART_MAIN); + lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_RAISED), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 10, LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x2A2E34), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(themeRole(0x2A2E34, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_pad_all(card, card_pad, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -9226,7 +9367,7 @@ static lv_obj_t* createSettingsModal(const char* title, SettingsModalKind kind) lv_obj_clear_flag(header, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_style_border_side(header, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); lv_obj_set_style_border_width(header, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(header, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(header, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_t* lbl = lv_label_create(header); lv_label_set_text(lbl, TR(title)); @@ -9932,7 +10073,7 @@ static void openDiscoveredSettingsSheetCb(lv_event_t* e) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 12, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -10081,12 +10222,13 @@ static void openDiscoveredModalCb(lv_event_t* e) { lv_obj_set_size(pb, SC(32), SC(32)); lv_obj_align(pb, LV_ALIGN_RIGHT_MID, -144, 0); // left of the sort styleButton(pb); - lv_obj_set_style_bg_color(pb, lv_color_hex(0x7A2A2A), LV_PART_MAIN); + lv_obj_set_style_bg_color(pb, lv_color_hex(themeRole(0x7A2A2A, COLOR_STATUS_DANGER)), LV_PART_MAIN); + lv_obj_set_style_text_color(pb, lv_color_hex(COLOR_ON_STATUS_DANGER), LV_PART_MAIN); lv_obj_add_event_cb(pb, discoveredPurgeCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* pl = lv_label_create(pb); useChainedFont(pl); lv_label_set_text(pl, LV_SYMBOL_TRASH); - lv_obj_set_style_text_color(pl, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(pl, lv_color_hex(COLOR_ON_STATUS_DANGER), LV_PART_MAIN); lv_obj_center(pl); } } @@ -10335,7 +10477,7 @@ static void buildProfileSettings() { lv_label_set_text(pk_lbl, pk_hex); lv_obj_set_style_text_color(pk_lbl, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); lv_obj_set_style_text_font(pk_lbl, &g_font_12, LV_PART_MAIN); - lv_obj_set_style_bg_color(pk_lbl, lv_color_hex(0x0A0B0C), LV_PART_MAIN); + lv_obj_set_style_bg_color(pk_lbl, lv_color_hex(COLOR_FIELD), LV_PART_MAIN); lv_obj_set_style_bg_opa(pk_lbl, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_pad_all(pk_lbl, 4, LV_PART_MAIN); lv_obj_set_style_radius(pk_lbl, 4, LV_PART_MAIN); @@ -10633,7 +10775,7 @@ static void buildRadioSettings() { lv_obj_remove_style_all(sep); lv_obj_set_size(sep, s_settings_content_w, 1); lv_obj_set_pos(sep, 0, y); - lv_obj_set_style_bg_color(sep, lv_color_hex(0x303438), LV_PART_MAIN); + lv_obj_set_style_bg_color(sep, lv_color_hex(themeRole(0x303438, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_bg_opa(sep, LV_OPA_COVER, LV_PART_MAIN); lv_obj_t* sl = lv_label_create(body); lv_label_set_text(sl, TR(title)); @@ -10669,7 +10811,7 @@ static void buildRadioSettings() { lv_obj_set_style_text_font(g_set_modal.radio_preset_dd, &g_font_12, LV_PART_MAIN); lv_obj_set_style_bg_color(g_set_modal.radio_preset_dd, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_text_color(g_set_modal.radio_preset_dd, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); - lv_obj_set_style_border_color(g_set_modal.radio_preset_dd, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(g_set_modal.radio_preset_dd, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); /* Dropdown list (the popup once tapped) */ lv_obj_t* preset_list = lv_dropdown_get_list(g_set_modal.radio_preset_dd); lv_obj_set_style_bg_color(preset_list, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); @@ -10791,7 +10933,7 @@ static void buildRadioSettings() { lv_obj_set_style_text_font(dd, &g_font_12, LV_PART_MAIN); lv_obj_set_style_bg_color(dd, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_text_color(dd, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); - lv_obj_set_style_border_color(dd, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(dd, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_t* loclist = lv_dropdown_get_list(dd); lv_obj_set_style_bg_color(loclist, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_text_color(loclist, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); @@ -10834,7 +10976,7 @@ static void buildRadioSettings() { lv_obj_set_style_text_font(dd, &g_font_12, LV_PART_MAIN); lv_obj_set_style_bg_color(dd, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_text_color(dd, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); - lv_obj_set_style_border_color(dd, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(dd, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_t* phlist = lv_dropdown_get_list(dd); lv_obj_set_style_bg_color(phlist, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_text_color(phlist, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); @@ -10930,7 +11072,7 @@ static void buildRadioSettings() { lv_obj_set_style_text_font(dd, &g_font_12, LV_PART_MAIN); lv_obj_set_style_bg_color(dd, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_text_color(dd, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); - lv_obj_set_style_border_color(dd, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(dd, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_t* antlist = lv_dropdown_get_list(dd); lv_obj_set_style_bg_color(antlist, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_text_color(antlist, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); @@ -11320,7 +11462,7 @@ static void openMemoryDetailCb(lv_event_t* e) { lv_obj_set_size(card, sw - 24, (sh - STATUSBAR_H) - 24); lv_obj_align(card, LV_ALIGN_CENTER, 0, 0); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_set_scroll_dir(card, LV_DIR_VER); @@ -12275,6 +12417,8 @@ static void openTimezonePicker() { lv_obj_set_size(row, sw - 16, SC(40)); styleButton(row); if (i == cur) lv_obj_set_style_bg_color(row, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); + lv_obj_set_style_text_color(row, + lv_color_hex(i == cur ? COLOR_ON_ACCENT : COLOR_TEXT), LV_PART_MAIN); lv_obj_add_event_cb(row, tzPickerSelectCb, LV_EVENT_CLICKED, (void*)(intptr_t)i); lv_obj_t* lbl = lv_label_create(row); lv_label_set_text(lbl, touchPrefsTimezoneLabel(i)); @@ -12357,7 +12501,7 @@ static void openLocalSensorsPage() { lv_obj_set_size(card, sw - 12, H - 44); lv_obj_set_pos(card, 6, 36); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 8, LV_PART_MAIN); lv_obj_set_scroll_dir(card, LV_DIR_VER); @@ -12395,7 +12539,7 @@ static void openLocalSensorsPage() { lv_obj_set_style_border_opa(chart, LV_OPA_30, LV_PART_MAIN); lv_obj_set_style_border_width(chart, 1, LV_PART_MAIN); lv_obj_set_style_radius(chart, 6, LV_PART_MAIN); - lv_obj_set_style_line_color(chart, lv_color_hex(0x2A2E30), LV_PART_TICKS); + lv_obj_set_style_line_color(chart, lv_color_hex(COLOR_CHART_TICK), LV_PART_TICKS); lv_obj_set_style_text_color(chart, lv_color_hex(COLOR_SUB), LV_PART_TICKS); lv_obj_set_style_text_font(chart, &g_font_12, LV_PART_TICKS); lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 4, 0, 3, 1, true, 40); @@ -12457,7 +12601,7 @@ static void openExpansionCard() { lv_obj_set_size(card, card_w, LV_SIZE_CONTENT); lv_obj_align(card, LV_ALIGN_CENTER, 0, 0); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -12607,6 +12751,22 @@ static void pagerKbBlCycleCb(lv_event_t* e) { } #endif +static void themeModeRestart(uint8_t mode) { +#if defined(ESP32) + if (touchPrefsGetThemeMode() == mode) return; + if (!touchPrefsSetThemeMode(mode)) { + if (g_lv.task) g_lv.task->showAlert(TR("Theme save failed"), 1600); + return; + } +#endif + if (g_lv.task) g_lv.task->rebootDevice(); +} + +static void themeModeSelectCb(lv_event_t* e) { + if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; + themeModeRestart((uint8_t)(uintptr_t)lv_event_get_user_data(e)); +} + static void buildDeviceSettings(int sec) { // One detail page per section: each block below is gated to its DSEC_* section // (skipped blocks don't advance y, so every page lays out from the top). @@ -12666,7 +12826,7 @@ static void buildDeviceSettings(int sec) { lv_obj_set_style_text_font(dd, &g_font_12, LV_PART_MAIN); lv_obj_set_style_bg_color(dd, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_text_color(dd, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); - lv_obj_set_style_border_color(dd, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(dd, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_t* gblist = lv_dropdown_get_list(dd); lv_obj_set_style_bg_color(gblist, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_text_color(gblist, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); @@ -13108,10 +13268,40 @@ static void buildDeviceSettings(int sec) { } #endif - /* Theme colour (UI accent): opens a colour-wheel + hex picker. The chosen - colour is clamped dark enough that off-white button text stays readable. */ + /* Firmware appearance: selecting a different palette saves and restarts so + every LVGL object is rebuilt with one coherent set of colours. */ { - y += settingsRowLabel(body, y, 0, TR("Theme colour"), COLOR_SUB, &g_font_12, 0) + 4; + y += settingsRowLabel(body, y, 0, TR("Appearance"), COLOR_SUB, &g_font_12, 0) + 4; + const uint8_t current = touchPrefsGetThemeMode(); + const lv_coord_t gap = 4; + const lv_coord_t row_w = s_settings_content_w - 2; + const lv_coord_t button_w = (row_w - gap) / 2; + const char* labels[2] = { TR("Night"), TR("Day") }; + for (uint8_t mode = TOUCH_THEME_NIGHT; mode <= TOUCH_THEME_DAY; ++mode) { + lv_obj_t* button = lv_btn_create(body); + lv_obj_set_size(button, button_w, SC(34)); + lv_obj_set_pos(button, 2 + mode * (button_w + gap), y); + styleButton(button); + if (mode == current) { + lv_obj_set_style_bg_opa(button, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_set_style_border_opa(button, LV_OPA_COVER, LV_PART_MAIN); + } + lv_obj_set_style_text_color(button, + lv_color_hex(mode == current ? COLOR_ON_ACCENT : COLOR_TEXT), LV_PART_MAIN); + lv_obj_add_event_cb(button, themeModeSelectCb, LV_EVENT_CLICKED, + (void*)(uintptr_t)mode); + lv_obj_t* label = lv_label_create(button); + char text[32]; + snprintf(text, sizeof(text), "%s%s", mode == current ? LV_SYMBOL_OK " " : "", labels[mode]); + lv_label_set_text(label, text); + lv_obj_center(label); + } + y += SC(42); + } + + /* Accent colour: opens a colour-wheel + hex picker. */ + { + y += settingsRowLabel(body, y, 0, TR("Accent colour"), COLOR_SUB, &g_font_12, 0) + 4; lv_obj_t* b = lv_btn_create(body); lv_obj_set_size(b, SC(150), SC(32)); lv_obj_set_pos(b, 2, y); @@ -13171,7 +13361,7 @@ static void buildDeviceSettings(int sec) { lv_obj_set_style_text_color(st, lv_color_hex(0xE3A127), LV_PART_MAIN); } else if (g_contacts_on_sd) { lv_label_set_text(st, TR("Contacts are saved to the SD card.")); - lv_obj_set_style_text_color(st, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(st, lv_color_hex(COLOR_STATUS_OK_TEXT), LV_PART_MAIN); } else if (want_sd) { lv_label_set_text(st, TR("Contacts are on internal flash - the SD card did not mount at boot. Re-seat the card and reboot.")); lv_obj_set_style_text_color(st, lv_color_hex(0xE3A127), LV_PART_MAIN); // amber warning @@ -13222,7 +13412,7 @@ static void buildDeviceSettings(int sec) { lv_obj_set_pos(sl, 2, y + SC(6)); lv_slider_set_range(sl, 1, 100); // dark is the mode's job ("Off"); on/auto always visibly lit lv_slider_set_value(sl, s_tdeck_kb_bl_pct, LV_ANIM_OFF); - lv_obj_set_style_bg_color(sl, lv_color_hex(0x202428), LV_PART_MAIN); + lv_obj_set_style_bg_color(sl, lv_color_hex(COLOR_TRACK), LV_PART_MAIN); lv_obj_set_style_bg_color(sl, lv_color_hex(COLOR_ACCENT), LV_PART_INDICATOR); lv_obj_set_style_bg_color(sl, lv_color_hex(COLOR_ACCENT), LV_PART_KNOB); lv_obj_set_style_pad_all(sl, 6, LV_PART_KNOB); @@ -13594,7 +13784,7 @@ static void buildDeviceSettings(int sec) { // Highlight the current colour with a white ring. const bool sel = (kLockColors[i] == curcol); lv_obj_set_style_border_width(sb, sel ? 2 : 1, LV_PART_MAIN); - lv_obj_set_style_border_color(sb, lv_color_hex(sel ? 0xFFFFFF : 0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(sb, lv_color_hex(sel ? 0xFFFFFF : COLOR_BORDER), LV_PART_MAIN); lv_obj_add_event_cb(sb, lockColorChosenCb, LV_EVENT_CLICKED, (void*)(uintptr_t)kLockColors[i]); } y += swz + 10; @@ -13726,9 +13916,9 @@ static void buildDeviceSettings(int sec) { lv_obj_set_size(b_reboot, lv_pct(100),SC(34)); lv_obj_set_pos(b_reboot, 2, y); styleButton(b_reboot); - lv_obj_set_style_bg_color(b_reboot, lv_color_hex(0xC44B55), LV_PART_MAIN); - lv_obj_set_style_bg_color(b_reboot, lv_color_hex(0xA13F47), LV_PART_MAIN | LV_STATE_PRESSED); - lv_obj_set_style_text_color(b_reboot, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_bg_color(b_reboot, lv_color_hex(themeRole(0xC44B55, COLOR_STATUS_DANGER)), LV_PART_MAIN); + lv_obj_set_style_bg_color(b_reboot, lv_color_hex(themeRole(0xA13F47, COLOR_STATUS_DANGER_PRESSED)), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(b_reboot, lv_color_hex(COLOR_ON_STATUS_DANGER), LV_PART_MAIN); lv_obj_add_event_cb(b_reboot, rebootCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* l_reboot = lv_label_create(b_reboot); useChainedFont(l_reboot); @@ -13783,9 +13973,9 @@ static void buildDeviceSettings(int sec) { lv_obj_set_size(b_cal, lv_pct(100), SC(34)); lv_obj_set_pos(b_cal, 2, y); styleButton(b_cal); - lv_obj_set_style_bg_color(b_cal, lv_color_hex(0x2F6B57), LV_PART_MAIN); - lv_obj_set_style_bg_color(b_cal, lv_color_hex(0x244F41), LV_PART_MAIN | LV_STATE_PRESSED); - lv_obj_set_style_text_color(b_cal, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_bg_color(b_cal, lv_color_hex(themeRole(0x2F6B57, COLOR_STATUS_OK)), LV_PART_MAIN); + lv_obj_set_style_bg_color(b_cal, lv_color_hex(themeRole(0x244F41, COLOR_STATUS_OK_PRESSED)), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(b_cal, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(b_cal, calibrateBatteryCb, LV_EVENT_SHORT_CLICKED, nullptr); lv_obj_add_event_cb(b_cal, calibrateBatteryCb, LV_EVENT_LONG_PRESSED, nullptr); lv_obj_t* l_cal = lv_label_create(b_cal); @@ -13804,7 +13994,7 @@ static void buildDeviceSettings(int sec) { lv_obj_t* sep = lv_obj_create(body); lv_obj_set_size(sep, lv_pct(100),SC(1)); lv_obj_set_pos(sep, 2, y); - lv_obj_set_style_bg_color(sep, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_bg_color(sep, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(sep, 0, LV_PART_MAIN); lv_obj_set_style_pad_all(sep, 0, LV_PART_MAIN); lv_obj_clear_flag(sep, LV_OBJ_FLAG_SCROLLABLE); @@ -14047,7 +14237,7 @@ static void showConfirm(const char* msg, const char* ok_label, SimpleCb on_confi lv_obj_align(card, LV_ALIGN_CENTER, 0, 0); styleSurface(card, COLOR_PANEL, 12); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_pad_all(card, PSC(12), LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); addCloseXBadge(card, confirmCancelEvt); // X behaves like Cancel @@ -14072,8 +14262,8 @@ static void showConfirm(const char* msg, const char* ok_label, SimpleCb on_confi lv_obj_set_size(b_cancel, PSC(80), PSC(34)); lv_obj_align(b_cancel, LV_ALIGN_BOTTOM_LEFT, 0, 0); styleButton(b_cancel); - lv_obj_set_style_bg_color(b_cancel, lv_color_hex(0x3A4A5C), LV_PART_MAIN); - lv_obj_set_style_bg_color(b_cancel, lv_color_hex(0x2D3947), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(b_cancel, lv_color_hex(COLOR_SECONDARY_ACTION), LV_PART_MAIN); + lv_obj_set_style_bg_color(b_cancel, lv_color_hex(themeRole(0x2D3947, COLOR_CONTROL_PRESSED)), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_text_color(b_cancel, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); lv_obj_add_event_cb(b_cancel, confirmCancelEvt, LV_EVENT_CLICKED, nullptr); lv_obj_t* lc = lv_label_create(b_cancel); @@ -14450,7 +14640,7 @@ static void wifiScanFillList() { lv_obj_t* r = lv_btn_create(s_wifi_scan_list); lv_obj_set_size(r, rw, SC(40)); // tall rows = easy to tap + scroll styleButton(r); - lv_obj_set_style_bg_color(r, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(r, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_add_event_cb(r, wifiScanSsidCb, LV_EVENT_CLICKED, (void*)(intptr_t)i); lv_obj_t* l = lv_label_create(r); lv_label_set_text(l, s_wifiscan_ssids[i]); @@ -14916,6 +15106,7 @@ static void openWifiJoinSheet(const char* ssid, bool manual) { lv_obj_t* join = lv_btn_create(body); lv_obj_set_size(join, iw, SC(38)); lv_obj_set_pos(join, 0, yy); styleButton(join); lv_obj_set_style_bg_color(join, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(join, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(join, wifiJoinConfirmCb, LV_EVENT_CLICKED, nullptr); { lv_obj_t* l = lv_label_create(join); lv_label_set_text(l, TR("Join")); lv_obj_center(l); } yy += SC(46); @@ -14977,7 +15168,7 @@ static void openWifiDetailsSheet(int net_idx) { lv_obj_t* st = lv_label_create(body); lv_label_set_text(st, is_active ? TR("Connected") : TR("Saved network")); lv_obj_set_style_text_font(st, &g_font_12, LV_PART_MAIN); - lv_obj_set_style_text_color(st, lv_color_hex(is_active ? COLOR_STATUS_OK : COLOR_SUB), LV_PART_MAIN); + lv_obj_set_style_text_color(st, lv_color_hex(is_active ? COLOR_STATUS_OK_TEXT : COLOR_SUB), LV_PART_MAIN); lv_obj_set_pos(st, 0, yy); yy += SC(24); @@ -14995,14 +15186,16 @@ static void openWifiDetailsSheet(int net_idx) { lv_obj_t* con = lv_btn_create(body); lv_obj_set_size(con, iw, SC(38)); lv_obj_set_pos(con, 0, yy); styleButton(con); lv_obj_set_style_bg_color(con, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(con, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(con, wifiDetailsConnectCb, LV_EVENT_CLICKED, nullptr); { lv_obj_t* l = lv_label_create(con); lv_label_set_text(l, TR("Connect")); lv_obj_center(l); } yy += SC(46); } lv_obj_t* forget = lv_btn_create(body); lv_obj_set_size(forget, iw, SC(38)); lv_obj_set_pos(forget, 0, yy); styleButton(forget); - lv_obj_set_style_bg_color(forget, lv_color_hex(0xC44B55), LV_PART_MAIN); - lv_obj_set_style_bg_color(forget, lv_color_hex(0xA13F47), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(forget, lv_color_hex(themeRole(0xC44B55, COLOR_STATUS_DANGER)), LV_PART_MAIN); + lv_obj_set_style_bg_color(forget, lv_color_hex(themeRole(0xA13F47, COLOR_STATUS_DANGER_PRESSED)), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(forget, lv_color_hex(COLOR_ON_STATUS_DANGER), LV_PART_MAIN); lv_obj_add_event_cb(forget, wifiDetailsForgetCb, LV_EVENT_CLICKED, nullptr); { lv_obj_t* l = lv_label_create(forget); lv_label_set_text(l, TR("Forget network")); lv_obj_center(l); } @@ -15053,7 +15246,7 @@ static void wifiListRow(const char* ssid, const char* right, lv_color_t txtcol, lv_obj_set_size(row, W, SC(30)); lv_obj_set_pos(row, 0, s_wifi_list_y); styleButton(row); - lv_obj_set_style_bg_color(row, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(row, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); if (cb) lv_obj_add_event_cb(row, cb, LV_EVENT_CLICKED, ud); lv_obj_t* l = lv_label_create(row); lv_label_set_text(l, ssid); @@ -15594,7 +15787,7 @@ static void openContactsSearchSheetCb(lv_event_t* e) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -15640,6 +15833,7 @@ static void openContactsSearchSheetCb(lv_event_t* e) { lv_obj_set_pos(apply_btn, card_w - 20 - PSC(110), PSC(70)); styleButton(apply_btn); lv_obj_set_style_bg_color(apply_btn, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(apply_btn, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(apply_btn, contactsSearchApplyCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* al = lv_label_create(apply_btn); useChainedFont(al); @@ -16084,7 +16278,7 @@ static void openAdminCmdPicker() { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 6, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -16119,7 +16313,7 @@ static void openAdminCmdPicker() { lv_obj_t* h = lv_list_add_text(list, e.label); lv_obj_set_style_text_color(h, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); lv_obj_set_style_text_font(h, &g_font_12, LV_PART_MAIN); - lv_obj_set_style_bg_color(h, lv_color_hex(0x0F1722), LV_PART_MAIN); + lv_obj_set_style_bg_color(h, lv_color_hex(themeRole(0x0F1722, COLOR_ACCENT_SURFACE)), LV_PART_MAIN); lv_obj_set_style_bg_opa(h, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_border_width(h, 0, LV_PART_MAIN); lv_obj_set_style_pad_ver(h, 6, LV_PART_MAIN); @@ -16132,9 +16326,9 @@ static void openAdminCmdPicker() { // lv_list_add_btn uses a light theme bg by default — flip to the // panel-dark palette so the text is actually legible. lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); - lv_obj_set_style_bg_color(btn, lv_color_hex(0x141516), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(btn, lv_color_hex(0x141516), LV_PART_MAIN); + lv_obj_set_style_border_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN); lv_obj_set_style_border_side(btn, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); lv_obj_set_style_border_width(btn, 1, LV_PART_MAIN); lv_obj_set_style_min_height(btn, 30, LV_PART_MAIN); @@ -16188,7 +16382,7 @@ static void openAdminConsole(const ContactInfo& c) { styleSurface(hdr, COLOR_PANEL, 0); lv_obj_set_style_border_side(hdr, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); lv_obj_set_style_border_width(hdr, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(hdr, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(hdr, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_clear_flag(hdr, LV_OBJ_FLAG_SCROLLABLE); lv_obj_t* title = lv_label_create(hdr); @@ -16220,7 +16414,7 @@ static void openAdminConsole(const ContactInfo& c) { lv_obj_set_size(s_admin_log_box, sw - 8, admin_h - 36 - 8 - 44); lv_obj_set_pos(s_admin_log_box, 4, 40); styleSurface(s_admin_log_box, 0x0A0B0C, 6); - lv_obj_set_style_border_color(s_admin_log_box, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(s_admin_log_box, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_admin_log_box, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(s_admin_log_box, 6, LV_PART_MAIN); lv_obj_set_scroll_dir(s_admin_log_box, LV_DIR_VER); @@ -16245,7 +16439,7 @@ static void openAdminConsole(const ContactInfo& c) { styleSurface(row, COLOR_PANEL, 0); lv_obj_set_style_border_side(row, LV_BORDER_SIDE_TOP, LV_PART_MAIN); lv_obj_set_style_border_width(row, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(row, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(row, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE); // "?" button on the left opens the command picker. The textarea @@ -16254,7 +16448,7 @@ static void openAdminConsole(const ContactInfo& c) { lv_obj_set_size(picker_btn, 32, 32); lv_obj_align(picker_btn, LV_ALIGN_LEFT_MID, 4, 0); styleButton(picker_btn); - lv_obj_set_style_bg_color(picker_btn, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(picker_btn, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_set_style_pad_all(picker_btn, 0, LV_PART_MAIN); lv_obj_add_event_cb(picker_btn, [](lv_event_t* e) { if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; @@ -16283,6 +16477,7 @@ static void openAdminConsole(const ContactInfo& c) { lv_obj_align(send_btn, LV_ALIGN_RIGHT_MID, -4, 0); styleButton(send_btn); lv_obj_set_style_bg_color(send_btn, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(send_btn, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(send_btn, [](lv_event_t* e) { const lv_event_code_t code = lv_event_get_code(e); if (code != LV_EVENT_CLICKED && code != LV_EVENT_READY) return; @@ -16410,7 +16605,7 @@ static void openAdminLoginPrompt(const ContactInfo& c) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -16519,6 +16714,7 @@ static void openAdminLoginPrompt(const ContactInfo& c) { lv_obj_set_pos(login_btn, card_w - 20 - PSC(100), PSC(116) + shift); styleButton(login_btn); lv_obj_set_style_bg_color(login_btn, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(login_btn, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(login_btn, adminPwSubmitCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* ll = lv_label_create(login_btn); useChainedFont(ll); @@ -16961,7 +17157,7 @@ static void losDrawPlot() { lv_obj_remove_style_all(graph); lv_obj_set_size(graph, gw, gh); lv_obj_set_pos(graph, gx, gy); - lv_obj_set_style_bg_color(graph, lv_color_hex(0x0B0D0F), LV_PART_MAIN); + lv_obj_set_style_bg_color(graph, lv_color_hex(COLOR_CHART_BG), LV_PART_MAIN); lv_obj_set_style_bg_opa(graph, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(graph, 4, LV_PART_MAIN); lv_obj_clear_flag(graph, LV_OBJ_FLAG_SCROLLABLE); @@ -17220,7 +17416,7 @@ static void openLosModal(uint32_t mesh_idx) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 8, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -17394,7 +17590,7 @@ static void openContactActionSheet(uint32_t mesh_idx, bool is_repeater, const ch lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, padding, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); // title + X stay fixed; the body below scrolls @@ -17476,11 +17672,14 @@ static void openContactActionSheet(uint32_t mesh_idx, bool is_repeater, const ch styleButton(b); lv_obj_set_style_pad_ver(b, 0, LV_PART_MAIN); lv_obj_set_style_pad_hor(b, 8, LV_PART_MAIN); - if (bg) lv_obj_set_style_bg_color(b, lv_color_hex(bg), LV_PART_MAIN); + if (bg) lv_obj_set_style_bg_color(b, + lv_color_hex(themeRole(bg, COLOR_STATUS_DANGER)), LV_PART_MAIN); lv_obj_add_event_cb(b, cb, LV_EVENT_CLICKED, nullptr); lv_obj_t* l = lv_label_create(b); lv_label_set_text(l, TR(label)); lv_obj_set_style_text_font(l, row_font, LV_PART_MAIN); + if (bg) lv_obj_set_style_text_color(l, + lv_color_hex(COLOR_ON_STATUS_DANGER), LV_PART_MAIN); lv_obj_center(l); y += btn_h + btn_gap; }; @@ -17805,7 +18004,8 @@ static void openAddContactModalCb(lv_event_t* e) { lv_obj_set_pos(b, 2, y); styleButton(b); lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(b, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(b, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(b, addContactSubmitCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* bl = lv_label_create(b); useChainedFont(bl); @@ -17870,7 +18070,8 @@ static void openCreatePrivateChannelModal() { lv_obj_set_pos(b, 2, y); styleButton(b); lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(b, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(b, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(b, createPrivateChannelSubmitCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* bl = lv_label_create(b); useChainedFont(bl); @@ -17973,7 +18174,8 @@ static void openJoinPrivateChannelModal() { lv_obj_set_pos(b, 2, y); styleButton(b); lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(b, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(b, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(b, joinPrivateChannelSubmitCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* bl = lv_label_create(b); useChainedFont(bl); @@ -18065,7 +18267,8 @@ static void openJoinHashtagChannelModal() { lv_obj_set_pos(b, 2, y); styleButton(b); lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(b, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(b, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(b, joinHashtagChannelSubmitCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* bl = lv_label_create(b); useChainedFont(bl); @@ -18146,7 +18349,7 @@ static void openAddChannelSheet() { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, pad, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -18306,7 +18509,7 @@ static void openShareMyContactPopup() { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -18730,7 +18933,7 @@ static void openBatteryChartWindow() { lv_obj_set_style_min_height(card, 120, LV_PART_MAIN); lv_obj_align(card, LV_ALIGN_TOP_MID, 0, 8); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_set_scroll_dir(card, LV_DIR_VER); @@ -18800,10 +19003,10 @@ static void openBatteryChartWindow() { lv_chart_set_div_line_count(chart, 4, 6); // helper grid: 4 horizontal, 6 vertical lv_obj_set_style_bg_color(chart, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(chart, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(chart, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(chart, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(chart, 1, LV_PART_MAIN); lv_obj_set_style_radius(chart, 6, LV_PART_MAIN); - lv_obj_set_style_line_color(chart, lv_color_hex(0x1A1D1F), LV_PART_MAIN); // faint grid lines + lv_obj_set_style_line_color(chart, lv_color_hex(COLOR_CHART_GRID), LV_PART_MAIN); // faint grid lines lv_obj_set_style_size(chart, 4, LV_PART_INDICATOR); // visible point dots // Axis ticks + labels (RF-monitor style): primary Y reformatted to volts by the // draw callback, secondary Y shows raw MHz. Reserve a label gutter on each side. @@ -18811,7 +19014,7 @@ static void openBatteryChartWindow() { lv_obj_set_style_pad_bottom(chart, 6, LV_PART_MAIN); lv_obj_set_style_text_font(chart, &g_font_12, LV_PART_TICKS); lv_obj_set_style_text_color(chart, lv_color_hex(COLOR_SUB), LV_PART_TICKS); - lv_obj_set_style_line_color(chart, lv_color_hex(0x2A2E30), LV_PART_TICKS); + lv_obj_set_style_line_color(chart, lv_color_hex(COLOR_CHART_TICK), LV_PART_TICKS); lv_obj_set_style_pad_left(chart, 4, LV_PART_TICKS); lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 4, 0, 4, 1, true, 40); lv_obj_add_event_cb(chart, battChartTickCb, LV_EVENT_DRAW_PART_BEGIN, nullptr); @@ -18892,9 +19095,10 @@ static void openBatteryChartWindow() { lv_obj_t* clr = lv_btn_create(card); lv_obj_set_size(clr, 30, 26); lv_obj_align(clr, LV_ALIGN_TOP_RIGHT, 0, row_y); - lv_obj_set_style_bg_color(clr, lv_color_hex(0xB23A48), LV_PART_MAIN); + lv_obj_set_style_bg_color(clr, lv_color_hex(themeRole(0xB23A48, COLOR_STATUS_DANGER)), LV_PART_MAIN); lv_obj_add_event_cb(clr, batteryClearCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* clrl = lv_label_create(clr); lv_label_set_text(clrl, LV_SYMBOL_TRASH); lv_obj_center(clrl); + if (s_theme_day) lv_obj_set_style_text_color(clrl, lv_color_hex(COLOR_ON_STATUS_DANGER), LV_PART_MAIN); useChainedFont(clrl); // Show/hide the CPU-MHz series (same size/style as the trash button, to its left). @@ -18902,12 +19106,15 @@ static void openBatteryChartWindow() { lv_obj_set_size(cpub, 30, 26); lv_obj_align(cpub, LV_ALIGN_TOP_RIGHT, -36, row_y); lv_obj_set_style_pad_all(cpub, 0, LV_PART_MAIN); - lv_obj_set_style_bg_color(cpub, lv_color_hex(s_batt_show_cpu ? 0x4A5256 : 0x3A3D40), LV_PART_MAIN); + lv_obj_set_style_bg_color(cpub, + lv_color_hex(s_batt_show_cpu ? themeRole(0x4A5256, COLOR_CONTROL) + : themeRole(0x3A3D40, COLOR_CONTROL_DISABLED)), LV_PART_MAIN); lv_obj_add_event_cb(cpub, batteryCpuToggleCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* cpul = lv_label_create(cpub); lv_label_set_text(cpul, "CPU"); lv_obj_set_style_text_font(cpul, &g_font_12, LV_PART_MAIN); - lv_obj_set_style_text_color(cpul, lv_color_hex(s_batt_show_cpu ? 0xFFFFFF : COLOR_SUB), LV_PART_MAIN); + lv_obj_set_style_text_color(cpul, + lv_color_hex(s_batt_show_cpu ? themeRole(0xFFFFFF, COLOR_TEXT) : COLOR_SUB), LV_PART_MAIN); lv_obj_center(cpul); // ---- Idle power-save stats (snapshot mirror of the Lock-settings diag; the @@ -20034,7 +20241,7 @@ static void openTermCmdPicker() { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 6, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -20063,7 +20270,7 @@ static void openTermCmdPicker() { lv_obj_t* h = lv_list_add_text(list, e.label); lv_obj_set_style_text_color(h, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); lv_obj_set_style_text_font(h, &g_font_12, LV_PART_MAIN); - lv_obj_set_style_bg_color(h, lv_color_hex(0x0F1722), LV_PART_MAIN); + lv_obj_set_style_bg_color(h, lv_color_hex(themeRole(0x0F1722, COLOR_ACCENT_SURFACE)), LV_PART_MAIN); lv_obj_set_style_bg_opa(h, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_border_width(h, 0, LV_PART_MAIN); lv_obj_set_style_pad_ver(h, 6, LV_PART_MAIN); @@ -20074,9 +20281,9 @@ static void openTermCmdPicker() { lv_obj_set_style_text_font(btn, &g_font_12, LV_PART_MAIN); lv_obj_set_style_text_color(btn, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); - lv_obj_set_style_bg_color(btn, lv_color_hex(0x141516), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(btn, lv_color_hex(0x141516), LV_PART_MAIN); + lv_obj_set_style_border_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN); lv_obj_set_style_border_side(btn, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); lv_obj_set_style_border_width(btn, 1, LV_PART_MAIN); lv_obj_set_style_min_height(btn, 30, LV_PART_MAIN); @@ -20111,7 +20318,7 @@ static void buildTerminal(lv_obj_t* body) { lv_obj_set_size(s_term_log_box, bw - 8, bh - row_h - 4); lv_obj_set_pos(s_term_log_box, 4, 2); styleSurface(s_term_log_box, 0x0A0B0C, 6); - lv_obj_set_style_border_color(s_term_log_box, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(s_term_log_box, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_term_log_box, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(s_term_log_box, 6, LV_PART_MAIN); lv_obj_set_scroll_dir(s_term_log_box, LV_DIR_VER); @@ -20134,7 +20341,7 @@ static void buildTerminal(lv_obj_t* body) { lv_obj_set_size(picker_btn, 32, 32); lv_obj_align(picker_btn, LV_ALIGN_LEFT_MID, 4, 0); styleButton(picker_btn); - lv_obj_set_style_bg_color(picker_btn, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(picker_btn, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_set_style_pad_all(picker_btn, 0, LV_PART_MAIN); lv_obj_add_event_cb(picker_btn, [](lv_event_t* e) { if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; @@ -20161,6 +20368,7 @@ static void buildTerminal(lv_obj_t* body) { lv_obj_align(send_btn, LV_ALIGN_RIGHT_MID, -4, 0); styleButton(send_btn); lv_obj_set_style_bg_color(send_btn, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(send_btn, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(send_btn, [](lv_event_t* e) { if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; terminalSubmit(); @@ -20200,9 +20408,9 @@ static void fmStyleRow(lv_obj_t* btn, uint32_t text_color) { lv_obj_set_style_text_font(btn, &g_font_14, LV_PART_MAIN); lv_obj_set_style_text_color(btn, lv_color_hex(text_color), LV_PART_MAIN); lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); - lv_obj_set_style_bg_color(btn, lv_color_hex(0x141516), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(btn, lv_color_hex(0x141516), LV_PART_MAIN); + lv_obj_set_style_border_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN); lv_obj_set_style_border_side(btn, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); lv_obj_set_style_border_width(btn, 1, LV_PART_MAIN); lv_obj_set_style_min_height(btn, 34, LV_PART_MAIN); @@ -20523,7 +20731,7 @@ static void fmShowBusyOverlay(const char* msg) { lv_obj_remove_style_all(s_fm_fmt_overlay); lv_obj_set_size(s_fm_fmt_overlay, sw, sh - STATUSBAR_H); lv_obj_set_pos(s_fm_fmt_overlay, 0, STATUSBAR_H); - lv_obj_set_style_bg_color(s_fm_fmt_overlay, lv_color_hex(0x0E1216), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_fm_fmt_overlay, lv_color_hex(themeRole(0x0E1216, COLOR_PANEL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_fm_fmt_overlay, LV_OPA_COVER, LV_PART_MAIN); lv_obj_clear_flag(s_fm_fmt_overlay, LV_OBJ_FLAG_SCROLLABLE); lv_obj_t* l = lv_label_create(s_fm_fmt_overlay); @@ -20531,7 +20739,7 @@ static void fmShowBusyOverlay(const char* msg) { lv_obj_set_width(l, sw - 36); lv_label_set_text(l, msg); lv_obj_set_style_text_align(l, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN); - lv_obj_set_style_text_color(l, lv_color_hex(0xFFCC66), LV_PART_MAIN); + lv_obj_set_style_text_color(l, lv_color_hex(themeRole(0xFFCC66, COLOR_STATUS_WARN_TEXT)), LV_PART_MAIN); lv_obj_set_style_text_font(l, &g_font_14, LV_PART_MAIN); lv_obj_center(l); } @@ -20775,7 +20983,7 @@ static void fmTextPrompt(const char* title, const char* initial, void (*cb)(cons lv_obj_align(card, LV_ALIGN_TOP_MID, 0, 8); styleSurface(card, COLOR_PANEL, 10); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x2A2E33), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(themeRole(0x2A2E33, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -20800,7 +21008,7 @@ static void fmTextPrompt(const char* title, const char* initial, void (*cb)(cons lv_obj_set_size(bc, 80, 32); lv_obj_align(bc, LV_ALIGN_BOTTOM_LEFT, 0, 0); styleButton(bc); - lv_obj_set_style_bg_color(bc, lv_color_hex(0x3A4A5C), LV_PART_MAIN); + lv_obj_set_style_bg_color(bc, lv_color_hex(COLOR_SECONDARY_ACTION), LV_PART_MAIN); lv_obj_add_event_cb(bc, fmPromptCancelCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* lc = lv_label_create(bc); lv_label_set_text(lc, TR("Cancel")); lv_obj_center(lc); useChainedFont(lc); @@ -20810,6 +21018,7 @@ static void fmTextPrompt(const char* title, const char* initial, void (*cb)(cons lv_obj_align(bo, LV_ALIGN_BOTTOM_RIGHT, 0, 0); styleButton(bo); lv_obj_set_style_bg_color(bo, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(bo, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(bo, fmPromptOkCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* lo = lv_label_create(bo); lv_label_set_text(lo, "OK"); lv_obj_center(lo); useChainedFont(lo); @@ -20948,11 +21157,19 @@ static lv_obj_t* fmActionBtn(lv_obj_t* parent, const char* text, lv_event_cb_t c lv_obj_set_width(b, lv_pct(100)); lv_obj_set_height(b, 34); styleButton(b); - lv_obj_set_style_bg_color(b, lv_color_hex(bg), LV_PART_MAIN); + uint32_t fill = bg; + uint32_t fg = COLOR_TEXT; + if (s_theme_day) { + if (bg == 0x5A2D2D) { fill = COLOR_STATUS_DANGER; fg = COLOR_ON_STATUS_DANGER; } + else if (bg == 0x2D4A2D) { fill = COLOR_STATUS_OK; fg = COLOR_ON_STATUS_OK; } + else fill = COLOR_CONTROL; + } + lv_obj_set_style_bg_color(b, lv_color_hex(fill), LV_PART_MAIN); lv_obj_add_event_cb(b, cb, LV_EVENT_CLICKED, nullptr); lv_obj_t* l = lv_label_create(b); useChainedFont(l); lv_label_set_text(l, TR(text)); + lv_obj_set_style_text_color(l, lv_color_hex(fg), LV_PART_MAIN); lv_obj_center(l); return b; } @@ -21044,7 +21261,7 @@ static void fmOpenActions(const char* name, bool isdir) { lv_obj_align(card, LV_ALIGN_CENTER, 0, 0); styleSurface(card, COLOR_PANEL, 10); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x2A2E33), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(themeRole(0x2A2E33, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_set_style_max_height(card, (sh - STATUSBAR_H) - 16, LV_PART_MAIN); lv_obj_set_flex_flow(card, LV_FLEX_FLOW_COLUMN); @@ -21142,6 +21359,7 @@ static void fmOpenEditor(const char* name) { lv_obj_align(save, LV_ALIGN_TOP_RIGHT, -50, 4); styleButton(save); lv_obj_set_style_bg_color(save, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(save, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(save, fmEditorSaveCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* sl = lv_label_create(save); lv_label_set_text(sl, TR("Save")); lv_obj_set_style_text_font(sl, &g_font_12, LV_PART_MAIN); lv_obj_center(sl); @@ -21150,7 +21368,7 @@ static void fmOpenEditor(const char* name) { lv_obj_set_size(cancel, 44, 28); lv_obj_align(cancel, LV_ALIGN_TOP_RIGHT, -3, 4); styleButton(cancel); - lv_obj_set_style_bg_color(cancel, lv_color_hex(0x3A4A5C), LV_PART_MAIN); + lv_obj_set_style_bg_color(cancel, lv_color_hex(COLOR_SECONDARY_ACTION), LV_PART_MAIN); lv_obj_add_event_cb(cancel, fmEditorCancelCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* cl = lv_label_create(cancel); lv_label_set_text(cl, LV_SYMBOL_CLOSE); tanCloseRed(cl); lv_obj_set_style_text_font(cl, &g_font_12, LV_PART_MAIN); lv_obj_center(cl); @@ -21541,7 +21759,7 @@ static void fmOpenImage(const char* name) { lv_obj_set_size(close, 30, 26); lv_obj_align(close, LV_ALIGN_TOP_RIGHT, -3, 3); styleButton(close); - lv_obj_set_style_bg_color(close, lv_color_hex(0x3A4A5C), LV_PART_MAIN); + lv_obj_set_style_bg_color(close, lv_color_hex(COLOR_SECONDARY_ACTION), LV_PART_MAIN); lv_obj_add_event_cb(close, fmImageCloseCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* cl = lv_label_create(close); lv_label_set_text(cl, LV_SYMBOL_CLOSE); tanCloseRed(cl); lv_obj_set_style_text_font(cl, &g_font_12, LV_PART_MAIN); lv_obj_center(cl); @@ -21551,7 +21769,7 @@ static void fmOpenImage(const char* name) { lv_obj_set_size(full, 52, 26); lv_obj_align(full, LV_ALIGN_TOP_RIGHT, -3 - 30 - 4, 3); styleButton(full); - lv_obj_set_style_bg_color(full, lv_color_hex(0x3A4A5C), LV_PART_MAIN); + lv_obj_set_style_bg_color(full, lv_color_hex(COLOR_SECONDARY_ACTION), LV_PART_MAIN); lv_obj_add_event_cb(full, fmImageFullCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* fll = lv_label_create(full); lv_label_set_text(fll, TR("Full")); lv_obj_set_style_text_font(fll, &g_font_12, LV_PART_MAIN); lv_obj_center(fll); @@ -22003,7 +22221,7 @@ static void buildFileManager(lv_obj_t* body) { lv_obj_set_size(back, 30, BTN_H); lv_obj_set_pos(back, 3, 3); styleButton(back); - lv_obj_set_style_bg_color(back, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(back, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_set_style_pad_all(back, 0, LV_PART_MAIN); lv_obj_add_event_cb(back, fmBackCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* backl = lv_label_create(back); @@ -22016,7 +22234,7 @@ static void buildFileManager(lv_obj_t* body) { lv_obj_set_size(add, 30, BTN_H); lv_obj_set_pos(add, 36, 3); styleButton(add); - lv_obj_set_style_bg_color(add, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(add, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_set_style_pad_all(add, 0, LV_PART_MAIN); lv_obj_add_event_cb(add, fmFolderMenuCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* addl = lv_label_create(add); @@ -22030,7 +22248,7 @@ static void buildFileManager(lv_obj_t* body) { lv_obj_set_size(find, BTN_W, BTN_H); lv_obj_set_pos(find, find_x, 3); styleButton(find); - lv_obj_set_style_bg_color(find, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(find, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_set_style_pad_all(find, 0, LV_PART_MAIN); lv_obj_add_event_cb(find, fmSearchBtnCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* findl = lv_label_create(find); @@ -22044,7 +22262,7 @@ static void buildFileManager(lv_obj_t* body) { lv_obj_set_size(sort, BTN_W, BTN_H); lv_obj_set_pos(sort, sort_x, 3); styleButton(sort); - lv_obj_set_style_bg_color(sort, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(sort, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_set_style_pad_all(sort, 0, LV_PART_MAIN); lv_obj_add_event_cb(sort, fmSortBtnCb, LV_EVENT_CLICKED, nullptr); s_fm_sort_lbl = lv_label_create(sort); @@ -22061,9 +22279,9 @@ static void buildFileManager(lv_obj_t* body) { lv_obj_set_width(s_fm_path_lbl, loc_w); lv_obj_set_style_text_font(s_fm_path_lbl, &g_font_12, LV_PART_MAIN); lv_obj_set_style_text_color(s_fm_path_lbl, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); - lv_obj_set_style_bg_color(s_fm_path_lbl, lv_color_hex(0x101418), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_fm_path_lbl, lv_color_hex(themeRole(0x101418, COLOR_FIELD)), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_fm_path_lbl, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(s_fm_path_lbl, lv_color_hex(0x2A2E33), LV_PART_MAIN); + lv_obj_set_style_border_color(s_fm_path_lbl, lv_color_hex(themeRole(0x2A2E33, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_border_width(s_fm_path_lbl, 1, LV_PART_MAIN); lv_obj_set_style_radius(s_fm_path_lbl, 5, LV_PART_MAIN); lv_obj_set_style_pad_hor(s_fm_path_lbl, 6, LV_PART_MAIN); @@ -22170,7 +22388,7 @@ static void openSignalInfoPopup() { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_set_scroll_dir(card, LV_DIR_VER); @@ -22224,7 +22442,7 @@ static void openSignalInfoPopup() { lv_obj_set_style_line_color(s_siginfo_chart, lv_color_hex(0x25292C), LV_PART_MAIN); lv_obj_set_style_line_width(s_siginfo_chart, 2, LV_PART_ITEMS); lv_obj_set_style_size(s_siginfo_chart, 0, LV_PART_INDICATOR); - s_siginfo_chart_tx = lv_chart_add_series(s_siginfo_chart, lv_color_hex(COLOR_STATUS_OK), + s_siginfo_chart_tx = lv_chart_add_series(s_siginfo_chart, lv_color_hex(COLOR_STATUS_OK_TEXT), LV_CHART_AXIS_PRIMARY_Y); s_siginfo_chart_rx = lv_chart_add_series(s_siginfo_chart, lv_color_hex(0x4F94CD), LV_CHART_AXIS_PRIMARY_Y); @@ -22244,7 +22462,7 @@ static void openSignalInfoPopup() { lv_obj_t* graph_legend = lv_label_create(s_siginfo_chart); char graph_legend_text[40]; snprintf(graph_legend_text, sizeof graph_legend_text, "#%06X TX# #4F94CD RX#", - (unsigned)COLOR_STATUS_OK); + (unsigned)COLOR_STATUS_OK_TEXT); lv_label_set_recolor(graph_legend, true); lv_label_set_text(graph_legend, graph_legend_text); lv_obj_set_style_text_font(graph_legend, &lv_font_montserrat_12, LV_PART_MAIN); @@ -22864,7 +23082,7 @@ static void openReaderPage(const char* initial_url = nullptr) { // --- Body: a vertical scroll container we fill with paragraph + link labels --- s_reader_scroll = lv_obj_create(s_reader_root); lv_obj_remove_style_all(s_reader_scroll); - lv_obj_set_style_bg_color(s_reader_scroll, lv_color_hex(0x14161A), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_reader_scroll, lv_color_hex(themeRole(0x14161A, COLOR_RAISED)), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_reader_scroll, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(s_reader_scroll, 6, LV_PART_MAIN); lv_obj_set_style_pad_all(s_reader_scroll, 8, LV_PART_MAIN); @@ -22874,9 +23092,9 @@ static void openReaderPage(const char* initial_url = nullptr) { lv_obj_remove_style_all(s_reader_navbar); lv_obj_set_size(s_reader_navbar, sw, kReaderNavH); lv_obj_set_pos(s_reader_navbar, 0, H - kReaderNavH); - lv_obj_set_style_bg_color(s_reader_navbar, lv_color_hex(0x000000), LV_PART_MAIN); // black bar + lv_obj_set_style_bg_color(s_reader_navbar, lv_color_hex(themeRole(0x000000, COLOR_PANEL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_reader_navbar, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(s_reader_navbar, lv_color_hex(0x222222), LV_PART_MAIN); + lv_obj_set_style_border_color(s_reader_navbar, lv_color_hex(themeRole(0x222222, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_border_width(s_reader_navbar, 1, LV_PART_MAIN); lv_obj_set_style_border_side(s_reader_navbar, LV_BORDER_SIDE_TOP, LV_PART_MAIN); lv_obj_set_style_pad_all(s_reader_navbar, 0, LV_PART_MAIN); @@ -22889,13 +23107,13 @@ static void openReaderPage(const char* initial_url = nullptr) { lv_obj_set_size(b, 46, kReaderNavH - 2); lv_obj_set_style_bg_opa(b, LV_OPA_TRANSP, LV_PART_MAIN); lv_obj_set_style_radius(b, 3, LV_PART_MAIN); - lv_obj_set_style_bg_color(b, lv_color_hex(0x1E1E1E), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(themeRole(0x1E1E1E, COLOR_CONTROL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(b, LV_OPA_COVER, LV_STATE_PRESSED); lv_obj_set_ext_click_area(b, 8); // thin bar, so pad the tap target lv_obj_add_event_cb(b, cb, LV_EVENT_CLICKED, nullptr); lv_obj_t* l = lv_label_create(b); lv_label_set_text(l, sym); lv_obj_center(l); lv_obj_set_style_text_font(l, &g_font_12, LV_PART_MAIN); - lv_obj_set_style_text_color(l, lv_color_hex(0x9FD8FF), LV_PART_MAIN); + lv_obj_set_style_text_color(l, lv_color_hex(themeRole(0x9FD8FF, COLOR_ACCENT)), LV_PART_MAIN); return b; }; s_reader_back = navBtn(LV_SYMBOL_LEFT, readerBackCb); @@ -24265,10 +24483,10 @@ static void openSpectrumPage() { lv_obj_clear_flag(s_spec_chart, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_style_bg_color(s_spec_chart, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_spec_chart, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(s_spec_chart, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(s_spec_chart, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_spec_chart, 1, LV_PART_MAIN); lv_obj_set_style_radius(s_spec_chart, 6, LV_PART_MAIN); - lv_obj_set_style_line_color(s_spec_chart, lv_color_hex(0x1A1D1F), LV_PART_MAIN); + lv_obj_set_style_line_color(s_spec_chart, lv_color_hex(COLOR_CHART_GRID), LV_PART_MAIN); lv_obj_set_style_size(s_spec_chart, 0, LV_PART_INDICATOR); // hide point dots lv_obj_set_style_line_width(s_spec_chart, 1, LV_PART_ITEMS); // thin: 160 points lv_chart_set_type(s_spec_chart, LV_CHART_TYPE_LINE); @@ -24281,7 +24499,7 @@ static void openSpectrumPage() { lv_obj_set_style_pad_left(s_spec_chart, 4, LV_PART_TICKS); lv_obj_set_style_text_font(s_spec_chart, &g_font_12, LV_PART_TICKS); lv_obj_set_style_text_color(s_spec_chart, lv_color_hex(COLOR_SUB), LV_PART_TICKS); - lv_obj_set_style_line_color(s_spec_chart, lv_color_hex(0x2A2E30), LV_PART_TICKS); + lv_obj_set_style_line_color(s_spec_chart, lv_color_hex(COLOR_CHART_TICK), LV_PART_TICKS); lv_chart_set_axis_tick(s_spec_chart, LV_CHART_AXIS_PRIMARY_Y, 4, 0, 3, 1, true, 40); s_spec_ser = lv_chart_add_series(s_spec_chart, lv_color_hex(0x35C9C9), LV_CHART_AXIS_PRIMARY_Y); lv_chart_set_all_value(s_spec_chart, s_spec_ser, SPEC_DBM_MIN); @@ -24667,7 +24885,7 @@ static void makeHome(lv_obj_t* tab) { lv_obj_set_style_border_width(s_home_chart, 1, LV_PART_MAIN); lv_obj_set_style_radius(s_home_chart, 6, LV_PART_MAIN); lv_obj_set_style_size(s_home_chart, 0, LV_PART_INDICATOR); - s_home_chart_tx = lv_chart_add_series(s_home_chart, lv_color_hex(COLOR_STATUS_OK), + s_home_chart_tx = lv_chart_add_series(s_home_chart, lv_color_hex(COLOR_STATUS_OK_TEXT), LV_CHART_AXIS_PRIMARY_Y); s_home_chart_rx = lv_chart_add_series(s_home_chart, lv_color_hex(0x4F94CD), LV_CHART_AXIS_PRIMARY_Y); @@ -24793,8 +25011,8 @@ static void makeHome(lv_obj_t* tab) { #endif styleButton(adv); lv_obj_set_style_bg_color(adv, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(adv, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); - lv_obj_set_style_text_color(adv, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_bg_color(adv, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(adv, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); if (home_land) { #if CAP_LARGE_SCREEN lv_obj_set_size(adv, BTNW, tan_btn_h); // slot 0 of the spread right column @@ -24841,7 +25059,9 @@ static void makeHome(lv_obj_t* tab) { styleButton(apb); lv_obj_set_size(apb, half, btn_h); lv_obj_align(apb, LV_ALIGN_TOP_LEFT, apps_x, apps_y); - const uint32_t inv_accent = 0xFFFFFFu ^ (COLOR_ACCENT & 0xFFFFFFu); + const uint32_t inv_accent = s_theme_day + ? COLOR_ACCENT_SURFACE + : 0xFFFFFFu ^ (COLOR_ACCENT & 0xFFFFFFu); lv_obj_set_style_bg_color(apb, lv_color_hex(inv_accent), LV_PART_MAIN); lv_obj_set_style_bg_opa(apb, LV_OPA_COVER, LV_PART_MAIN); lv_obj_add_event_cb(apb, homeAppsBtnCb, LV_EVENT_CLICKED, nullptr); @@ -24849,7 +25069,8 @@ static void makeHome(lv_obj_t* tab) { lv_obj_t* apl = lv_label_create(apb); lv_label_set_text(apl, TR(LV_SYMBOL_LIST " Apps")); lv_obj_set_style_text_font(apl, &g_font_14, LV_PART_MAIN); - lv_obj_set_style_text_color(apl, lv_color_hex(0xFFFFFF), LV_PART_MAIN); + lv_obj_set_style_text_color(apl, + lv_color_hex(themeRole(0xFFFFFF, COLOR_TEXT)), LV_PART_MAIN); lv_obj_center(apl); #if defined(HAS_TDISPLAY_P4) && CAP_LARGE_SCREEN // Rows 1–2 of the portrait-large launcher grid: Terminal | Files, then Control full-width. @@ -24897,12 +25118,15 @@ static void makeHome(lv_obj_t* tab) { lv_obj_t* l = lv_label_create(b); lv_label_set_text(l, TR(label)); lv_obj_set_style_text_font(l, bh >= 44 ? &g_font_14 : &g_font_12, LV_PART_MAIN); - lv_obj_set_style_text_color(l, lv_color_hex(bg ? 0xFFFFFF : COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(l, + lv_color_hex(bg ? themeRole(0xFFFFFF, COLOR_TEXT) : COLOR_TEXT), LV_PART_MAIN); lv_obj_center(l); return b; }; // The accent's inverse — used for the "Apps" pop colour and the Control-panel button. - const uint32_t inv_accent = 0xFFFFFFu ^ (COLOR_ACCENT & 0xFFFFFFu); + const uint32_t inv_accent = s_theme_day + ? COLOR_ACCENT_SURFACE + : 0xFFFFFFu ^ (COLOR_ACCENT & 0xFFFFFFu); #if CAP_LARGE_SCREEN make_launcher(TR(">_ Terminal"), tanBtnY(1), homeTerminalCb, 0, tan_btn_h); #if defined(HAS_TDECK_GT911) @@ -24988,6 +25212,8 @@ static lv_obj_t* s_contacts_seg_btns[4] = { nullptr, nullptr, nullptr, nul static lv_obj_t* s_contacts_overflow_root = nullptr; static void styleContactsSegment(lv_obj_t* b, bool active) { + lv_obj_set_style_text_color(b, + lv_color_hex(active ? COLOR_ON_ACCENT : COLOR_TEXT), LV_PART_MAIN); if (active) { // Filled, brighter — clearly the current view. lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); @@ -25096,7 +25322,7 @@ static void openContactsOverflowSheetCb(lv_event_t* e) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, padding, LV_PART_MAIN); lv_obj_add_flag(card, LV_OBJ_FLAG_SCROLLABLE); // safety net: if capped below content (e.g. Huge scale), the lower rows stay reachable @@ -25322,7 +25548,7 @@ static void ctDeleteProgressOpen(){ s_ctd_bar = lv_bar_create(card); lv_obj_set_size(s_ctd_bar, sw - 60 - 28, 10); lv_obj_align(s_ctd_bar, LV_ALIGN_TOP_LEFT, 0, 26); - lv_obj_set_style_bg_color(s_ctd_bar, lv_color_hex(0x2A2D31), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_ctd_bar, lv_color_hex(themeRole(0x2A2D31, COLOR_TRACK)), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_ctd_bar, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_bg_color(s_ctd_bar, lv_color_hex(COLOR_ACCENT), LV_PART_INDICATOR); lv_obj_set_style_bg_opa(s_ctd_bar, LV_OPA_COVER, LV_PART_INDICATOR); @@ -25399,7 +25625,8 @@ static void ctSortSheetGestureCb(lv_event_t* e){ } static void ctPaintOpt(lv_obj_t* b, bool active){ if(!b) return; - lv_obj_set_style_bg_color(b, lv_color_hex(active ? COLOR_ACCENT : 0x1A1D20), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, + lv_color_hex(active ? COLOR_ACCENT : themeRole(0x1A1D20, COLOR_CONTROL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(b, active ? LV_OPA_40 : LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_border_width(b, active ? 1 : 0, LV_PART_MAIN); } @@ -25485,7 +25712,7 @@ static lv_obj_t* ctOpenOptionSheet(const char* title){ lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 10, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 12, LV_PART_MAIN); lv_obj_set_style_pad_row(card, 6, LV_PART_MAIN); @@ -25740,12 +25967,13 @@ static void makeContactsTab(lv_obj_t* tab) { s_ct_del_btn = lv_btn_create(s_ct_select_bar); lv_obj_set_size(s_ct_del_btn, 90, kChipH); lv_obj_set_style_radius(s_ct_del_btn, 6, LV_PART_MAIN); - lv_obj_set_style_bg_color(s_ct_del_btn, lv_color_hex(0xC0392B), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_ct_del_btn, lv_color_hex(themeRole(0xC0392B, COLOR_STATUS_DANGER)), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_ct_del_btn, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_pad_all(s_ct_del_btn, 0, LV_PART_MAIN); lv_obj_add_event_cb(s_ct_del_btn, ctDeleteSelCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* l = lv_label_create(s_ct_del_btn); lv_label_set_text(l, TR("Delete")); - lv_obj_set_style_text_color(l, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_text_color(l, + lv_color_hex(themeRole(0xFFFFFF, COLOR_ON_STATUS_DANGER)), LV_PART_MAIN); lv_obj_set_style_text_font(l, &g_font_12, LV_PART_MAIN); lv_obj_center(l); } } @@ -29407,7 +29635,7 @@ static void showRouteHud() { lv_obj_t* rl = lv_label_create(rb); useChainedFont(rl); lv_label_set_text(rl, LV_SYMBOL_REFRESH); - lv_obj_set_style_text_color(rl, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(rl, lv_color_hex(COLOR_ON_ACCENT), LV_PART_MAIN); lv_obj_center(rl); } @@ -29702,7 +29930,7 @@ static void mapOptInfoCb(lv_event_t* e) { lv_obj_set_size(card, sw - 24, (sh - STATUSBAR_H) - 24); lv_obj_align(card, LV_ALIGN_CENTER, 0, 0); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_set_scroll_dir(card, LV_DIR_VER); @@ -29775,7 +30003,7 @@ static void openMapOptions() { lv_obj_set_style_max_height(card, sh - STATUSBAR_H - 16, LV_PART_MAIN); // ...scroll if past the screen lv_obj_align(card, LV_ALIGN_TOP_MID, 0, 8); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 12, LV_PART_MAIN); lv_obj_set_scroll_dir(card, LV_DIR_VER); @@ -29853,7 +30081,7 @@ static void openMapOptions() { lv_obj_remove_style_all(sep); lv_obj_set_size(sep, cardw - 24, 1); lv_obj_set_pos(sep, 0, y + 4); - lv_obj_set_style_bg_color(sep, lv_color_hex(0x303438), LV_PART_MAIN); + lv_obj_set_style_bg_color(sep, lv_color_hex(themeRole(0x303438, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_bg_opa(sep, LV_OPA_COVER, LV_PART_MAIN); lv_obj_t* sl = lv_label_create(card); lv_label_set_text(sl, TR("Show on map")); @@ -29936,7 +30164,7 @@ static void openMapOptions() { lv_obj_t* xb = addCloseXBadge(card, mapOptionsDismissCb); lv_obj_set_style_bg_color(xb, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(xb, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(xb, lv_color_hex(0x303438), LV_PART_MAIN); + lv_obj_set_style_border_color(xb, lv_color_hex(themeRole(0x303438, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_border_width(xb, 1, LV_PART_MAIN); } @@ -30028,7 +30256,7 @@ static void openMapPicker(const int* idxs, int n) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, pad, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -30225,7 +30453,7 @@ static void mapContactsFillList() { lv_obj_t* b = lv_btn_create(s_map_contacts_list); lv_obj_set_size(b, rw, 46); styleButton(b); - lv_obj_set_style_bg_color(b, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_set_style_pad_all(b, 0, LV_PART_MAIN); lv_obj_add_event_cb(b, mapContactsRowCb, LV_EVENT_CLICKED, reinterpret_cast((intptr_t)e.midx)); @@ -30299,7 +30527,7 @@ static void openMapContactsList() { lv_obj_set_size(card, card_w, card_h); lv_obj_align(card, LV_ALIGN_CENTER, 0, 0); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, pad, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -30316,7 +30544,7 @@ static void openMapContactsList() { lv_obj_set_size(sort_b, 108, 26); lv_obj_align(sort_b, LV_ALIGN_TOP_RIGHT, -28, 0); styleButton(sort_b); - lv_obj_set_style_bg_color(sort_b, lv_color_hex(0x1A1B1C), LV_PART_MAIN); + lv_obj_set_style_bg_color(sort_b, lv_color_hex(COLOR_CONTROL), LV_PART_MAIN); lv_obj_add_event_cb(sort_b, mapContactsSortCb, LV_EVENT_CLICKED, nullptr); s_map_contacts_sort_lbl = lv_label_create(sort_b); lv_label_set_text_fmt(s_map_contacts_sort_lbl, LV_SYMBOL_SHUFFLE " %s", @@ -30872,7 +31100,7 @@ static void makeMapTab(lv_obj_t* tab) { lv_obj_remove_style_all(s_map_canvas); lv_obj_set_size(s_map_canvas, k_map_canvas_w, k_map_canvas_h); lv_obj_set_pos(s_map_canvas, 0, 0); - lv_obj_set_style_bg_color(s_map_canvas, lv_color_hex(0x0A0B0C), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_map_canvas, lv_color_hex(COLOR_FIELD), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_map_canvas, LV_OPA_COVER, LV_PART_MAIN); lv_obj_clear_flag(s_map_canvas, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(s_map_canvas, LV_OBJ_FLAG_CLICKABLE); @@ -31000,7 +31228,7 @@ static void makeMapTab(lv_obj_t* tab) { lv_obj_align(s_map_zoom_slider, LV_ALIGN_BOTTOM_MID, 0, -(TABBAR_H + 8)); lv_slider_set_range(s_map_zoom_slider, k_map_zoom_min, k_map_zoom_max); lv_slider_set_value(s_map_zoom_slider, s_map_zoom, LV_ANIM_OFF); - lv_obj_set_style_bg_color(s_map_zoom_slider, lv_color_hex(0x202428), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_map_zoom_slider, lv_color_hex(COLOR_TRACK), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_map_zoom_slider, LV_OPA_80, LV_PART_MAIN); lv_obj_set_style_bg_color(s_map_zoom_slider, lv_color_hex(COLOR_ACCENT), LV_PART_INDICATOR); lv_obj_set_style_bg_color(s_map_zoom_slider, lv_color_hex(COLOR_ACCENT), LV_PART_KNOB); @@ -31371,7 +31599,7 @@ static void makeChatDetail(LvChatPanel& p) { lv_obj_set_size(p.jump_oldest_btn, 40, 40); lv_obj_set_pos(p.jump_oldest_btn, chatScreenW() - 42, CHAT_HDR_H + STATUSBAR_H + 2); styleChipAsFkey(p.jump_oldest_btn, jolbl, 4, 0xC724B1, 40, true); - lv_obj_set_style_bg_color(p.jump_oldest_btn, lv_color_hex(0x101113), LV_PART_MAIN); + lv_obj_set_style_bg_color(p.jump_oldest_btn, lv_color_hex(themeRole(0x101113, COLOR_CONTROL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(p.jump_oldest_btn, LV_OPA_70, LV_PART_MAIN); lv_obj_set_style_radius(p.jump_oldest_btn, 8, LV_PART_MAIN); #endif @@ -31399,7 +31627,7 @@ static void makeChatDetail(LvChatPanel& p) { lv_obj_set_size(p.jump_btn, 40, 40); lv_obj_set_pos(p.jump_btn, chatScreenW() - 42, chatCompYOpen() - 50); styleChipAsFkey(p.jump_btn, jlbl, 4, 0xC724B1, 40, true); // ◇ — same shape as the menubar's purple key - lv_obj_set_style_bg_color(p.jump_btn, lv_color_hex(0x101113), LV_PART_MAIN); + lv_obj_set_style_bg_color(p.jump_btn, lv_color_hex(themeRole(0x101113, COLOR_CONTROL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(p.jump_btn, LV_OPA_70, LV_PART_MAIN); lv_obj_set_style_radius(p.jump_btn, 8, LV_PART_MAIN); #endif @@ -31442,7 +31670,7 @@ static void makeChatDetail(LvChatPanel& p) { styleButton(qr_btn); lv_obj_set_style_radius(qr_btn, chip_sz / 2, LV_PART_MAIN); // Glass chip: dark + translucent so the chat bubbles show through faintly. - lv_obj_set_style_bg_color(qr_btn, lv_color_hex(0x000000), LV_PART_MAIN); + lv_obj_set_style_bg_color(qr_btn, lv_color_hex(themeRole(0x000000, COLOR_PANEL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(qr_btn, LV_OPA_50, LV_PART_MAIN); lv_obj_add_event_cb(qr_btn, openQuickReplyPickerCb, LV_EVENT_CLICKED, &p); lv_obj_t* ql = lv_label_create(qr_btn); @@ -31452,7 +31680,7 @@ static void makeChatDetail(LvChatPanel& p) { #if defined(HAS_TANMATSU) styleChipAsFkey(qr_btn, ql, 0, 0xF5A623, chip_sz, true); // orange △ — quick replies (F2) // styleChipAsFkey clears the bg to draw the shape — restore the glass backing behind it. - lv_obj_set_style_bg_color(qr_btn, lv_color_hex(0x000000), LV_PART_MAIN); + lv_obj_set_style_bg_color(qr_btn, lv_color_hex(themeRole(0x000000, COLOR_PANEL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(qr_btn, LV_OPA_50, LV_PART_MAIN); lv_obj_set_style_radius(qr_btn, chip_sz / 2, LV_PART_MAIN); // ~40% smaller list glyph (fixed size, not the UI-scaled g_font_14) so it sits neatly in @@ -31467,7 +31695,7 @@ static void makeChatDetail(LvChatPanel& p) { styleButton(emoji_btn); lv_obj_set_style_radius(emoji_btn, chip_sz / 2, LV_PART_MAIN); // Glass chip: dark + translucent so the chat bubbles show through faintly. - lv_obj_set_style_bg_color(emoji_btn, lv_color_hex(0x000000), LV_PART_MAIN); + lv_obj_set_style_bg_color(emoji_btn, lv_color_hex(themeRole(0x000000, COLOR_PANEL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(emoji_btn, LV_OPA_50, LV_PART_MAIN); lv_obj_add_event_cb(emoji_btn, openEmojiPickerCb, LV_EVENT_CLICKED, &p); lv_obj_t* el = lv_label_create(emoji_btn); @@ -31477,7 +31705,7 @@ static void makeChatDetail(LvChatPanel& p) { #if defined(HAS_TANMATSU) styleChipAsFkey(emoji_btn, el, 1, 0xFFD400, chip_sz, false); // yellow □ — emoji picker (F3); keep the colour glyph // styleChipAsFkey clears the bg to draw the shape — restore the glass backing behind it. - lv_obj_set_style_bg_color(emoji_btn, lv_color_hex(0x000000), LV_PART_MAIN); + lv_obj_set_style_bg_color(emoji_btn, lv_color_hex(themeRole(0x000000, COLOR_PANEL)), LV_PART_MAIN); lv_obj_set_style_bg_opa(emoji_btn, LV_OPA_50, LV_PART_MAIN); lv_obj_set_style_radius(emoji_btn, chip_sz / 2, LV_PART_MAIN); #if LV_USE_IMGFONT @@ -31676,6 +31904,8 @@ static void buildLanguageSettings() { lv_obj_set_pos(b, 2, y); styleButton(b); if (l == cur) lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); + lv_obj_set_style_text_color(b, + lv_color_hex(l == cur ? COLOR_ON_ACCENT : COLOR_TEXT), LV_PART_MAIN); lv_obj_add_event_cb(b, langChosenCb, LV_EVENT_CLICKED, (void*)(intptr_t)l); lv_obj_t* lb = lv_label_create(b); lv_label_set_text(lb, kUiLangNames[l]); // native name (renders via the font fallback chain) @@ -31915,12 +32145,13 @@ static void settingsCatBuild(int cat) { lv_obj_set_size(s_ota_btn, lblw, 38); styleButton(s_ota_btn); lv_obj_set_style_bg_color(s_ota_btn, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(s_ota_btn, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(s_ota_btn, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(s_ota_btn, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(s_ota_btn, otaInstallLatestCb, LV_EVENT_CLICKED, nullptr); s_ota_btn_lbl = lv_label_create(s_ota_btn); lv_label_set_text(s_ota_btn_lbl, TR(LV_SYMBOL_DOWNLOAD " Install update")); lv_obj_set_style_text_font(s_ota_btn_lbl, &g_font_14, LV_PART_MAIN); - lv_obj_set_style_text_color(s_ota_btn_lbl, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(s_ota_btn_lbl, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_center(s_ota_btn_lbl); otaButtonRefreshState(); // grey immediately if we already know we're current @@ -32249,12 +32480,12 @@ static void makeSettings(lv_obj_t* tab) { s_settings_cat_card[c] = card; // remembered so hiding can apply live lv_obj_remove_style_all(card); lv_obj_set_size(card, card_w, card_h); - lv_obj_set_style_bg_color(card, lv_color_hex(0x121417), LV_PART_MAIN); + lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_RAISED), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_radius(card, 10, LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x2A2E34), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(themeRole(0x2A2E34, COLOR_BORDER)), LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); lv_obj_add_event_cb(card, settingsCatOpenCb, LV_EVENT_CLICKED, (void*)(intptr_t)c); @@ -32465,7 +32696,7 @@ static void openTraceResultPopup(const char* title, const char* body) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_set_scroll_dir(card, LV_DIR_VER); @@ -32743,7 +32974,7 @@ static void openMessageActionMenu(int msg_idx) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, pad, LV_PART_MAIN); if (scroll_menu) { @@ -32879,7 +33110,7 @@ static void openMessageInfoPopup(int msg_idx) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -33708,7 +33939,8 @@ static void chatBuildBubbleMeta(const UITask::UIMessage& m, bool channel_mode, } snprintf(out, out_len, "%s%s%s", ts_buf, deliv_glyph, rep_buf); - if (out_fg) *out_fg = deliv_glyph[0] ? deliv_fg : COLOR_SUB; + if (out_fg) *out_fg = s_theme_day ? COLOR_CHAT_META + : (deliv_glyph[0] ? deliv_fg : COLOR_SUB); } static lv_coord_t chatTextWidth(const char* s) { @@ -33776,11 +34008,13 @@ static void chatBuildCompactLine(const UITask::UIMessage& m, LvChatPanel* p, int if (!line || line_cap == 0) return; line[0] = '\0'; const bool colorful_bubbles = touchPrefsGetColorfulBubbles(); - lv_color_t bg_ignored = lv_color_hex(COLOR_RECV_BG); + lv_color_t dark_sender_col = lv_color_hex(COLOR_RECV_BG); lv_color_t sender_col = lv_color_hex(COLOR_ACCENT); const char* color_name = m.outgoing ? the_mesh.getNodePrefs()->node_name : d.show_sender; - if (colorful_bubbles && color_name && color_name[0]) - usernameBubbleColors(color_name, &bg_ignored, &sender_col); + if (colorful_bubbles && color_name && color_name[0]) { + usernameBubbleColors(color_name, &dark_sender_col, &sender_col); + if (s_theme_day) sender_col = dark_sender_col; + } const char* row_name = m.outgoing ? the_mesh.getNodePrefs()->node_name : (d.san_sender[0] && !(d.san_sender[0] == 'r' && d.san_sender[1] == 'x' && d.san_sender[2] == '\0')) @@ -33800,7 +34034,8 @@ static void chatBuildCompactLine(const UITask::UIMessage& m, LvChatPanel* p, int switch (m.deliv_state) { case UITask::DELIV_SENT: dglyph = LV_SYMBOL_UPLOAD; dfg = COLOR_SUB; break; case UITask::DELIV_DELIVERED: dglyph = LV_SYMBOL_OK LV_SYMBOL_OK; dfg = COLOR_ACCENT; break; - case UITask::DELIV_FAILED: dglyph = LV_SYMBOL_CLOSE " tap to resend"; dfg = 0xE08080; break; + case UITask::DELIV_FAILED: dglyph = LV_SYMBOL_CLOSE " tap to resend"; + dfg = s_theme_day ? COLOR_STATUS_DANGER_TEXT : 0xE08080; break; } } char reps[12] = ""; @@ -34299,7 +34534,8 @@ static bool chatRecolorUrls(const char* in, char* out, int cap) { int o = 0, i = 0; while (in[i] && o < cap - 12) { if (i == a) { - o += snprintf(out + o, cap - o, "#4EA1FF "); + o += snprintf(out + o, cap - o, "#%06X ", + (unsigned)(COLOR_CHAT_LINK & 0xFFFFFFu)); while (i < b && o < cap - 2) out[o++] = in[i++]; if (o < cap - 1) out[o++] = '#'; if (!chatUrlSpan(in, i, &a, &b)) a = -1; @@ -34338,7 +34574,7 @@ static void openUrlQrPopup(const char* url) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -34428,7 +34664,7 @@ static void openUrlMenu(const char* url) { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 8, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, pad, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -34488,11 +34724,12 @@ static lv_coord_t chatVirtCreateBubble(LvChatPanel* p, int logical_i, int ring_i const bool mentions_me = (p->channel_mode || s_chat_virt.thread_is_room) && !m.outgoing && textMentionsMe(d.show_text); const char* color_name = m.outgoing ? the_mesh.getNodePrefs()->node_name : d.show_sender; - lv_color_t bubble_bg = lv_color_hex(m.outgoing ? COLOR_SENT_BG : COLOR_RECV_BG); + lv_color_t bubble_bg = lv_color_hex(m.outgoing ? COLOR_CHAT_SENT_BG : COLOR_CHAT_RECV_BG); lv_color_t sender_col = lv_color_hex(COLOR_ACCENT); if (colorful_bubbles && color_name && color_name[0]) usernameBubbleColors(color_name, &bubble_bg, &sender_col); - if (mentions_me) bubble_bg = lv_color_hex(COLOR_MENTION_BG); + if (mentions_me) bubble_bg = lv_color_hex(COLOR_CHAT_MENTION_BG); + if (s_theme_day) sender_col = lv_color_hex(COLOR_CHAT_TEXT); lv_obj_set_style_bg_color(bubble, bubble_bg, LV_PART_MAIN); lv_obj_set_style_bg_opa(bubble, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_pad_hor(bubble, kChatBubblePadH, LV_PART_MAIN); @@ -34565,7 +34802,7 @@ static lv_coord_t chatVirtCreateBubble(LvChatPanel* p, int logical_i, int ring_i lv_obj_t* tlbl = lv_label_create(bubble); lv_obj_set_style_text_font(tlbl, msg_font, LV_PART_MAIN); - lv_obj_set_style_text_color(tlbl, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(tlbl, lv_color_hex(COLOR_CHAT_TEXT), LV_PART_MAIN); lv_label_set_text(tlbl, d.san_text); // Clickable URLs: tint any link blue (recolor tags are zero-width, so wrapping/height // below still measure from the plain d.san_text and stay correct). @@ -35243,9 +35480,9 @@ static void refreshChatList(LvChatPanel& p) { // 1 px bottom hairline, square corners, 16 px type icon + 14 px name. lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_bg_color(btn, lv_color_hex(0x141516), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_border_width(btn, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(btn, lv_color_hex(0x141516), LV_PART_MAIN); + lv_obj_set_style_border_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN); lv_obj_set_style_border_side(btn, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); lv_obj_set_style_radius(btn, 0, LV_PART_MAIN); lv_obj_set_style_text_color(btn, @@ -35323,7 +35560,7 @@ static void refreshChatList(LvChatPanel& p) { else snprintf(cnt, sizeof cnt, "%u", (unsigned)unread); lv_label_set_text(badge, cnt); lv_obj_set_style_text_font(badge, &g_font_12, LV_PART_MAIN); - lv_obj_set_style_text_color(badge, lv_color_hex(0x0A0B0C), LV_PART_MAIN); + lv_obj_set_style_text_color(badge, lv_color_hex(COLOR_FIELD), LV_PART_MAIN); lv_obj_set_style_bg_color(badge, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); lv_obj_set_style_bg_opa(badge, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(badge, 9, LV_PART_MAIN); @@ -35349,9 +35586,9 @@ static void refreshChatList(LvChatPanel& p) { btn = lv_list_add_btn(p.list_cont, nullptr, nullptr); lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_bg_color(btn, lv_color_hex(0x141516), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_border_width(btn, 1, LV_PART_MAIN); - lv_obj_set_style_border_color(btn, lv_color_hex(0x141516), LV_PART_MAIN); + lv_obj_set_style_border_color(btn, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN); lv_obj_set_style_border_side(btn, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); lv_obj_set_style_radius(btn, 0, LV_PART_MAIN); lv_obj_set_style_pad_all(btn, 0, LV_PART_MAIN); @@ -35528,7 +35765,7 @@ static void refreshChatList(LvChatPanel& p) { else snprintf(cnt, sizeof cnt, "%u", (unsigned)unread); lv_label_set_text(badge, cnt); lv_obj_set_style_text_font(badge, rowMetaFont, LV_PART_MAIN); - lv_obj_set_style_text_color(badge, lv_color_hex(0x0A0B0C), LV_PART_MAIN); + lv_obj_set_style_text_color(badge, lv_color_hex(COLOR_FIELD), LV_PART_MAIN); lv_obj_set_style_bg_color(badge, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); lv_obj_set_style_bg_opa(badge, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(badge, 9, LV_PART_MAIN); @@ -35946,8 +36183,8 @@ static void refreshContactsList() { lv_obj_set_size(rb, row_w, ROW_H); lv_obj_set_style_bg_color(rb, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(rb, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_bg_color(rb, lv_color_hex(0x141516), LV_PART_MAIN | LV_STATE_PRESSED); - lv_obj_set_style_border_color(rb, lv_color_hex(0x141516), LV_PART_MAIN); + lv_obj_set_style_bg_color(rb, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_border_color(rb, lv_color_hex(COLOR_CONTROL_PRESSED), LV_PART_MAIN); lv_obj_set_style_border_width(rb, 1, LV_PART_MAIN); lv_obj_set_style_border_side(rb, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN); lv_obj_set_style_radius(rb, 0, LV_PART_MAIN); @@ -36533,7 +36770,7 @@ static void pagerLockingPopupShow() { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 12, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -36546,7 +36783,7 @@ static void pagerLockingPopupShow() { s_pager_locking_bar = lv_bar_create(card); lv_obj_set_size(s_pager_locking_bar, 140, 8); lv_obj_align(s_pager_locking_bar, LV_ALIGN_BOTTOM_MID, 0, -14); - lv_obj_set_style_bg_color(s_pager_locking_bar, lv_color_hex(0x2A2D31), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_pager_locking_bar, lv_color_hex(themeRole(0x2A2D31, COLOR_TRACK)), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_pager_locking_bar, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_bg_color(s_pager_locking_bar, lv_color_hex(COLOR_ACCENT), LV_PART_INDICATOR); lv_obj_set_style_bg_opa(s_pager_locking_bar, LV_OPA_COVER, LV_PART_INDICATOR); @@ -37188,7 +37425,7 @@ static void lockscreenUnlockProgress(unsigned long remaining_ms) { lv_obj_set_style_bg_color(s_unlock_popup, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_unlock_popup, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(s_unlock_popup, 12, LV_PART_MAIN); - lv_obj_set_style_border_color(s_unlock_popup, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(s_unlock_popup, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_unlock_popup, 1, LV_PART_MAIN); lv_obj_clear_flag(s_unlock_popup, LV_OBJ_FLAG_SCROLLABLE); @@ -37199,7 +37436,7 @@ static void lockscreenUnlockProgress(unsigned long remaining_ms) { lv_obj_align(t, LV_ALIGN_TOP_MID, 0, 8); s_unlock_count = lv_label_create(s_unlock_popup); - lv_obj_set_style_text_color(s_unlock_count, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(s_unlock_count, lv_color_hex(COLOR_STATUS_OK_TEXT), LV_PART_MAIN); lv_obj_set_style_text_font(s_unlock_count, &g_font_16, LV_PART_MAIN); lv_obj_align(s_unlock_count, LV_ALIGN_CENTER, 0, 6); @@ -38104,7 +38341,8 @@ static void buildBackupsSettings() { lv_obj_set_pos(eb, 0, y); styleButton(eb); lv_obj_set_style_bg_color(eb, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(eb, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(eb, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(eb, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(eb, +[](lv_event_t* e) { if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; char fn[48]; backupMakeFilename(fn, sizeof fn); @@ -38115,7 +38353,7 @@ static void buildBackupsSettings() { char eblbl[56]; snprintf(eblbl, sizeof eblbl, LV_SYMBOL_SAVE " %s", TR("Export new backup")); lv_label_set_text(el, eblbl); lv_obj_set_style_text_font(el, &g_font_14, LV_PART_MAIN); - lv_obj_set_style_text_color(el, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(el, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_center(el); y += SC(48); } @@ -38170,13 +38408,14 @@ static void buildBackupsSettings() { lv_obj_set_size(del, SC(40), SC(32)); lv_obj_align(del, LV_ALIGN_RIGHT_MID, 0, 0); styleButton(del); - lv_obj_set_style_bg_color(del, lv_color_hex(0x7A2A2A), LV_PART_MAIN); - lv_obj_set_style_bg_color(del, lv_color_hex(0x5E2020), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(del, lv_color_hex(themeRole(0x7A2A2A, COLOR_STATUS_DANGER)), LV_PART_MAIN); + lv_obj_set_style_bg_color(del, lv_color_hex(themeRole(0x5E2020, COLOR_STATUS_DANGER_PRESSED)), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_add_event_cb(del, backupDeleteCb, LV_EVENT_CLICKED, s_backup_paths[i]); lv_obj_t* dl = lv_label_create(del); lv_label_set_text(dl, LV_SYMBOL_TRASH); lv_obj_set_style_text_font(dl, &g_font_14, LV_PART_MAIN); - lv_obj_set_style_text_color(dl, lv_color_hex(0xFFD8D8), LV_PART_MAIN); + lv_obj_set_style_text_color(dl, + lv_color_hex(themeRole(0xFFD8D8, COLOR_ON_STATUS_DANGER)), LV_PART_MAIN); lv_obj_center(dl); y += SC(42); @@ -38188,14 +38427,15 @@ static void buildBackupsSettings() { lv_obj_set_size(fr, cw, SC(40)); lv_obj_set_pos(fr, 0, y); styleButton(fr); - lv_obj_set_style_bg_color(fr, lv_color_hex(0x8B1E1E), LV_PART_MAIN); - lv_obj_set_style_bg_color(fr, lv_color_hex(0x6A1616), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(fr, lv_color_hex(themeRole(0x8B1E1E, COLOR_STATUS_DANGER)), LV_PART_MAIN); + lv_obj_set_style_bg_color(fr, lv_color_hex(themeRole(0x6A1616, COLOR_STATUS_DANGER_PRESSED)), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_add_event_cb(fr, backupFactoryResetCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* frl = lv_label_create(fr); char frlbl[48]; snprintf(frlbl, sizeof frlbl, LV_SYMBOL_TRASH " %s", TR("Factory reset")); lv_label_set_text(frl, frlbl); lv_obj_set_style_text_font(frl, &g_font_14, LV_PART_MAIN); - lv_obj_set_style_text_color(frl, lv_color_hex(0xFFE2E2), LV_PART_MAIN); + lv_obj_set_style_text_color(frl, + lv_color_hex(themeRole(0xFFE2E2, COLOR_ON_STATUS_DANGER)), LV_PART_MAIN); lv_obj_center(frl); y += SC(46); @@ -38251,7 +38491,7 @@ static void startLockingCountdown() { lv_obj_set_style_bg_color(card, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(card, 12, LV_PART_MAIN); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -38263,7 +38503,7 @@ static void startLockingCountdown() { s_locking_count = lv_label_create(card); lv_label_set_text(s_locking_count, "1"); - lv_obj_set_style_text_color(s_locking_count, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(s_locking_count, lv_color_hex(COLOR_STATUS_OK_TEXT), LV_PART_MAIN); lv_obj_set_style_text_font(s_locking_count, &g_font_16, LV_PART_MAIN); lv_obj_align(s_locking_count, LV_ALIGN_CENTER, 0, 6); @@ -39256,7 +39496,8 @@ static void showSubtleNotifyLvgl(const char* text, uint32_t duration_ms) { lv_obj_remove_style_all(s_notify_chip); lv_obj_set_size(s_notify_chip, LV_SIZE_CONTENT, LV_SIZE_CONTENT); // hugs the text lv_obj_set_style_bg_opa(s_notify_chip, LV_OPA_90, LV_PART_MAIN); - lv_obj_set_style_bg_color(s_notify_chip, lv_color_hex(0x16191C), LV_PART_MAIN); // muted dark grey, not the accent-bordered panel + lv_obj_set_style_bg_color(s_notify_chip, + lv_color_hex(themeRole(0x16191C, COLOR_RAISED)), LV_PART_MAIN); lv_obj_set_style_radius(s_notify_chip, 14, LV_PART_MAIN); // pill lv_obj_set_style_border_width(s_notify_chip, 1, LV_PART_MAIN); lv_obj_set_style_border_color(s_notify_chip, lv_color_hex(0x33383E), LV_PART_MAIN); // dim hairline, no bright accent @@ -39566,11 +39807,11 @@ static void ccGpsNoneCb(lv_event_t* e) { g_lv.task->showAlert(TR("No onboard GPS"), 1200); } #endif -// Theme-colour chip (both boards): close the control center, open the accent picker. +// Day/Night chip: persist the opposite palette and rebuild the complete UI on restart. static void ccThemeCb(lv_event_t* e) { if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; closeControlCenter(); - openAccentPicker(); + themeModeRestart(s_theme_day ? TOUCH_THEME_NIGHT : TOUCH_THEME_DAY); } #if defined(HAS_THINKNODE_M9) || defined(TLORA_PAGER) @@ -39816,7 +40057,7 @@ static void openPowerMenu() { lv_obj_set_size(card, card_w, card_h); lv_obj_align(card, LV_ALIGN_CENTER, 0, 0); styleSurface(card, COLOR_PANEL, 10); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 12, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -39833,14 +40074,17 @@ static void openPowerMenu() { lv_obj_align(b, LV_ALIGN_TOP_MID, 0, y); styleButton(b); if (bg) { - lv_obj_set_style_bg_color(b, lv_color_hex(bg), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, + lv_color_hex(themeRole(bg, COLOR_STATUS_DANGER)), LV_PART_MAIN); lv_obj_set_style_bg_opa(b, LV_OPA_COVER, LV_PART_MAIN); } lv_obj_add_event_cb(b, cb, LV_EVENT_CLICKED, nullptr); lv_obj_t* l = lv_label_create(b); lv_label_set_text(l, TR(txt)); lv_obj_set_style_text_font(l, &g_font_14, LV_PART_MAIN); - lv_obj_set_style_text_color(l, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(l, + lv_color_hex(bg ? COLOR_ON_STATUS_DANGER + : COLOR_TEXT), LV_PART_MAIN); lv_obj_center(l); return b; }; @@ -39927,7 +40171,8 @@ static void ccToggle(lv_obj_t* parent, const char* sym, const char* label, lv_obj_add_event_cb(b, ccLongNavCb, LV_EVENT_LONG_PRESSED, (void*)(intptr_t)long_cat); lv_obj_t* ic = lv_label_create(b); lv_label_set_text(ic, sym); - lv_obj_set_style_text_color(ic, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(ic, + lv_color_hex(active ? COLOR_ON_ACCENT : COLOR_TEXT), LV_PART_MAIN); if (sub_text && sub_text[0]) { // Glyph + a small caption word stacked inside the circle (the keyboard chip keeps its // off/on/auto mode visible under the keyboard icon). Shrink the glyph a touch to make room. @@ -39936,7 +40181,8 @@ static void ccToggle(lv_obj_t* parent, const char* sym, const char* label, lv_obj_t* st = lv_label_create(b); lv_label_set_text(st, sub_text); lv_obj_set_style_text_font(st, &g_font_12, LV_PART_MAIN); - lv_obj_set_style_text_color(st, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(st, + lv_color_hex(active ? COLOR_ON_ACCENT : COLOR_TEXT), LV_PART_MAIN); lv_obj_align(st, LV_ALIGN_BOTTOM_MID, 0, d <= 42 ? -1 : -5); } else { // Just the icon, centred. @@ -40202,7 +40448,7 @@ static void openControlCenter() { lv_obj_set_style_bg_opa(card, 218, LV_PART_MAIN); lv_obj_set_style_radius(card, 12, LV_PART_MAIN); // Subtle light hairline (instead of the near-black border) reads more like a glass edge. - lv_obj_set_style_border_color(card, lv_color_hex(0x3A3D42), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(themeRole(0x3A3D42, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); @@ -40521,7 +40767,8 @@ static void openControlCenter() { // settings reorg gave GPS its own category (the GPS block used to live under radio/device). ccToggle(row, LV_SYMBOL_GPS, TR("GPS"), gps_on, ccGpsCb, tw, th, CAT_GPS); #endif - ccToggle(row, LV_SYMBOL_TINT, TR("Theme"), false, ccThemeCb, tw, th, CAT_DISPLAY); + ccToggle(row, s_theme_day ? TOUCH_SYM_SUN : TOUCH_SYM_MOON, TR("Theme"), s_theme_day, + ccThemeCb, tw, th, CAT_DISPLAY); #if CAP_KEYBOARD // Keyboard-backlight chip: the keyboard glyph WITH its off/on/auto mode word beneath it, // so the mode stays visible at a glance (sub_text stacks a small caption under the icon). @@ -40842,7 +41089,7 @@ static void luaStoreMarkBtnBusy(lv_obj_t* b) { if (!b) return; const uint32_t n = lv_obj_get_child_cnt(b); for (uint32_t k = 0; k < n; k++) lv_obj_add_flag(lv_obj_get_child(b, k), LV_OBJ_FLAG_HIDDEN); - lv_obj_set_style_bg_color(b, lv_color_hex(0x39404C), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(themeRole(0x39404C, COLOR_SECONDARY_ACTION)), LV_PART_MAIN); #if LV_USE_SPINNER lv_obj_t* sp = lv_spinner_create(b, 800, 60); const lv_coord_t d = lv_font_get_line_height(&g_font_12) + 2; @@ -41071,7 +41318,8 @@ static void luaStoreStyleTabs() { lv_obj_set_style_bg_color(b, lv_color_hex(on ? COLOR_ACCENT : COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(b, LV_OPA_COVER, LV_PART_MAIN); lv_obj_t* l = lv_obj_get_child(b, 0); - if (l) lv_obj_set_style_text_color(l, lv_color_hex(on ? 0x0E1216 : COLOR_SUB), LV_PART_MAIN); + if (l) lv_obj_set_style_text_color(l, + lv_color_hex(on ? themeRole(0x0E1216, COLOR_ON_ACCENT) : COLOR_SUB), LV_PART_MAIN); } } @@ -41195,7 +41443,7 @@ static void luaStoreRebuildList() { lv_obj_set_style_bg_color(row, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(row, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(row, 10, LV_PART_MAIN); - lv_obj_set_style_border_color(row, lv_color_hex(0x232830), LV_PART_MAIN); + lv_obj_set_style_border_color(row, lv_color_hex(themeRole(0x232830, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_border_width(row, 1, LV_PART_MAIN); lv_obj_set_style_pad_hor(row, 8, LV_PART_MAIN); lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE); @@ -41223,10 +41471,19 @@ static void luaStoreRebuildList() { lv_obj_set_size(b, bw, 28); lv_obj_align(b, LV_ALIGN_RIGHT_MID, xoff, 0); lv_obj_set_style_radius(b, 8, LV_PART_MAIN); - lv_obj_set_style_bg_color(b, lv_color_hex(col), LV_PART_MAIN); + const bool danger = col == 0x8A4444; + const bool info = col == 0x4F9DF7; + lv_obj_set_style_bg_color(b, + lv_color_hex(danger ? themeRole(col, COLOR_STATUS_DANGER) + : info ? COLOR_STATUS_INFO : col), LV_PART_MAIN); lv_obj_t* bl = lv_label_create(b); lv_label_set_text(bl, txt); lv_obj_set_style_text_font(bl, &g_font_12, LV_PART_MAIN); + if (s_theme_day) { + lv_obj_set_style_text_color(bl, + lv_color_hex(danger ? COLOR_ON_STATUS_DANGER + : info ? COLOR_ON_STATUS_INFO : COLOR_ON_ACCENT), LV_PART_MAIN); + } lv_obj_center(bl); lv_obj_add_event_cb(b, cb, LV_EVENT_CLICKED, (void*)ud); return b; @@ -41393,7 +41650,7 @@ static void luaStoreRebuildList() { lv_obj_set_style_bg_color(row, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(row, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(row, 10, LV_PART_MAIN); - lv_obj_set_style_border_color(row, lv_color_hex(0x232830), LV_PART_MAIN); + lv_obj_set_style_border_color(row, lv_color_hex(themeRole(0x232830, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_border_width(row, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(row, 7, LV_PART_MAIN); lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE); @@ -41443,7 +41700,7 @@ static void luaStoreRebuildList() { lv_obj_t* bl = lv_label_create(b); const bool cur = inst && strcmp(inst->ver, s_lua_cat[i].ver) == 0; if (!inst) lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN); - else if (!cur) lv_obj_set_style_bg_color(b, lv_color_hex(0x4F9DF7), LV_PART_MAIN); + else if (!cur) lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_INFO), LV_PART_MAIN); const bool runnable = luaStoreBoardMeets(s_lua_cat[i].requires_cap); lv_label_set_text(bl, !runnable ? TR("N/A") : !inst ? TR("Get") : cur ? TR("Remove") : TR("Update")); lv_obj_set_style_text_font(bl, &g_font_12, LV_PART_MAIN); @@ -41451,18 +41708,24 @@ static void luaStoreRebuildList() { if (!runnable && !inst) { // Greyed and inert, with the reason on the row rather than a toast after // a pointless download. - lv_obj_set_style_bg_color(b, lv_color_hex(0x39404C), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(themeRole(0x39404C, COLOR_SECONDARY_ACTION)), LV_PART_MAIN); lv_obj_clear_flag(b, LV_OBJ_FLAG_CLICKABLE); lv_obj_set_style_text_color(ds, lv_color_hex(0xD7574E), LV_PART_MAIN); lv_label_set_text(ds, TR("This board cannot run this app.")); } else if (inst && cur) { // Remove needs the INSTALLED index int ii = (int)(inst - s_lua_inst); - lv_obj_set_style_bg_color(b, lv_color_hex(0x8A4444), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(themeRole(0x8A4444, COLOR_STATUS_DANGER)), LV_PART_MAIN); lv_obj_add_event_cb(b, luaStoreRemoveBtnCb, LV_EVENT_CLICKED, (void*)(intptr_t)ii); } else { lv_obj_add_event_cb(b, luaStoreInstallBtnCb, LV_EVENT_CLICKED, (void*)(intptr_t)i); } + if (s_theme_day) { + lv_obj_set_style_text_color(bl, + lv_color_hex(!runnable && !inst ? COLOR_TEXT + : inst && cur ? COLOR_ON_STATUS_DANGER + : inst ? COLOR_ON_STATUS_INFO : COLOR_ON_ACCENT), LV_PART_MAIN); + } // A rebuild can land mid-download (a card scan or the catalog fetch finishing) // and would wipe the spinner set on tap, so repaint it from the in-flight id. if (s_luastore_busy && s_luainst_id[0] && strcmp(s_luainst_id, s_lua_cat[i].id) == 0) @@ -41491,7 +41754,7 @@ static void luaStoreRebuildList() { lv_obj_set_style_bg_color(row, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(row, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(row, 10, LV_PART_MAIN); - lv_obj_set_style_border_color(row, lv_color_hex(0x232830), LV_PART_MAIN); + lv_obj_set_style_border_color(row, lv_color_hex(themeRole(0x232830, COLOR_BORDER)), LV_PART_MAIN); lv_obj_set_style_border_width(row, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(row, 7, LV_PART_MAIN); lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE); @@ -41508,10 +41771,12 @@ static void luaStoreRebuildList() { lv_obj_t* b = lv_btn_create(row); lv_obj_set_size(b, act_w, 28); lv_obj_align(b, LV_ALIGN_RIGHT_MID, 0, 0); - lv_obj_set_style_bg_color(b, lv_color_hex(0x8A4444), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(themeRole(0x8A4444, COLOR_STATUS_DANGER)), LV_PART_MAIN); lv_obj_t* bl = lv_label_create(b); lv_label_set_text(bl, TR("Remove")); lv_obj_set_style_text_font(bl, &g_font_12, LV_PART_MAIN); + if (s_theme_day) + lv_obj_set_style_text_color(bl, lv_color_hex(COLOR_ON_STATUS_DANGER), LV_PART_MAIN); lv_obj_center(bl); lv_obj_add_event_cb(b, luaStoreRemoveBtnCb, LV_EVENT_CLICKED, (void*)(intptr_t)i); } @@ -42936,10 +43201,11 @@ static void buildGlobalStatusBar() { }; g_statusbar.inbox_add = mk(0); // leftmost — green primary lv_obj_set_style_bg_color(g_statusbar.inbox_add, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(g_statusbar.inbox_add, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(g_statusbar.inbox_add, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(g_statusbar.inbox_add, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_add_event_cb(g_statusbar.inbox_add, chatsAddBtnCb, LV_EVENT_CLICKED, nullptr); { lv_obj_t* l = lv_label_create(g_statusbar.inbox_add); lv_label_set_text(l, LV_SYMBOL_PLUS); - lv_obj_set_style_text_color(l, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(l, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_set_style_text_font(l, uiChromeFont(), LV_PART_MAIN); lv_obj_center(l); } g_statusbar.inbox_mark = mk(1); // middle — mark all read lv_obj_add_event_cb(g_statusbar.inbox_mark, chatsMarkAllReadBtnCb, LV_EVENT_CLICKED, nullptr); @@ -44374,7 +44640,8 @@ static void buildBootSplash() { lv_label_set_recolor(wm, true); lv_label_set_text(wm, TR("WADA#15B6A6 MESH#")); lv_obj_set_style_text_font(wm, &lv_font_unscii_16, LV_PART_MAIN); - lv_obj_set_style_text_color(wm, lv_color_hex(0xFFFFFF), LV_PART_MAIN); + lv_obj_set_style_text_color(wm, + lv_color_hex(themeRole(0xFFFFFF, COLOR_TEXT)), LV_PART_MAIN); lv_obj_set_style_text_letter_space(wm, 3, LV_PART_MAIN); lv_obj_align(wm, LV_ALIGN_CENTER, 0, 60); // below the now-centred mark lv_obj_set_style_opa(wm, LV_OPA_TRANSP, LV_PART_MAIN); @@ -44463,13 +44730,15 @@ static lv_obj_t* setupBtn(const char* txt, lv_event_cb_t cb, bool primary, styleButton(b); if (primary) { lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); - lv_obj_set_style_bg_color(b, lv_color_hex(0x3B7039), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_STATUS_OK_PRESSED), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(b, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); } lv_obj_add_event_cb(b, cb, LV_EVENT_CLICKED, nullptr); lv_obj_t* l = lv_label_create(b); lv_label_set_text(l, TR(txt)); lv_obj_set_style_text_font(l, &g_font_14, LV_PART_MAIN); - lv_obj_set_style_text_color(l, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_text_color(l, + lv_color_hex(primary ? COLOR_ON_STATUS_OK : COLOR_TEXT), LV_PART_MAIN); lv_obj_center(l); return b; } @@ -44606,8 +44875,13 @@ static void setupRegionRowCb(lv_event_t* e) { const uint32_t n = lv_obj_get_child_cnt(s_setup_region_list); for (uint32_t i = 0; i < n; ++i) { lv_obj_t* c = lv_obj_get_child(s_setup_region_list, i); - if (c) lv_obj_set_style_bg_color( - c, lv_color_hex((int)i == idx ? COLOR_STATUS_OK : 0x1A1B1C), LV_PART_MAIN); + if (c) { + const bool selected = (int)i == idx; + lv_obj_set_style_bg_color( + c, lv_color_hex(selected ? COLOR_STATUS_OK : COLOR_CONTROL), LV_PART_MAIN); + lv_obj_set_style_text_color( + c, lv_color_hex(selected ? COLOR_ON_STATUS_OK : COLOR_TEXT), LV_PART_MAIN); + } } #if CAP_KEYPAD_NAV // With 20 region presets in this list, walking NEXT one detent at a time past @@ -44634,8 +44908,10 @@ static void setupFillRegionList() { lv_obj_t* r = lv_btn_create(s_setup_region_list); lv_obj_set_size(r, rw, 34); styleButton(r); - lv_obj_set_style_bg_color(r, lv_color_hex(i == s_setup_region_sel ? COLOR_STATUS_OK : 0x1A1B1C), + lv_obj_set_style_bg_color(r, lv_color_hex(i == s_setup_region_sel ? COLOR_STATUS_OK : COLOR_CONTROL), LV_PART_MAIN); + lv_obj_set_style_text_color(r, + lv_color_hex(i == s_setup_region_sel ? COLOR_ON_STATUS_OK : COLOR_TEXT), LV_PART_MAIN); lv_obj_add_event_cb(r, setupRegionRowCb, LV_EVENT_CLICKED, (void*)(intptr_t)i); lv_obj_t* l = lv_label_create(r); lv_label_set_text(l, k_mesh_radio_presets[i].label); @@ -44797,6 +45073,25 @@ static void touchThemeApplyCb(lv_theme_t* /*th*/, lv_obj_t* obj) { lv_obj_set_style_bg_color(obj, lv_color_hex(COLOR_ACCENT), LV_PART_INDICATOR | LV_STATE_CHECKED); } + if (!s_theme_day) return; + if (lv_obj_check_type(obj, &lv_textarea_class)) { + lv_obj_set_style_bg_color(obj, lv_color_hex(COLOR_FIELD), LV_PART_MAIN); + lv_obj_set_style_text_color(obj, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_border_color(obj, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); + lv_obj_set_style_text_color(obj, lv_color_hex(COLOR_SUB), LV_PART_TEXTAREA_PLACEHOLDER); + lv_obj_set_style_bg_color(obj, lv_color_hex(COLOR_ACCENT), LV_PART_CURSOR); + } else if (lv_obj_check_type(obj, &lv_dropdown_class)) { + lv_obj_set_style_bg_color(obj, lv_color_hex(COLOR_FIELD), LV_PART_MAIN); + lv_obj_set_style_text_color(obj, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + lv_obj_set_style_border_color(obj, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); + } else if (lv_obj_check_type(obj, &lv_slider_class)) { + lv_obj_set_style_bg_color(obj, lv_color_hex(COLOR_TRACK), LV_PART_MAIN); + lv_obj_set_style_bg_color(obj, lv_color_hex(COLOR_ACCENT), LV_PART_INDICATOR); + lv_obj_set_style_bg_color(obj, lv_color_hex(COLOR_ACCENT), LV_PART_KNOB); + } else if (lv_obj_check_type(obj, &lv_checkbox_class)) { + lv_obj_set_style_border_color(obj, lv_color_hex(COLOR_ACCENT), LV_PART_INDICATOR); + lv_obj_set_style_text_color(obj, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); + } } static lv_theme_t s_touch_theme; // our wrapper theme (parent = stock default) @@ -44888,7 +45183,7 @@ static void openAccentPicker() { lv_obj_set_scroll_dir(s_accent_picker, LV_DIR_VER); lv_obj_t* title = lv_label_create(s_accent_picker); - lv_label_set_text(title, TR("Theme colour")); + lv_label_set_text(title, TR("Accent colour")); lv_obj_set_style_text_font(title, &g_font_16, LV_PART_MAIN); lv_obj_set_style_text_color(title, lv_color_hex(COLOR_TEXT), LV_PART_MAIN); @@ -45783,13 +46078,13 @@ static void makeSensorsTab(lv_obj_t* tab) { lv_chart_set_div_line_count(s_sens_env_chart, 3, 4); lv_obj_set_style_bg_color(s_sens_env_chart, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_sens_env_chart, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(s_sens_env_chart, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(s_sens_env_chart, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_sens_env_chart, 1, LV_PART_MAIN); lv_obj_set_style_radius(s_sens_env_chart, 6, LV_PART_MAIN); - lv_obj_set_style_line_color(s_sens_env_chart, lv_color_hex(0x1A1D1F), LV_PART_MAIN); + lv_obj_set_style_line_color(s_sens_env_chart, lv_color_hex(COLOR_CHART_GRID), LV_PART_MAIN); lv_obj_set_style_text_font(s_sens_env_chart, &g_font_12, LV_PART_TICKS); lv_obj_set_style_text_color(s_sens_env_chart, lv_color_hex(COLOR_SUB), LV_PART_TICKS); - lv_obj_set_style_line_color(s_sens_env_chart, lv_color_hex(0x2A2E30), LV_PART_TICKS); + lv_obj_set_style_line_color(s_sens_env_chart, lv_color_hex(COLOR_CHART_TICK), LV_PART_TICKS); lv_obj_set_style_pad_left(s_sens_env_chart, 4, LV_PART_TICKS); lv_obj_set_style_pad_top(s_sens_env_chart, 6, LV_PART_MAIN); lv_obj_set_style_pad_bottom(s_sens_env_chart, 6, LV_PART_MAIN); @@ -45819,13 +46114,13 @@ static void makeSensorsTab(lv_obj_t* tab) { lv_chart_set_div_line_count(s_sens_press_chart, 3, 4); lv_obj_set_style_bg_color(s_sens_press_chart, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_sens_press_chart, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(s_sens_press_chart, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(s_sens_press_chart, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_sens_press_chart, 1, LV_PART_MAIN); lv_obj_set_style_radius(s_sens_press_chart, 6, LV_PART_MAIN); - lv_obj_set_style_line_color(s_sens_press_chart, lv_color_hex(0x1A1D1F), LV_PART_MAIN); + lv_obj_set_style_line_color(s_sens_press_chart, lv_color_hex(COLOR_CHART_GRID), LV_PART_MAIN); lv_obj_set_style_text_font(s_sens_press_chart, &g_font_12, LV_PART_TICKS); lv_obj_set_style_text_color(s_sens_press_chart, lv_color_hex(COLOR_SUB), LV_PART_TICKS); - lv_obj_set_style_line_color(s_sens_press_chart, lv_color_hex(0x2A2E30), LV_PART_TICKS); + lv_obj_set_style_line_color(s_sens_press_chart, lv_color_hex(COLOR_CHART_TICK), LV_PART_TICKS); lv_obj_set_style_pad_left(s_sens_press_chart, 4, LV_PART_TICKS); lv_obj_set_style_pad_top(s_sens_press_chart, 6, LV_PART_MAIN); lv_obj_set_style_pad_bottom(s_sens_press_chart, 6, LV_PART_MAIN); @@ -45882,13 +46177,13 @@ static void makeSensorsTab(lv_obj_t* tab) { lv_chart_set_div_line_count(s_sens_batt_chart, 3, 4); lv_obj_set_style_bg_color(s_sens_batt_chart, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_sens_batt_chart, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(s_sens_batt_chart, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(s_sens_batt_chart, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(s_sens_batt_chart, 1, LV_PART_MAIN); lv_obj_set_style_radius(s_sens_batt_chart, 6, LV_PART_MAIN); - lv_obj_set_style_line_color(s_sens_batt_chart, lv_color_hex(0x1A1D1F), LV_PART_MAIN); + lv_obj_set_style_line_color(s_sens_batt_chart, lv_color_hex(COLOR_CHART_GRID), LV_PART_MAIN); lv_obj_set_style_text_font(s_sens_batt_chart, &g_font_12, LV_PART_TICKS); lv_obj_set_style_text_color(s_sens_batt_chart, lv_color_hex(COLOR_SUB), LV_PART_TICKS); - lv_obj_set_style_line_color(s_sens_batt_chart, lv_color_hex(0x2A2E30), LV_PART_TICKS); + lv_obj_set_style_line_color(s_sens_batt_chart, lv_color_hex(COLOR_CHART_TICK), LV_PART_TICKS); lv_obj_set_style_pad_left(s_sens_batt_chart, 4, LV_PART_TICKS); lv_obj_set_style_pad_top(s_sens_batt_chart, 6, LV_PART_MAIN); lv_obj_set_style_pad_bottom(s_sens_batt_chart, 6, LV_PART_MAIN); @@ -45952,6 +46247,7 @@ static void buildUiTree() { // Load the saved theme accent before any widget is built so the whole tree // adopts it. g_lv.tabview/keyboard are still null here, so applyAccent only // sets the colour globals (no live re-style needed at boot). + applyThemeMode(touchPrefsGetThemeMode()); applyAccent(touchPrefsGetAccentColor()); lv_obj_t* root = lv_scr_act(); @@ -46202,8 +46498,8 @@ static void buildUiTree() { lv_obj_add_event_cb(g_lv.keyboard, accentLongPressCb, LV_EVENT_LONG_PRESSED, nullptr); // Dark-theme keyboard styling lv_obj_set_style_bg_color(g_lv.keyboard, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); - lv_obj_set_style_bg_color(g_lv.keyboard, lv_color_hex(0x1B2B3A), LV_PART_ITEMS); - lv_obj_set_style_border_color(g_lv.keyboard, lv_color_hex(0x18191A), LV_PART_ITEMS); + lv_obj_set_style_bg_color(g_lv.keyboard, lv_color_hex(COLOR_ACCENT_SURFACE), LV_PART_ITEMS); + lv_obj_set_style_border_color(g_lv.keyboard, lv_color_hex(COLOR_BORDER), LV_PART_ITEMS); lv_obj_set_style_text_color(g_lv.keyboard, lv_color_hex(COLOR_TEXT), LV_PART_ITEMS); // Key labels use the extended font (montserrat + Cyrillic/Greek/Arabic fallback) // so the secondary keyboard layouts render real glyphs instead of tofu boxes. @@ -46230,8 +46526,8 @@ static void buildUiTree() { auto makeRotBtn = [](const char* sym, lv_event_cb_t cb) { lv_obj_t* b = lv_btn_create(lv_layer_top()); lv_obj_set_size(b, 32, 26); - lv_obj_set_style_bg_color(b, lv_color_hex(0x1B2B3A), LV_PART_MAIN); - lv_obj_set_style_border_color(b, lv_color_hex(0x2A3D52), LV_PART_MAIN); + lv_obj_set_style_bg_color(b, lv_color_hex(COLOR_ACCENT_SURFACE), LV_PART_MAIN); + lv_obj_set_style_border_color(b, lv_color_hex(COLOR_ACCENT_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(b, 1, LV_PART_MAIN); lv_obj_set_style_radius(b, 6, LV_PART_MAIN); lv_obj_set_style_pad_all(b, 0, LV_PART_MAIN); @@ -46296,7 +46592,7 @@ static void buildUiTree() { lv_obj_set_style_text_font(s_live_diag_label, &g_font_12, LV_PART_MAIN); lv_obj_set_style_text_color(s_live_diag_label, lv_color_hex(0xC7D2DE), LV_PART_MAIN); lv_obj_set_style_bg_opa(s_live_diag_label, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_bg_color(s_live_diag_label, lv_color_hex(0x0A0B0C), LV_PART_MAIN); + lv_obj_set_style_bg_color(s_live_diag_label, lv_color_hex(COLOR_FIELD), LV_PART_MAIN); lv_obj_set_style_pad_hor(s_live_diag_label, 4, LV_PART_MAIN); lv_obj_set_style_pad_ver(s_live_diag_label, 2, LV_PART_MAIN); lv_obj_set_style_radius(s_live_diag_label, 4, LV_PART_MAIN); @@ -46718,7 +47014,7 @@ static void openTelemetryWindow(const uint8_t* key6, const char* name, int state lv_obj_set_size(card, cardw, sh - STATUSBAR_H - 12); lv_obj_align(card, LV_ALIGN_TOP_MID, 0, 6); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 10, LV_PART_MAIN); lv_obj_set_scroll_dir(card, LV_DIR_VER); @@ -46842,16 +47138,16 @@ static void openTelemetryWindow(const uint8_t* key6, const char* name, int state lv_chart_set_div_line_count(chart, 4, 6); lv_obj_set_style_bg_color(chart, lv_color_hex(COLOR_PANEL), LV_PART_MAIN); lv_obj_set_style_bg_opa(chart, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_color(chart, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(chart, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(chart, 1, LV_PART_MAIN); lv_obj_set_style_radius(chart, 6, LV_PART_MAIN); - lv_obj_set_style_line_color(chart, lv_color_hex(0x1A1D1F), LV_PART_MAIN); + lv_obj_set_style_line_color(chart, lv_color_hex(COLOR_CHART_GRID), LV_PART_MAIN); lv_obj_set_style_size(chart, 3, LV_PART_INDICATOR); lv_obj_set_style_pad_top(chart, 6, LV_PART_MAIN); lv_obj_set_style_pad_bottom(chart, 6, LV_PART_MAIN); lv_obj_set_style_text_font(chart, &g_font_12, LV_PART_TICKS); lv_obj_set_style_text_color(chart, lv_color_hex(COLOR_SUB), LV_PART_TICKS); - lv_obj_set_style_line_color(chart, lv_color_hex(0x2A2E30), LV_PART_TICKS); + lv_obj_set_style_line_color(chart, lv_color_hex(COLOR_CHART_TICK), LV_PART_TICKS); lv_obj_set_style_pad_left(chart, 4, LV_PART_TICKS); lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 4, 0, 4, 1, true, 40); lv_chart_set_axis_tick(chart, LV_CHART_AXIS_SECONDARY_Y, 4, 0, 4, 1, true, 36); @@ -46963,7 +47259,7 @@ static void openTelemetryConfigWindow() { lv_obj_set_style_max_height(card, sh - STATUSBAR_H - 24, LV_PART_MAIN); lv_obj_align(card, LV_ALIGN_TOP_MID, 0, 16); styleSurface(card, COLOR_PANEL, 8); - lv_obj_set_style_border_color(card, lv_color_hex(0x18191A), LV_PART_MAIN); + lv_obj_set_style_border_color(card, lv_color_hex(COLOR_BORDER), LV_PART_MAIN); lv_obj_set_style_border_width(card, 1, LV_PART_MAIN); lv_obj_set_style_pad_all(card, 12, LV_PART_MAIN); lv_obj_set_scroll_dir(card, LV_DIR_VER); @@ -47027,6 +47323,7 @@ static void openTelemetryConfigWindow() { lv_obj_set_size(ap, cardw - 24, 32); lv_obj_set_pos(ap, 0, y + 42); styleButton(ap); lv_obj_set_style_bg_color(ap, lv_color_hex(COLOR_STATUS_OK), LV_PART_MAIN); + lv_obj_set_style_text_color(ap, lv_color_hex(COLOR_ON_STATUS_OK), LV_PART_MAIN); lv_obj_set_style_bg_opa(ap, LV_OPA_40, LV_PART_MAIN); lv_obj_add_event_cb(ap, telemPollApplyCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* apl = lv_label_create(ap); lv_label_set_text(apl, TR("Apply auto-poll")); lv_obj_center(apl); @@ -47037,7 +47334,8 @@ static void openTelemetryConfigWindow() { lv_obj_t* clr = lv_btn_create(card); lv_obj_set_size(clr, cardw - 24, 32); lv_obj_set_pos(clr, 0, y); styleButton(clr); - lv_obj_set_style_bg_color(clr, lv_color_hex(0xB23A48), LV_PART_MAIN); + lv_obj_set_style_bg_color(clr, lv_color_hex(themeRole(0xB23A48, COLOR_STATUS_DANGER)), LV_PART_MAIN); + lv_obj_set_style_text_color(clr, lv_color_hex(COLOR_ON_STATUS_DANGER), LV_PART_MAIN); lv_obj_add_event_cb(clr, telemClearCb, LV_EVENT_CLICKED, nullptr); lv_obj_t* cl = lv_label_create(clr); lv_label_set_text(cl, TR(LV_SYMBOL_TRASH " Clear history")); lv_obj_center(cl); useChainedFont(cl); @@ -52090,6 +52388,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no // accent itself), so without this its accent-tinted glyphs — the Wi-Fi/Bluetooth // icons — would freeze the compile-time default instead of the user's colour. // Idempotent: buildUiTree's own call just re-sets the same globals. + applyThemeMode(touchPrefsGetThemeMode()); applyAccent(touchPrefsGetAccentColor()); // Build the always-on top status bar AFTER the display driver is diff --git a/src/ui-touch/i18n_builtin.h b/src/ui-touch/i18n_builtin.h index c908fc9..1f9543a 100644 --- a/src/ui-touch/i18n_builtin.h +++ b/src/ui-touch/i18n_builtin.h @@ -32,6 +32,7 @@ static const I18nPair kBuiltin_hu[] = { { "About", "Névjegy" }, { "About / credits", "Névjegy / köszönet" }, { "About the map", "A térképről" }, + { "Accent colour", "Kiemelőszín" }, { "Accent popups", "Ékezetes felugró ablakok" }, { "Accent popups: off", "Accent popups: kikapcsolva" }, { "Accent popups: on", "Accent popups: bekapcsolva" }, @@ -60,6 +61,7 @@ static const I18nPair kBuiltin_hu[] = { { "App drawer as home", "Alkalmazásfiók kezdőlapként" }, { "App icon size", "Alkalmazás ikon mérete" }, { "App permissions", "Appengedélyek" }, + { "Appearance", "Megjelenés" }, { "Applies after restart.", "Újraindítás után érvényes." }, { "Apply", "Alkalmaz" }, { "Apply auto-poll", "Automatikus szavazás alkalmazása" }, @@ -231,6 +233,7 @@ static const I18nPair kBuiltin_hu[] = { { "Danger zone", "Veszélyes zóna" }, { "Data -> SD card on reboot\n(card must be inserted)", "Adatok -> SD-kártya újraindításkor\n(a kártyát be kell helyezni)" }, { "Data -> internal on reboot", "Adatok -> belső újraindításkor" }, + { "Day", "Nappali" }, { "Delete", "Töröl" }, { "Delete %d contact(s)?", "%d névjegy törlése?" }, { "Delete chat", "Csevegés törlése" }, @@ -531,6 +534,7 @@ static const I18nPair kBuiltin_hu[] = { { "New game", "Új játék" }, { "New room msg", "Új szobaüzenet" }, { "Next", "Következő" }, + { "Night", "Éjszakai" }, { "Night mode (invert)", "Éjszakai mód (invertált)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Nem található .json biztonsági mentés.\nElőször exportáljon egyet, vagy másolja át a\nmeshcore-backup.json fájlt az\nSD-kártyára vagy a belső flash memóriába." }, { "No @mentions yet", "Még nincsenek @említések" }, @@ -896,7 +900,7 @@ static const I18nPair kBuiltin_hu[] = { { "Test builds: off", "Tesztverziók: kikapcsolva" }, { "Test builds: on", "Tesztverziók: bekapcsolva" }, { "Theme", "Téma" }, - { "Theme colour", "Téma szine" }, + { "Theme save failed", "Téma mentése sikertelen" }, { "Then pick a mode:", "Majd válassz módot:" }, { "This contact hasn't shared\na GPS position.", "Ez a kapcsolat nem osztotta meg GPS-pozícióját." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Ez az eszköz egyszerre képes Wi-Fi-t és Bluetooth-t futtatni, önálló rádióként és telefonkiegészítőként (MeshCore alkalmazás) is.\n\nA kettő egyidejű futtatása több RAM-ot fogyaszt, ezért csak azt kapcsold be, amire szükséged van.\n\nBármikor beállíthatod őket a Beállításokban." }, @@ -1057,6 +1061,7 @@ static const I18nPair kBuiltin_nl[] = { { "AUTOMATIC ADVERTS", "AUTOMATISCHE ADVERTS" }, { "About", "Over" }, { "About the map", "Over de kaart" }, + { "Accent colour", "Accentkleur" }, { "Accent popups", "Accent-popups" }, { "Accent popups: off", "Accent-popups: uit" }, { "Accent popups: on", "Accent-popups: aan" }, @@ -1083,6 +1088,7 @@ static const I18nPair kBuiltin_nl[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Iedereen kan deelnemen. Sleutel komt van de hashtag (kleine letters)." }, { "App drawer as home", "App-lade als startscherm" }, { "App icon size", "Grootte app-pictogrammen" }, + { "Appearance", "Weergave" }, { "Applies after restart.", "Actief na herstart." }, { "Apply", "Toepassen" }, { "Apply auto-poll", "Auto-poll toepassen" }, @@ -1245,6 +1251,7 @@ static const I18nPair kBuiltin_nl[] = { { "Danger zone", "Gevarenzone" }, { "Data -> SD card on reboot\n(card must be inserted)", "Gegevens -> SD-kaart bij herstart\n(kaart moet geplaatst zijn)" }, { "Data -> internal on reboot", "Gegevens -> intern bij herstart" }, + { "Day", "Dag" }, { "Delete", "Verwijderen" }, { "Delete %d contact(s)?", "%d contact(en) verwijderen?" }, { "Delete chat", "Chat verwijderen" }, @@ -1515,6 +1522,7 @@ static const I18nPair kBuiltin_nl[] = { { "New game", "Nieuw spel" }, { "New room msg", "Nieuw roombericht" }, { "Next", "Volgende" }, + { "Night", "Nacht" }, { "Night mode (invert)", "Nachtmodus (omkeren)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Geen .json back-ups gevonden.\nExporteer er eerst een, of kopieer\neen meshcore-backup.json naar de\nSD-kaart of interne flash." }, { "No @mentions yet", "Nog geen @vermeldingen" }, @@ -1832,7 +1840,7 @@ static const I18nPair kBuiltin_nl[] = { { "Test builds: off", "Testbuilds: uit" }, { "Test builds: on", "Testbuilds: aan" }, { "Theme", "Thema" }, - { "Theme colour", "Themakleur" }, + { "Theme save failed", "Thema opslaan mislukt" }, { "Then pick a mode:", "Kies daarna een modus:" }, { "This contact hasn't shared\na GPS position.", "Dit contact heeft geen\nGPS-positie gedeeld." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Dit apparaat kan Wi-Fi en Bluetooth tegelijk draaien, als zelfstandige radio én telefoon-companion (MeshCore-app) samen.\n\nBeide tegelijk gebruikt meer RAM, dus zet alleen aan wat je nodig hebt.\n\nStel ze altijd in via Instellingen." }, @@ -1976,6 +1984,7 @@ static const I18nPair kBuiltin_de[] = { { "AUTOMATIC ADVERTS", "AUTOMATISCHE ADVERTS" }, { "About", "Über" }, { "About the map", "Über die Karte" }, + { "Accent colour", "Akzentfarbe" }, { "Accent popups", "Akzent-Popups" }, { "Accent popups: off", "Akzent-Popups: aus" }, { "Accent popups: on", "Akzent-Popups: ein" }, @@ -2002,6 +2011,7 @@ static const I18nPair kBuiltin_de[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Jeder kann beitreten. Schlüssel aus dem Hashtag (Kleinbuchstaben)." }, { "App drawer as home", "App-Drawer als Startseite" }, { "App icon size", "App-Symbolgröße" }, + { "Appearance", "Erscheinungsbild" }, { "Applies after restart.", "Gilt nach Neustart." }, { "Apply", "Anwenden" }, { "Apply auto-poll", "Auto-Poll übernehmen" }, @@ -2168,6 +2178,7 @@ static const I18nPair kBuiltin_de[] = { { "Danger zone", "Gefahrenzone" }, { "Data -> SD card on reboot\n(card must be inserted)", "Daten -> SD-Karte bei Neustart\n(Karte muss eingelegt sein)" }, { "Data -> internal on reboot", "Daten -> intern bei Neustart" }, + { "Day", "Tag" }, { "Delete", "Löschen" }, { "Delete %d contact(s)?", "%d Kontakt(e) löschen?" }, { "Delete chat", "Chat löschen" }, @@ -2440,6 +2451,7 @@ static const I18nPair kBuiltin_de[] = { { "New game", "Neues Spiel" }, { "New room msg", "Neue Raumnachricht" }, { "Next", "Weiter" }, + { "Night", "Nacht" }, { "Night mode (invert)", "Nachtmodus (invertieren)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Keine .json-Backups gefunden.\nZuerst eins exportieren oder\nmeshcore-backup.json auf die\nSD-Karte oder den Flash kopieren." }, { "No @mentions yet", "Noch keine @Erwähnungen" }, @@ -2756,7 +2768,7 @@ static const I18nPair kBuiltin_de[] = { { "Test builds: off", "Test-Builds: aus" }, { "Test builds: on", "Test-Builds: an" }, { "Theme", "Thema" }, - { "Theme colour", "Themenfarbe" }, + { "Theme save failed", "Design konnte nicht gespeichert werden" }, { "Then pick a mode:", "Dann einen Modus wählen:" }, { "This contact hasn't shared\na GPS position.", "Dieser Kontakt hat keine\nGPS-Position geteilt." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Dieses Gerät kann Wi-Fi und Bluetooth gleichzeitig als eigenständiges Funkgerät und Telefon-Begleiter (MeshCore-App) betreiben.\n\nBeides gleichzeitig braucht mehr RAM, aktiviere also nur, was du brauchst.\n\nJederzeit in den Einstellungen einrichtbar." }, @@ -2905,6 +2917,7 @@ static const I18nPair kBuiltin_fr[] = { { "AUTOMATIC ADVERTS", "ADVERTS AUTOMATIQUES" }, { "About", "À propos" }, { "About the map", "À propos de la carte" }, + { "Accent colour", "Couleur d'accent" }, { "Accent popups", "Fenêtres d'accents" }, { "Accent popups: off", "Fenêtres d'accents : désactivé" }, { "Accent popups: on", "Fenêtres d'accents : activé" }, @@ -2932,6 +2945,7 @@ static const I18nPair kBuiltin_fr[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Tout le monde peut rejoindre. Clé dérivée du hashtag (minuscules)." }, { "App drawer as home", "Tiroir d'apps comme accueil" }, { "App icon size", "Taille des icônes d'app" }, + { "Appearance", "Apparence" }, { "Applies after restart.", "Appliqué après redémarrage." }, { "Apply", "Appliquer" }, { "Apply auto-poll", "Appliquer l'auto-poll" }, @@ -3085,6 +3099,7 @@ static const I18nPair kBuiltin_fr[] = { { "Danger zone", "Zone de danger" }, { "Data -> SD card on reboot\n(card must be inserted)", "Données -> carte SD au redémarrage\n(carte requise)" }, { "Data -> internal on reboot", "Données -> interne au redémarrage" }, + { "Day", "Jour" }, { "Delete", "Supprimer" }, { "Delete %d contact(s)?", "Supprimer %d contact(s) ?" }, { "Delete chat", "Supprimer la discussion" }, @@ -3344,6 +3359,7 @@ static const I18nPair kBuiltin_fr[] = { { "New game", "Nouvelle partie" }, { "New room msg", "Nouveau msg de salon" }, { "Next", "Suivant" }, + { "Night", "Nuit" }, { "Night mode (invert)", "Mode nuit (inverser)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Aucune sauvegarde .json trouvée.\nExportez-en une, ou copiez un\nmeshcore-backup.json sur la\ncarte SD ou le flash interne." }, { "No @mentions yet", "Aucune @mention pour l'instant" }, @@ -3649,7 +3665,7 @@ static const I18nPair kBuiltin_fr[] = { { "Test builds: off", "Builds de test : off" }, { "Test builds: on", "Builds de test : on" }, { "Theme", "Thème" }, - { "Theme colour", "Couleur du thème" }, + { "Theme save failed", "Échec de l’enregistrement du thème" }, { "Then pick a mode:", "Puis choisissez un mode :" }, { "This contact hasn't shared\na GPS position.", "Ce contact n'a pas partagé\nde position GPS." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Cet appareil peut utiliser Wi-Fi et Bluetooth en même temps, comme radio autonome et compagnon de téléphone (app MeshCore).\n\nLes deux à la fois consomment plus de RAM, n'activez que le nécessaire.\n\nRéglez-les à tout moment dans Réglages." }, @@ -3789,6 +3805,7 @@ static const I18nPair kBuiltin_es[] = { { "AUTOMATIC ADVERTS", "ADVERTS AUTOMÁTICOS" }, { "About", "Acerca de" }, { "About the map", "Acerca del mapa" }, + { "Accent colour", "Color de acento" }, { "Accent popups", "Ventanas de acentos" }, { "Accent popups: off", "Ventanas de acentos: desactivado" }, { "Accent popups: on", "Ventanas de acentos: activado" }, @@ -3815,6 +3832,7 @@ static const I18nPair kBuiltin_es[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Cualquiera puede unirse. La clave deriva del hashtag (minúsculas)." }, { "App drawer as home", "Cajón de apps como inicio" }, { "App icon size", "Tamaño de iconos de app" }, + { "Appearance", "Apariencia" }, { "Applies after restart.", "Se aplica tras reiniciar." }, { "Apply", "Aplicar" }, { "Apply auto-poll", "Aplicar auto-poll" }, @@ -3967,6 +3985,7 @@ static const I18nPair kBuiltin_es[] = { { "Danger zone", "Zona de peligro" }, { "Data -> SD card on reboot\n(card must be inserted)", "Datos -> tarjeta SD al reiniciar\n(tarjeta requerida)" }, { "Data -> internal on reboot", "Datos -> interno al reiniciar" }, + { "Day", "Día" }, { "Delete", "Eliminar" }, { "Delete %d contact(s)?", "¿Eliminar %d contacto(s)?" }, { "Delete chat", "Eliminar chat" }, @@ -4231,6 +4250,7 @@ static const I18nPair kBuiltin_es[] = { { "New game", "Partida nueva" }, { "New room msg", "Nuevo msj de sala" }, { "Next", "Siguiente" }, + { "Night", "Noche" }, { "Night mode (invert)", "Modo noche (invertir)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "No se hallaron copias .json.\nExporta una, o copia un\nmeshcore-backup.json a la\ntarjeta SD o flash interna." }, { "No @mentions yet", "Aún no hay @menciones" }, @@ -4537,7 +4557,7 @@ static const I18nPair kBuiltin_es[] = { { "Test builds: off", "Compilaciones de prueba: off" }, { "Test builds: on", "Compilaciones de prueba: on" }, { "Theme", "Tema" }, - { "Theme colour", "Color del tema" }, + { "Theme save failed", "No se pudo guardar el tema" }, { "Then pick a mode:", "Luego elige un modo:" }, { "This contact hasn't shared\na GPS position.", "Este contacto no ha compartido\nuna posición GPS." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Este dispositivo puede usar Wi-Fi y Bluetooth a la vez, como radio independiente y compañero del teléfono (app MeshCore).\n\nUsar ambos consume más RAM, así que activa solo lo que necesites.\n\nConfigúralos cuando quieras en Ajustes." }, @@ -4678,6 +4698,7 @@ static const I18nPair kBuiltin_it[] = { { "AUTOMATIC ADVERTS", "ADVERT AUTOMATICI" }, { "About", "Informazioni" }, { "About the map", "Informazioni sulla mappa" }, + { "Accent colour", "Colore accento" }, { "Accent popups", "Popup accenti" }, { "Accent popups: off", "Popup accenti: disattivi" }, { "Accent popups: on", "Popup accenti: attivi" }, @@ -4703,6 +4724,7 @@ static const I18nPair kBuiltin_it[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Chiunque può unirsi. La chiave deriva dall'hashtag (minuscolo)." }, { "App drawer as home", "Drawer app come home" }, { "App icon size", "Dimensione icone app" }, + { "Appearance", "Aspetto" }, { "Applies after restart.", "Attivo dopo il riavvio." }, { "Apply", "Applica" }, { "Apply auto-poll", "Applica auto-poll" }, @@ -4857,6 +4879,7 @@ static const I18nPair kBuiltin_it[] = { { "Danger zone", "Zona pericolosa" }, { "Data -> SD card on reboot\n(card must be inserted)", "Dati -> scheda SD al riavvio\n(scheda richiesta)" }, { "Data -> internal on reboot", "Dati -> interno al riavvio" }, + { "Day", "Giorno" }, { "Delete", "Elimina" }, { "Delete %d contact(s)?", "Eliminare %d contatti?" }, { "Delete chat", "Elimina chat" }, @@ -5119,6 +5142,7 @@ static const I18nPair kBuiltin_it[] = { { "New game", "Nuova partita" }, { "New room msg", "Nuovo msg stanza" }, { "Next", "Avanti" }, + { "Night", "Notte" }, { "Night mode (invert)", "Modalità notte (inverti)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Nessun backup .json trovato.\nEsportane uno, o copia un\nmeshcore-backup.json sulla\nscheda SD o flash interna." }, { "No @mentions yet", "Ancora nessuna @menzione" }, @@ -5423,7 +5447,7 @@ static const I18nPair kBuiltin_it[] = { { "Test builds: off", "Build di test: off" }, { "Test builds: on", "Build di test: on" }, { "Theme", "Tema" }, - { "Theme colour", "Colore tema" }, + { "Theme save failed", "Salvataggio tema non riuscito" }, { "Then pick a mode:", "Poi scegli una modalità:" }, { "This contact hasn't shared\na GPS position.", "Questo contatto non ha condiviso\nuna posizione GPS." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Questo dispositivo può usare Wi-Fi e Bluetooth insieme, come radio autonoma e compagno del telefono (app MeshCore).\n\nUsarli entrambi consuma più RAM, quindi attiva solo ciò che serve.\n\nConfigurali quando vuoi in Impostazioni." }, @@ -5565,6 +5589,7 @@ static const I18nPair kBuiltin_ru[] = { { "AUTOMATIC ADVERTS", "АВТОМАТИЧЕСКИЕ АДВЕРТЫ" }, { "About", "О программе" }, { "About the map", "О карте" }, + { "Accent colour", "Акцентный цвет" }, { "Accent popups", "Подсказки акцентов" }, { "Accent popups: off", "Подсказки акцентов: выкл" }, { "Accent popups: on", "Подсказки акцентов: вкл" }, @@ -5592,6 +5617,7 @@ static const I18nPair kBuiltin_ru[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Любой может присоединиться. Ключ из хэштега (нижний регистр)." }, { "App drawer as home", "Меню приложений как главный экран" }, { "App icon size", "Размер значков приложений" }, + { "Appearance", "Оформление" }, { "Applies after restart.", "Применяется после перезапуска." }, { "Apply", "Применить" }, { "Apply auto-poll", "Применить автоопрос" }, @@ -5746,6 +5772,7 @@ static const I18nPair kBuiltin_ru[] = { { "Danger zone", "Опасная зона" }, { "Data -> SD card on reboot\n(card must be inserted)", "Данные -> SD-карта при перезагрузке\n(нужна карта)" }, { "Data -> internal on reboot", "Данные -> внутр. при перезагрузке" }, + { "Day", "День" }, { "Delete", "Удалить" }, { "Delete %d contact(s)?", "Удалить %d контакт(ов)?" }, { "Delete chat", "Удалить чат" }, @@ -6013,6 +6040,7 @@ static const I18nPair kBuiltin_ru[] = { { "New game", "Новая игра" }, { "New room msg", "Новое сообщ. комнаты" }, { "Next", "Далее" }, + { "Night", "Ночь" }, { "Night mode (invert)", "Ночной режим (инверсия)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Резервные копии .json не найдены.\nЭкспортируйте одну или скопируйте\nmeshcore-backup.json на\nSD-карту или внутр. флеш." }, { "No @mentions yet", "Пока нет @упоминаний" }, @@ -6320,7 +6348,7 @@ static const I18nPair kBuiltin_ru[] = { { "Test builds: off", "Тестовые сборки: выкл" }, { "Test builds: on", "Тестовые сборки: вкл" }, { "Theme", "Тема" }, - { "Theme colour", "Цвет темы" }, + { "Theme save failed", "Не удалось сохранить тему" }, { "Then pick a mode:", "Затем выберите режим:" }, { "This contact hasn't shared\na GPS position.", "Этот контакт не делился\nGPS-позицией." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Это устройство может использовать Wi-Fi и Bluetooth одновременно — как автономную рацию и компаньон телефона (приложение MeshCore).\n\nОба сразу используют больше ОЗУ, включайте только нужное.\n\nНастройте их в любой момент в Настройках." }, @@ -6465,6 +6493,7 @@ static const I18nPair kBuiltin_uk[] = { { "AUTOMATIC ADVERTS", "АВТОМАТИЧНІ АДВЕРТИ" }, { "About", "Про програму" }, { "About the map", "Про карту" }, + { "Accent colour", "Акцентний колір" }, { "Accent popups", "Підказки акцентів" }, { "Accent popups: off", "Підказки акцентів: вимк" }, { "Accent popups: on", "Підказки акцентів: увімк" }, @@ -6492,6 +6521,7 @@ static const I18nPair kBuiltin_uk[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Будь-хто може приєднатися. Ключ із хештегу (нижній регістр)." }, { "App drawer as home", "Меню додатків як головний екран" }, { "App icon size", "Розмір значків додатків" }, + { "Appearance", "Вигляд" }, { "Applies after restart.", "Діє після перезапуску." }, { "Apply", "Застосувати" }, { "Apply auto-poll", "Застосувати автоопитування" }, @@ -6646,6 +6676,7 @@ static const I18nPair kBuiltin_uk[] = { { "Danger zone", "Небезпечна зона" }, { "Data -> SD card on reboot\n(card must be inserted)", "Дані -> SD-картка при перезавантаженні\n(потрібна картка)" }, { "Data -> internal on reboot", "Дані -> внутр. при перезавантаженні" }, + { "Day", "День" }, { "Delete", "Видалити" }, { "Delete %d contact(s)?", "Видалити %d контакт(ів)?" }, { "Delete chat", "Видалити чат" }, @@ -6913,6 +6944,7 @@ static const I18nPair kBuiltin_uk[] = { { "New game", "Нова гра" }, { "New room msg", "Нове повід. кімнати" }, { "Next", "Далі" }, + { "Night", "Ніч" }, { "Night mode (invert)", "Нічний режим (інверсія)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Резервних копій .json не знайдено.\nЕкспортуйте одну або скопіюйте\nmeshcore-backup.json на\nSD-картку чи внутр. флеш." }, { "No @mentions yet", "Поки що немає @згадок" }, @@ -7220,7 +7252,7 @@ static const I18nPair kBuiltin_uk[] = { { "Test builds: off", "Тестові збірки: вимк" }, { "Test builds: on", "Тестові збірки: увімк" }, { "Theme", "Тема" }, - { "Theme colour", "Колір теми" }, + { "Theme save failed", "Не вдалося зберегти тему" }, { "Then pick a mode:", "Потім виберіть режим:" }, { "This contact hasn't shared\na GPS position.", "Цей контакт не ділився\nGPS-позицією." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Цей пристрій може використовувати Wi-Fi і Bluetooth одночасно — як автономну рацію та компаньйон телефона (застосунок MeshCore).\n\nОбидва разом використовують більше ОЗП, вмикайте лише потрібне.\n\nНалаштуйте їх будь-коли в Налаштуваннях." }, @@ -7364,6 +7396,7 @@ static const I18nPair kBuiltin_bg[] = { { "AUTOMATIC ADVERTS", "АВТОМАТИЧНИ АДВЕРТИ" }, { "About", "Относно" }, { "About the map", "Относно картата" }, + { "Accent colour", "Акцентен цвят" }, { "Accent popups", "Изскачащи акценти" }, { "Accent popups: off", "Изскачащи акценти: изкл" }, { "Accent popups: on", "Изскачащи акценти: вкл" }, @@ -7390,6 +7423,7 @@ static const I18nPair kBuiltin_bg[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Всеки може да се присъедини. Ключът е от хаштага (малки букви)." }, { "App drawer as home", "Меню с приложения като начален екран" }, { "App icon size", "Размер на иконите на приложенията" }, + { "Appearance", "Изглед" }, { "Applies after restart.", "Прилага се след рестарт." }, { "Apply", "Приложи" }, { "Apply auto-poll", "Приложи авто-запитване" }, @@ -7544,6 +7578,7 @@ static const I18nPair kBuiltin_bg[] = { { "Danger zone", "Опасна зона" }, { "Data -> SD card on reboot\n(card must be inserted)", "Данни -> SD карта при рестарт\n(нужна е карта)" }, { "Data -> internal on reboot", "Данни -> вътрешна при рестарт" }, + { "Day", "Ден" }, { "Delete", "Изтрий" }, { "Delete %d contact(s)?", "Изтриване на %d контакт(а)?" }, { "Delete chat", "Изтрий чата" }, @@ -7811,6 +7846,7 @@ static const I18nPair kBuiltin_bg[] = { { "New game", "Нова игра" }, { "New room msg", "Ново съобщ. в стая" }, { "Next", "Напред" }, + { "Night", "Нощ" }, { "Night mode (invert)", "Нощен режим (инвертиране)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Няма намерени .json архиви.\nЕкспортирайте един или копирайте\nmeshcore-backup.json на\nSD картата или вътр. флаш." }, { "No @mentions yet", "Още няма @споменавания" }, @@ -8118,7 +8154,7 @@ static const I18nPair kBuiltin_bg[] = { { "Test builds: off", "Тестови компилации: изкл" }, { "Test builds: on", "Тестови компилации: вкл" }, { "Theme", "Тема" }, - { "Theme colour", "Цвят на темата" }, + { "Theme save failed", "Неуспешно запазване на темата" }, { "Then pick a mode:", "После избери режим:" }, { "This contact hasn't shared\na GPS position.", "Този контакт не е споделил\nGPS позиция." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Това устройство може да ползва Wi-Fi и Bluetooth едновременно — като самостоятелно радио и телефонен компаньон (приложение MeshCore).\n\nДвете заедно ползват повече RAM, затова включвай само нужното.\n\nНастрой ги по всяко време в Настройки." }, @@ -8262,6 +8298,7 @@ static const I18nPair kBuiltin_sr[] = { { "AUTOMATIC ADVERTS", "АУТОМАТСКИ АДВЕРТИ" }, { "About", "О програму" }, { "About the map", "О мапи" }, + { "Accent colour", "Акцентна боја" }, { "Accent popups", "Искачући акценти" }, { "Accent popups: off", "Искачући акценти: искљ" }, { "Accent popups: on", "Искачући акценти: укљ" }, @@ -8288,6 +8325,7 @@ static const I18nPair kBuiltin_sr[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Свако може да се придружи. Кључ из хаштага (мала слова)." }, { "App drawer as home", "Фиока апликација као почетни екран" }, { "App icon size", "Величина икона апликација" }, + { "Appearance", "Изглед" }, { "Applies after restart.", "Важи након рестарта." }, { "Apply", "Примени" }, { "Apply auto-poll", "Примени ауто-упит" }, @@ -8442,6 +8480,7 @@ static const I18nPair kBuiltin_sr[] = { { "Danger zone", "Опасна зона" }, { "Data -> SD card on reboot\n(card must be inserted)", "Подаци -> SD картица при рестарту\n(потребна картица)" }, { "Data -> internal on reboot", "Подаци -> интерно при рестарту" }, + { "Day", "Дан" }, { "Delete", "Обриши" }, { "Delete %d contact(s)?", "Обрисати %d контакт(а)?" }, { "Delete chat", "Обриши ћаскање" }, @@ -8709,6 +8748,7 @@ static const I18nPair kBuiltin_sr[] = { { "New game", "Нова игра" }, { "New room msg", "Нова порука собе" }, { "Next", "Даље" }, + { "Night", "Ноћ" }, { "Night mode (invert)", "Ноћни режим (инвертуј)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Нема .json резервних копија.\nИзвезите једну или копирајте\nmeshcore-backup.json на\nSD картицу или интерни флеш." }, { "No @mentions yet", "Још нема @помена" }, @@ -9016,7 +9056,7 @@ static const I18nPair kBuiltin_sr[] = { { "Test builds: off", "Тест верзије: искључено" }, { "Test builds: on", "Тест верзије: укључено" }, { "Theme", "Тема" }, - { "Theme colour", "Боја теме" }, + { "Theme save failed", "Чување теме није успело" }, { "Then pick a mode:", "Затим изабери режим:" }, { "This contact hasn't shared\na GPS position.", "Овај контакт није поделио\nGPS позицију." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Овај уређај може користити Wi-Fi и Bluetooth истовремено — као самостални радио и телефонски пратилац (MeshCore апликација).\n\nОба заједно троше више RAM-а, зато укључи само оно што ти треба.\n\nПодеси их било кад у Подешавањима." }, @@ -9160,6 +9200,7 @@ static const I18nPair kBuiltin_el[] = { { "AUTOMATIC ADVERTS", "ΑΥΤΟΜΑΤΑ ADVERTS" }, { "About", "Σχετικά" }, { "About the map", "Σχετικά με τον χάρτη" }, + { "Accent colour", "Χρώμα έμφασης" }, { "Accent popups", "Αναδυόμενα τόνων" }, { "Accent popups: off", "Αναδυόμενα τόνων: όχι" }, { "Accent popups: on", "Αναδυόμενα τόνων: ναι" }, @@ -9186,6 +9227,7 @@ static const I18nPair kBuiltin_el[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Όλοι μπορούν να συμμετάσχουν. Κλειδί από το hashtag (πεζά)." }, { "App drawer as home", "Συρτάρι εφαρμογών ως αρχική" }, { "App icon size", "Μέγεθος εικονιδίων εφαρμογών" }, + { "Appearance", "Εμφάνιση" }, { "Applies after restart.", "Ισχύει μετά την επανεκκίνηση." }, { "Apply", "Εφαρμογή" }, { "Apply auto-poll", "Εφαρμογή auto-poll" }, @@ -9340,6 +9382,7 @@ static const I18nPair kBuiltin_el[] = { { "Danger zone", "Ζώνη κινδύνου" }, { "Data -> SD card on reboot\n(card must be inserted)", "Δεδομένα -> κάρτα SD στην επανεκκίνηση\n(απαιτείται κάρτα)" }, { "Data -> internal on reboot", "Δεδομένα -> εσωτερικά στην επανεκκίνηση" }, + { "Day", "Ημέρα" }, { "Delete", "Διαγραφή" }, { "Delete %d contact(s)?", "Διαγραφή %d επαφών;" }, { "Delete chat", "Διαγραφή συνομιλίας" }, @@ -9605,6 +9648,7 @@ static const I18nPair kBuiltin_el[] = { { "New game", "Νέο παιχνίδι" }, { "New room msg", "Νέο μήνυμα δωματίου" }, { "Next", "Επόμενο" }, + { "Night", "Νύχτα" }, { "Night mode (invert)", "Νυχτερινή λειτουργία (αντιστροφή)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Δεν βρέθηκαν αντίγραφα .json.\nΕξαγάγετε ένα ή αντιγράψτε\nmeshcore-backup.json στην\nκάρτα SD ή στο εσωτ. flash." }, { "No @mentions yet", "Καμία @αναφορά ακόμη" }, @@ -9911,7 +9955,7 @@ static const I18nPair kBuiltin_el[] = { { "Test builds: off", "Δοκιμαστικά builds: off" }, { "Test builds: on", "Δοκιμαστικά builds: on" }, { "Theme", "Θέμα" }, - { "Theme colour", "Χρώμα θέματος" }, + { "Theme save failed", "Αποτυχία αποθήκευσης θέματος" }, { "Then pick a mode:", "Μετά επιλέξτε λειτουργία:" }, { "This contact hasn't shared\na GPS position.", "Αυτή η επαφή δεν μοιράστηκε\nθέση GPS." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Αυτή η συσκευή μπορεί να χρησιμοποιεί Wi-Fi και Bluetooth ταυτόχρονα, ως αυτόνομος πομποδέκτης και συνοδός τηλεφώνου (εφαρμογή MeshCore).\n\nΚαι τα δύο μαζί χρησιμοποιούν περισσότερη RAM, οπότε ενεργοποιήστε μόνο ό,τι χρειάζεστε.\n\nΡυθμίστε τα οποτεδήποτε στις Ρυθμίσεις." }, @@ -10054,6 +10098,7 @@ static const I18nPair kBuiltin_pt_br[] = { { "AUTOMATIC ADVERTS", "ADVERTS AUTOMÁTICOS" }, { "About", "Sobre" }, { "About the map", "Sobre o mapa" }, + { "Accent colour", "Cor de destaque" }, { "Accent popups", "Pop-ups de acento" }, { "Accent popups: off", "Pop-ups de acento: desligados" }, { "Accent popups: on", "Pop-ups de acento: ligados" }, @@ -10079,6 +10124,7 @@ static const I18nPair kBuiltin_pt_br[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Qualquer um pode entrar. A chave vem da hashtag (minúsculas)." }, { "App drawer as home", "Gaveta de apps como tela inicial" }, { "App icon size", "Tamanho dos ícones de apps" }, + { "Appearance", "Aparência" }, { "Applies after restart.", "Aplica-se após reiniciar." }, { "Apply", "Aplicar" }, { "Apply auto-poll", "Aplicar auto-poll" }, @@ -10229,6 +10275,7 @@ static const I18nPair kBuiltin_pt_br[] = { { "Danger zone", "Zona de perigo" }, { "Data -> SD card on reboot\n(card must be inserted)", "Dados -> cartão SD ao reiniciar\n(cartão deve estar inserido)" }, { "Data -> internal on reboot", "Dados -> interno ao reiniciar" }, + { "Day", "Dia" }, { "Delete", "Excluir" }, { "Delete %d contact(s)?", "Excluir %d contato(s)?" }, { "Delete chat", "Excluir conversa" }, @@ -10489,6 +10536,7 @@ static const I18nPair kBuiltin_pt_br[] = { { "New game", "Novo jogo" }, { "New room msg", "Nova msg de sala" }, { "Next", "Avançar" }, + { "Night", "Noite" }, { "Night mode (invert)", "Modo noturno (inverter)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Nenhum backup .json encontrado.\nExporte um primeiro, ou copie um\nmeshcore-backup.json para o\ncartão SD ou a flash interna." }, { "No @mentions yet", "Ainda sem @menções" }, @@ -10793,7 +10841,7 @@ static const I18nPair kBuiltin_pt_br[] = { { "Test builds: off", "Builds de teste: off" }, { "Test builds: on", "Builds de teste: on" }, { "Theme", "Tema" }, - { "Theme colour", "Cor do tema" }, + { "Theme save failed", "Falha ao salvar o tema" }, { "Then pick a mode:", "Depois escolha um modo:" }, { "This contact hasn't shared\na GPS position.", "Este contato não compartilhou\numa posição GPS." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Este aparelho pode usar Wi-Fi e Bluetooth ao mesmo tempo, como rádio autônomo e companheiro do celular (app MeshCore).\n\nUsar os dois juntos consome mais RAM, então ligue só o que precisar.\n\nConfigure quando quiser em Ajustes." }, @@ -10933,6 +10981,7 @@ static const I18nPair kBuiltin_ro[] = { { "AUTOMATIC ADVERTS", "ADVERTURI AUTOMATE" }, { "About", "Despre" }, { "About the map", "Despre hartă" }, + { "Accent colour", "Culoare de accent" }, { "Accent popups", "Popupuri cu accente" }, { "Accent popups: off", "Popupuri accente: oprite" }, { "Accent popups: on", "Popupuri accente: pornite" }, @@ -10958,6 +11007,7 @@ static const I18nPair kBuiltin_ro[] = { { "Anyone can join. Key is derived from the hashtag (lowercase).", "Oricine poate intra. Cheia derivă din hashtag (litere mici)." }, { "App drawer as home", "Sertarul de aplicaţii ca ecran principal" }, { "App icon size", "Dimensiune pictograme aplicaţii" }, + { "Appearance", "Aspect" }, { "Applies after restart.", "Se aplică după repornire." }, { "Apply", "Aplică" }, { "Apply auto-poll", "Aplică auto-interogarea" }, @@ -11111,6 +11161,7 @@ static const I18nPair kBuiltin_ro[] = { { "Danger zone", "Zonă periculoasă" }, { "Data -> SD card on reboot\n(card must be inserted)", "Date -> card SD la repornire\n(cardul trebuie introdus)" }, { "Data -> internal on reboot", "Date -> stocare internă la repornire" }, + { "Day", "Zi" }, { "Delete", "Şterge" }, { "Delete %d contact(s)?", "Ştergi %d contact(e)?" }, { "Delete chat", "Şterge conversaţia" }, @@ -11371,6 +11422,7 @@ static const I18nPair kBuiltin_ro[] = { { "New game", "Joc nou" }, { "New room msg", "Mesaj nou în cameră" }, { "Next", "Înainte" }, + { "Night", "Noapte" }, { "Night mode (invert)", "Mod nocturn (inversare)" }, { "No .json backups found.\nExport one first, or copy a\nmeshcore-backup.json to the\nSD card or internal flash.", "Niciun backup .json găsit.\nExportă unul mai întâi sau copiază\nun meshcore-backup.json pe cardul\nSD ori în flash-ul intern." }, { "No @mentions yet", "Nicio menţiune @ încă" }, @@ -11674,7 +11726,7 @@ static const I18nPair kBuiltin_ro[] = { { "Test builds: off", "Versiuni de test: oprite" }, { "Test builds: on", "Versiuni de test: pornite" }, { "Theme", "Temă" }, - { "Theme colour", "Culoare temă" }, + { "Theme save failed", "Salvarea temei a eșuat" }, { "Then pick a mode:", "Apoi alege un mod:" }, { "This contact hasn't shared\na GPS position.", "Acest contact nu a partajat\no poziţie GPS." }, { "This device can run Wi-Fi and Bluetooth at once, as a standalone radio and a phone companion (MeshCore app) together.\n\nRunning both at the same time uses more RAM, so turn on only what you need.\n\nSet them up anytime in Settings.", "Acest dispozitiv poate rula Wi-Fi şi Bluetooth simultan, ca radio de sine stătător şi companion de telefon (aplicaţia MeshCore) în acelaşi timp.\n\nRularea ambelor foloseşte mai multă memorie RAM, deci porneşte doar ce îţi trebuie.\n\nLe poţi configura oricând în Setări." }, diff --git a/test/test_touch_prefs_schema.cpp b/test/test_touch_prefs_schema.cpp index c04899e..5918b5e 100644 --- a/test/test_touch_prefs_schema.cpp +++ b/test/test_touch_prefs_schema.cpp @@ -22,6 +22,7 @@ static Config safeDefaults() { c.hist_per_chat = 250; c.p4_antenna = 0; c.retry_echo = 1; + c.theme_mode = 0; return c; } @@ -29,7 +30,9 @@ int main() { constexpr size_t v43_size = offsetof(Config, retry_echo); constexpr size_t suffix_offset = offsetof(Config, web_mirror); constexpr size_t v43_suffix_size = v43_size - suffix_offset; - static_assert(v43_size + 1 == sizeof(Config), "v45 must append exactly one byte"); + static_assert(offsetof(Config, retry_echo) == + offsetof(Config, p4_antenna) + sizeof(Config::p4_antenna), + "v45 retry field moved"); // A v43 blob has the current layout minus the newly appended retry byte. // Every established field must survive byte-for-byte and retry defaults on. @@ -85,16 +88,37 @@ int main() { reinterpret_cast(&defaults) + suffix_offset, sizeof(Config) - suffix_offset) == 0); // ambiguous suffix fails safe - // Once rewritten as v45, the whole structure is authoritative, including an - // explicit retry opt-out and a deliberately enabled Remote Mode. + // Once rewritten as v45, every field through retry_echo is authoritative; + // fields appended by later versions retain their safe defaults. + constexpr size_t v45_size = offsetof(Config, app_hide); Config v45 = safeDefaults(); + v45.ver = 45; v45.remote_mode = 1; v45.remote_landscape = 0; v45.retry_echo = 0; migrated = safeDefaults(); - assert(TouchPrefsSchema::overlayStored(migrated, &v45, sizeof(v45), &stored_version)); + assert(TouchPrefsSchema::overlayStored(migrated, &v45, v45_size, &stored_version)); assert(stored_version == 45); - assert(memcmp(&migrated, &v45, sizeof(v45)) == 0); + assert(migrated.remote_mode == 1); + assert(migrated.remote_landscape == 0); + assert(migrated.retry_echo == 0); + assert(migrated.app_hide == safeDefaults().app_hide); + assert(migrated.theme_mode == 0); + + // v54 appends only theme_mode. A v53 blob preserves every older field and + // leaves the new byte at the caller-provided Night default. + constexpr size_t v53_size = offsetof(Config, theme_mode); + static_assert(v53_size + sizeof(Config::theme_mode) == sizeof(Config), + "v54 theme field must remain last"); + Config v53 = safeDefaults(); + v53.ver = 53; + v53.kb_force_legacy = 1; + v53.theme_mode = 1; // outside the stored v53 extent + migrated = safeDefaults(); + assert(TouchPrefsSchema::overlayStored(migrated, &v53, v53_size, &stored_version)); + assert(stored_version == 53); + assert(migrated.kb_force_legacy == 1); + assert(migrated.theme_mode == 0); Config invalid = safeDefaults(); uint8_t garbage[sizeof(Config)] = {};