From 2b07fd036fe9fef534beaad2768f71b1af31c699 Mon Sep 17 00:00:00 2001 From: Eiswolf-BG <37225519+Eiswolf-BG@users.noreply.github.com> Date: Wed, 5 Aug 2026 01:07:08 +0200 Subject: [PATCH] v2.2: Custom Unicode font for accented characters (DE/TR/FR/ES/IT), 4-category menu reorganization, black/green UI theme, header layout improvements --- src/airline_filter_screen.cpp | 31 +-- src/calibration_screen.cpp | 4 +- src/i18n.h | 23 +- src/i18n_de.h | 61 +++-- src/i18n_en.h | 9 + src/i18n_es.h | 83 +++--- src/i18n_fr.h | 105 ++++---- src/i18n_it.h | 19 +- src/i18n_tr.h | 161 +++++------ src/language_screen.cpp | 13 +- src/location_presets_screen.cpp | 37 ++- src/logbook_files_screen.cpp | 18 +- src/main.cpp | 68 +++-- src/menu_screen.cpp | 283 ++++++++++++-------- src/radar_screen.cpp | 118 +++++--- src/stats_screen.cpp | 21 +- src/ui_font.h | 461 ++++++++++++++++++++++++++++++++ src/units_screen.cpp | 13 +- src/wifi_manage_screen.cpp | 26 +- src/wifi_setup_screen.cpp | 39 ++- 20 files changed, 1115 insertions(+), 478 deletions(-) create mode 100644 src/ui_font.h diff --git a/src/airline_filter_screen.cpp b/src/airline_filter_screen.cpp index 0d20942..59e9e77 100644 --- a/src/airline_filter_screen.cpp +++ b/src/airline_filter_screen.cpp @@ -14,11 +14,12 @@ namespace { } }; - void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { - tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); - tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool danger = false) { + uint16_t accent = danger ? TFT_RED : TFT_GREEN; + tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK); + tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, bg); + tft.setTextColor(accent, TFT_BLACK); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.setTextDatum(TL_DATUM); } @@ -59,14 +60,14 @@ namespace { auto redraw = [&]() { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 10); tft.println(I18n::t(StringId::AIRLINE_ADD_TITLE)); - tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 30, TFT_NAVY); - tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 30, TFT_DARKGREY); + tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 30, TFT_BLACK); + tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 30, TFT_GREEN); tft.setTextSize(2); - tft.setTextColor(TFT_WHITE, TFT_NAVY); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(14, 47); tft.print(buf); tft.setTextSize(1); @@ -76,7 +77,7 @@ namespace { for (uint8_t i = 0; i < 7; i++) drawButton(tft, row3Rects[i], String(ROW3[i])); drawButton(tft, backspaceBtn, "<-"); - drawButton(tft, cancelBtn, I18n::t(StringId::CANCEL), TFT_MAROON); + drawButton(tft, cancelBtn, I18n::t(StringId::CANCEL), true); drawButton(tft, confirmBtn, I18n::t(StringId::ADD)); }; @@ -126,10 +127,10 @@ void run(TFT_eSPI& tft) { bool done = false; while (!done) { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 8); tft.println(I18n::t(StringId::AIRLINE_FILTER_TITLE)); - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 26); tft.println(I18n::t(StringId::AIRLINE_FILTER_DESC1)); tft.setCursor(10, 38); @@ -147,13 +148,13 @@ void run(TFT_eSPI& tft) { rowRects[i] = rowRect; removeRects[i] = removeRect; - tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_NAVY); - tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREY); + tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_BLACK); + tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_GREEN); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, TFT_NAVY); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.drawString(AirlineFilter::icaoAt(i), rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2); tft.setTextDatum(TL_DATUM); - drawButton(tft, removeRect, "X", TFT_MAROON); + drawButton(tft, removeRect, "X", true); y += ROW_H + ROW_GAP; } diff --git a/src/calibration_screen.cpp b/src/calibration_screen.cpp index f6a9e66..6ae0c4e 100644 --- a/src/calibration_screen.cpp +++ b/src/calibration_screen.cpp @@ -64,7 +64,7 @@ void run(TFT_eSPI& tft) { for (uint8_t i = 0; i < 4; i++) { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setTextSize(1); tft.setTextDatum(MC_DATUM); tft.drawString(I18n::t(StringId::CALIB_TITLE), W / 2, H / 2 - 12); @@ -88,7 +88,7 @@ void run(TFT_eSPI& tft) { TouchInput::saveCalibration(); tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 10); tft.println(I18n::t(StringId::CALIB_SAVED)); delay(800); diff --git a/src/i18n.h b/src/i18n.h index 22099ee..13c693f 100644 --- a/src/i18n.h +++ b/src/i18n.h @@ -1,12 +1,6 @@ #pragma once #include -// Zentrales Uebersetzungssystem: jeder feste UI-Text der App hat eine -// StringId. Die eigentlichen Uebersetzungen liegen in je einer Datei pro -// Sprache (i18n_en.h, i18n_de.h, i18n_fr.h, i18n_tr.h, i18n_es.h, i18n_it.h) - -// jede Datei enthaelt ein Array in GENAU derselben Reihenfolge wie dieses -// Enum. Neue Menuepunkte/Texte: hier eine neue StringId VOR COUNT einfuegen, -// dann in ALLEN 6 Sprachdateien an derselben Position ergaenzen. enum class StringId : uint8_t { OK = 0, CANCEL, @@ -139,17 +133,20 @@ enum class StringId : uint8_t { LANGUAGE_TITLE, - COUNT // IMMER als letztes Element - markiert die Gesamtanzahl + MENU_CATEGORY_REGION, + MENU_CATEGORY_WIFI, + MENU_CATEGORY_SYSTEM, + MENU_CATEGORY_FLIGHT, + + WIFI_NETWORKS_TITLE, + WIFI_ADD_NETWORK, + WIFI_EMPTY_SLOT, + + COUNT }; namespace I18n { constexpr uint8_t LANG_COUNT = 6; - - // Liefert den uebersetzten Text fuer 'id' in der aktuell in den - // Einstellungen gewaehlten Sprache (SettingsStore::language()). const char* t(StringId id); - - // Eigennamen der Sprachen (fuer das Sprachauswahl-Menue), IMMER im - // jeweiligen Idiom selbst geschrieben (z.B. "Deutsch", nicht "German"). const char* languageName(uint8_t index); } \ No newline at end of file diff --git a/src/i18n_de.h b/src/i18n_de.h index 412d44e..60e5775 100644 --- a/src/i18n_de.h +++ b/src/i18n_de.h @@ -4,10 +4,10 @@ static const char* const I18N_DE[] = { "OK", "Abbrechen", - "Zurueck", - "< Zurueck", - "Laedt...", - "Hinzufuegen", + "Zurück", + "< Zurück", + "Lädt...", + "Hinzufügen", "AN", "AUS", @@ -20,13 +20,13 @@ static const char* const I18N_DE[] = { "Ermittle Standort...", "Bereit!", - "Fuer diese App wird", + "Für diese App wird", "eine SD-Karte", - "benoetigt", - "SD-Karte einlegen und Geraet neu starten", + "benötigt", + "SD-Karte einlegen und Gerät neu starten", "Touch-Kalibrierung", - "Bitte den Kreis beruehren:", + "Bitte den Kreis berühren:", "Oben links", "Oben rechts", "Unten rechts", @@ -35,14 +35,14 @@ static const char* const I18N_DE[] = { "Suche WLAN...", "Keine Netzwerke gefunden", - "WLAN waehlen:", + "WLAN wählen:", "Verbunden!", "Verbindung fehlgeschlagen.", - "Zurueck zur Netzwerkliste...", + "Zurück zur Netzwerkliste...", "Passwort:", "Leerzeichen", "Verbinden", - "Zurueck zur Liste", + "Zurück zur Liste", "Bereits 3 Netzwerke gespeichert.", "Erst eins entfernen.", "Verbinde...", @@ -60,7 +60,7 @@ static const char* const I18N_DE[] = { "Logbuch-Dateien", "Weitere Einstellungen >", "Notfall-Alarm: ", - "Naeherungs-LED: ", + "Näherungs-LED: ", "LED-Herzschlag: ", "Flugbuch: ", "Bodenfahrzeuge ausblenden: ", @@ -69,7 +69,7 @@ static const char* const I18N_DE[] = { "Einstellungen wiederherstellen", "Backup gespeichert.", "Backup fehlgeschlagen.", - "Wiederhergestellt. Geraet neu starten!", + "Wiederhergestellt. Gerät neu starten!", "Wiederherstellung fehlgeschlagen.", "Sprache", "Einheiten", @@ -77,46 +77,46 @@ static const char* const I18N_DE[] = { "Ausgeblendete Airlines", "Flugzeuge dieser Airlines werden", "im Radar ausgeblendet.", - "Airline hinzufuegen", - "Airline hinzufuegen (ICAO-Code)", + "Airline hinzufügen", + "Airline hinzufügen (ICAO-Code)", "Standort-Presets", "Automatisch (GPS/IP)", "Preset", "(leer)", - "Preset hinzufuegen", + "Preset hinzufügen", "Breitengrad (z.B. 48.6833)", - "Laengengrad (z.B. 9.8333)", + "Längengrad (z.B. 9.8333)", "Statistik", "Heute geloggte Flugzeuge", "Insgesamt geloggte Flugzeuge", "Tage mit Sichtungen", "Durchschnitt pro Tag", - "Flugbuch zuruecksetzen", - "Nochmal tippen zum Bestaetigen!", - "Flugbuch-Daten zurueckgesetzt.", + "Flugbuch zurücksetzen", + "Nochmal tippen zum Bestätigen!", + "Flugbuch-Daten zurückgesetzt.", "Laufzeit: ", "Logbuch-Dateien", - "Noch keine Flugbuch-Eintraege.", + "Noch keine Flugbuch-Einträge.", "Zeige die letzten ", " von ", " Tagen", " Flugzeuge", - "Fuer Details antippen", + "Für Details antippen", "Modell: ", "Typ: ", - "laedt...", + "lädt...", "unbekannt", - "Hoehe: ", + "Höhe: ", "Geschw.: ", "Distanz: ", "Kurs: ", - "Sitzplaetze (geschaetzt): ", - "Sitzplaetze: unbekannt", + "Sitzplätze (geschätzt): ", + "Sitzplätze: unbekannt", "Woanders antippen zum Schliessen", "Steigen: ", "Sinken: ", @@ -132,6 +132,15 @@ static const char* const I18N_DE[] = { "Imperial (ft, kt, nm)", "Sprache", + + "Land/Region", + "WLAN/Netzwerk", + "System", + "Flugoptionen", + + "WLAN-Netzwerke", + "Netzwerk hinzufügen", + "(leer)", }; static_assert(sizeof(I18N_DE) / sizeof(I18N_DE[0]) == (size_t)StringId::COUNT, diff --git a/src/i18n_en.h b/src/i18n_en.h index e3ae671..12c91a1 100644 --- a/src/i18n_en.h +++ b/src/i18n_en.h @@ -132,6 +132,15 @@ static const char* const I18N_EN[] = { "Imperial (ft, kt, nm)", "Language", + + "Region & Language", + "WiFi / Network", + "System", + "Flight Options", + + "WiFi networks", + "Add network", + "(empty)", }; static_assert(sizeof(I18N_EN) / sizeof(I18N_EN[0]) == (size_t)StringId::COUNT, diff --git a/src/i18n_es.h b/src/i18n_es.h index d312063..1d19371 100644 --- a/src/i18n_es.h +++ b/src/i18n_es.h @@ -4,10 +4,10 @@ static const char* const I18N_ES[] = { "OK", "Cancelar", - "Atras", - "< Atras", + "Atrás", + "< Atrás", "Cargando...", - "Anadir", + "Añadir", "ON", "OFF", @@ -16,30 +16,30 @@ static const char* const I18N_ES[] = { "Tarjeta SD: OK", "Conectando WiFi...", "WiFi OK: ", - "WiFi FALLO", - "Obteniendo ubicacion...", - "Listo!", + "WiFi FALLÓ", + "Obteniendo ubicación...", + "¡Listo!", "Esta app necesita", "una tarjeta SD", "requerida", "Inserte una tarjeta y reinicie el dispositivo", - "Calibracion tactil", - "Toque el circulo:", + "Calibración táctil", + "Toque el círculo:", "Arriba izquierda", "Arriba derecha", "Abajo derecha", "Abajo izquierda", - "Calibracion guardada!", + "¡Calibración guardada!", "Buscando WiFi...", "No se encontraron redes", "Elegir WiFi:", - "Conectado!", - "Conexion fallida.", + "¡Conectado!", + "Conexión fallida.", "Volviendo a la lista...", - "Contrasena:", + "Contraseña:", "Espacio", "Conectar", "Volver a la lista", @@ -50,59 +50,59 @@ static const char* const I18N_ES[] = { "Ajustes", "Ajustes (2/2)", - "Calibrar tactil", + "Calibrar táctil", "Pantalla: invertida (toque)", "Pantalla: normal (toque)", "Gestionar redes WiFi", "Ubicaciones guardadas", "Apagado de pantalla: ", - "Estadisticas", + "Estadísticas", "Archivos del registro", - "Mas ajustes >", + "Más ajustes >", "Alerta de emergencia: ", "LED de proximidad: ", "Latido LED: ", "Registro de vuelos: ", - "Ocultar vehiculos terrestres: ", - "Filtro de aerolineas", + "Ocultar vehículos terrestres: ", + "Filtro de aerolíneas", "Copia de seguridad", "Restaurar ajustes", "Copia guardada.", - "Fallo la copia de seguridad.", - "Restaurado. Reinicie el dispositivo!", - "Fallo la restauracion.", + "Falló la copia de seguridad.", + "¡Restaurado. Reinicie el dispositivo!", + "Falló la restauración.", "Idioma", "Unidades", - "Aerolineas ocultas", - "Los aviones de estas aerolineas", + "Aerolíneas ocultas", + "Los aviones de estas aerolíneas", "se ocultan del radar.", - "Anadir aerolinea", - "Anadir aerolinea (codigo OACI)", + "Añadir aerolínea", + "Añadir aerolínea (código OACI)", "Ubicaciones guardadas", "Auto (GPS/IP)", - "Ubicacion", - "(vacio)", - "Anadir ubicacion", + "Ubicación", + "(vacío)", + "Añadir ubicación", "Latitud (ej. 48.6833)", "Longitud (ej. 9.8333)", - "Estadisticas", + "Estadísticas", "Aviones registrados hoy", "Aviones registrados en total", - "Dias con avistamientos", - "Promedio por dia", + "Días con avistamientos", + "Promedio por día", "Reiniciar registro", - "Toque de nuevo para confirmar!", + "¡Toque de nuevo para confirmar!", "Registro reiniciado.", "Tiempo activo: ", "Archivos del registro", - "Aun no hay registros.", - "Mostrando los ultimos ", + "Aún no hay registros.", + "Mostrando los últimos ", " de ", - " dias", + " días", " aviones", "Toque para ver detalles", @@ -124,14 +124,23 @@ static const char* const I18N_ES[] = { "Squawk: ", "Guardado: ", - "Fallo la captura de pantalla", + "Falló la captura de pantalla", "Unidades", - "Auto (segun ubicacion)", - "Metrico (km, m, km/h)", + "Auto (según ubicación)", + "Métrico (km, m, km/h)", "Imperial (ft, kt, nm)", "Idioma", + + "Región e idioma", + "WiFi / Red", + "Sistema", + "Opciones de vuelo", + + "Redes WiFi", + "Añadir red", + "(vacío)", }; static_assert(sizeof(I18N_ES) / sizeof(I18N_ES[0]) == (size_t)StringId::COUNT, diff --git a/src/i18n_fr.h b/src/i18n_fr.h index a32798f..5849eca 100644 --- a/src/i18n_fr.h +++ b/src/i18n_fr.h @@ -16,71 +16,71 @@ static const char* const I18N_FR[] = { "Carte SD: OK", "Connexion WiFi...", "WiFi OK: ", - "ECHEC WiFi", + "ÉCHEC WiFi", "Localisation...", - "Pret !", + "Prêt !", "Pour cette appli une", "carte SD est", "requise", - "Inserez une carte et redemarrez l'appareil", + "Insérez une carte et redémarrez l'appareil", - "Etalonnage tactile", + "Étalonnage tactile", "Touchez le cercle :", "Haut gauche", "Haut droite", "Bas droite", "Bas gauche", - "Etalonnage enregistre !", + "Étalonnage enregistré !", "Recherche WiFi...", - "Aucun reseau trouve", + "Aucun réseau trouvé", "Choisir WiFi :", - "Connecte !", - "Echec de connexion.", - "Retour a la liste...", + "Connecté !", + "Échec de connexion.", + "Retour à la liste...", "Mot de passe :", "Espace", "Connecter", - "Retour a la liste", - "Deja 3 reseaux enregistres.", + "Retour à la liste", + "Déjà 3 réseaux enregistrés.", "Supprimez-en un d'abord.", "Connexion...", "WiFi : ", - "Reglages", - "Reglages (2/2)", - "Etalonner le tactile", - "Ecran : inverse (touchez)", - "Ecran : normal (touchez)", - "Gerer les reseaux WiFi", - "Positions enregistrees", - "Extinction ecran : ", + "Réglages", + "Réglages (2/2)", + "Étalonner le tactile", + "Écran : inversé (touchez)", + "Écran : normal (touchez)", + "Gérer les réseaux WiFi", + "Positions enregistrées", + "Extinction écran : ", "Statistiques", "Fichiers du journal", - "Plus de reglages >", + "Plus de réglages >", "Alerte urgence : ", - "LED de proximite : ", + "LED de proximité : ", "Battement LED : ", "Journal de vol : ", - "Masquer vehicules au sol : ", + "Masquer véhicules au sol : ", "Filtre compagnies", - "Sauvegarder reglages", - "Restaurer reglages", - "Sauvegarde enregistree.", - "Echec de la sauvegarde.", - "Restaure. Redemarrez l'appareil !", - "Echec de la restauration.", + "Sauvegarder réglages", + "Restaurer réglages", + "Sauvegarde enregistrée.", + "Échec de la sauvegarde.", + "Restauré. Redémarrez l'appareil !", + "Échec de la restauration.", "Langue", - "Unites", + "Unités", - "Compagnies masquees", + "Compagnies masquées", "Les avions de ces compagnies sont", - "masques du radar.", + "masqués du radar.", "Ajouter une compagnie", "Ajouter (code OACI)", - "Positions enregistrees", + "Positions enregistrées", "Auto (GPS/IP)", "Position", "(vide)", @@ -89,25 +89,25 @@ static const char* const I18N_FR[] = { "Longitude (ex. 9.8333)", "Statistiques", - "Avions enregistres aujourd'hui", - "Avions enregistres au total", + "Avions enregistrés aujourd'hui", + "Avions enregistrés au total", "Jours avec observations", "Moyenne par jour", - "Reinitialiser le journal", + "Réinitialiser le journal", "Retouchez pour confirmer !", - "Journal reinitialise.", - "Duree de fonctionnement : ", + "Journal réinitialisé.", + "Durée de fonctionnement : ", "Fichiers du journal", - "Aucune entree pour le moment.", + "Aucune entrée pour le moment.", "Affichage des ", " derniers sur ", " jours", " avions", - "Touchez pour les details", + "Touchez pour les détails", - "Modele : ", + "Modèle : ", "Type : ", "chargement...", "inconnu", @@ -115,23 +115,32 @@ static const char* const I18N_FR[] = { "Vitesse : ", "Dist. : ", "Cap : ", - "Sieges (estime) : ", - "Sieges : inconnu", + "Sièges (estimé) : ", + "Sièges : inconnu", "Touchez ailleurs pour fermer", - "Montee : ", + "Montée : ", "Descente : ", "Vol en palier", "Squawk : ", - "Enregistre : ", - "Echec de la capture d'ecran", + "Enregistré : ", + "Échec de la capture d'écran", - "Unites", + "Unités", "Auto (selon la position)", - "Metrique (km, m, km/h)", - "Imperial (ft, kt, nm)", + "Métrique (km, m, km/h)", + "Impérial (ft, kt, nm)", "Langue", + + "Région et langue", + "WiFi / Réseau", + "Système", + "Options de vol", + + "Réseaux WiFi", + "Ajouter un réseau", + "(vide)", }; static_assert(sizeof(I18N_FR) / sizeof(I18N_FR[0]) == (size_t)StringId::COUNT, diff --git a/src/i18n_it.h b/src/i18n_it.h index 8aedace..701cefc 100644 --- a/src/i18n_it.h +++ b/src/i18n_it.h @@ -43,7 +43,7 @@ static const char* const I18N_IT[] = { "Spazio", "Connetti", "Torna alla lista", - "Gia 3 reti salvate.", + "Già 3 reti salvate.", "Rimuovine una prima.", "Connessione...", "WiFi: ", @@ -60,7 +60,7 @@ static const char* const I18N_IT[] = { "File del registro", "Altre impostazioni >", "Allarme emergenza: ", - "LED di prossimita: ", + "LED di prossimità: ", "Battito LED: ", "Diario di volo: ", "Nascondi veicoli a terra: ", @@ -72,7 +72,7 @@ static const char* const I18N_IT[] = { "Ripristinato. Riavviare il dispositivo!", "Ripristino fallito.", "Lingua", - "Unita", + "Unità", "Compagnie nascoste", "Gli aerei di queste compagnie", @@ -96,7 +96,7 @@ static const char* const I18N_IT[] = { "Reimposta registro", "Tocca di nuovo per confermare!", "Registro reimpostato.", - "Tempo di attivita: ", + "Tempo di attività: ", "File del registro", "Ancora nessun dato.", @@ -126,12 +126,21 @@ static const char* const I18N_IT[] = { "Salvato: ", "Screenshot fallito", - "Unita", + "Unità", "Auto (in base alla posizione)", "Metrico (km, m, km/h)", "Imperiale (ft, kt, nm)", "Lingua", + + "Regione e lingua", + "WiFi / Rete", + "Sistema", + "Opzioni di volo", + + "Reti WiFi", + "Aggiungi rete", + "(vuoto)", }; static_assert(sizeof(I18N_IT) / sizeof(I18N_IT[0]) == (size_t)StringId::COUNT, diff --git a/src/i18n_tr.h b/src/i18n_tr.h index 69cdd25..cc04140 100644 --- a/src/i18n_tr.h +++ b/src/i18n_tr.h @@ -3,135 +3,144 @@ static const char* const I18N_TR[] = { "Tamam", - "Iptal", + "İptal", "Geri", "< Geri", - "Yukleniyor...", + "Yükleniyor...", "Ekle", - "ACIK", + "AÇIK", "KAPALI", "Asla", "SD Kart: OK", - "WiFi'ye baglaniyor...", + "WiFi'ye bağlanıyor...", "WiFi OK: ", - "WiFi BASARISIZ", - "Konum aliniyor...", - "Hazir!", + "WiFi BAŞARISIZ", + "Konum alınıyor...", + "Hazır!", - "Bu uygulama icin bir", + "Bu uygulama için bir", "SD kart", "gereklidir", - "Bir kart takin ve cihazi yeniden baslatin", + "Bir kart takın ve cihazı yeniden başlatın", "Dokunmatik Kalibrasyon", - "Lutfen daireye dokunun:", - "Sol ust", - "Sag ust", - "Sag alt", + "Lütfen daireye dokunun:", + "Sol üst", + "Sağ üst", + "Sağ alt", "Sol alt", "Kalibrasyon kaydedildi!", - "WiFi taraniyor...", - "Ag bulunamadi", - "WiFi secin:", - "Baglandi!", - "Baglanti basarisiz.", - "Ag listesine donuluyor...", - "Sifre:", - "Bosluk", - "Baglan", - "Listeye don", - "Zaten 3 ag kayitli.", - "Once birini kaldirin.", - "Baglaniyor...", + "WiFi taranıyor...", + "Ağ bulunamadı", + "WiFi seçin:", + "Bağlandı!", + "Bağlantı başarısız.", + "Ağ listesine dönülüyor...", + "Şifre:", + "Boşluk", + "Bağlan", + "Listeye dön", + "Zaten 3 ağ kayıtlı.", + "Önce birini kaldırın.", + "Bağlanıyor...", "WiFi: ", "Ayarlar", "Ayarlar (2/2)", - "Dokunmatigi kalibre et", + "Dokunmatiği kalibre et", "Ekran: ters (dokun)", "Ekran: normal (dokun)", - "WiFi aglarini yonet", - "Konum on ayarlari", - "Ekran zaman asimi: ", - "Istatistikler", - "Kayit defteri dosyalari", - "Diger ayarlar >", - "Acil durum alarmi: ", - "Yakinlik LED'i: ", - "LED kalp atisi: ", - "Ucus kayit defteri: ", - "Kara araclarini gizle: ", + "WiFi ağlarını yönet", + "Konum ön ayarları", + "Ekran zaman aşımı: ", + "İstatistikler", + "Kayıt defteri dosyaları", + "Diğer ayarlar >", + "Acil durum alarmı: ", + "Yakınlık LED'i: ", + "LED kalp atışı: ", + "Uçuş kayıt defteri: ", + "Kara araçlarını gizle: ", "Havayolu filtresi", - "Ayarlari yedekle", - "Ayarlari geri yukle", + "Ayarları yedekle", + "Ayarları geri yükle", "Yedekleme kaydedildi.", - "Yedekleme basarisiz.", - "Geri yuklendi. Cihazi yeniden baslatin!", - "Geri yukleme basarisiz.", + "Yedekleme başarısız.", + "Geri yüklendi. Cihazı yeniden başlatın!", + "Geri yükleme başarısız.", "Dil", "Birimler", - "Gizli havayollari", - "Bu havayollarina ait ucaklar", + "Gizli havayolları", + "Bu havayollarına ait uçaklar", "radarda gizlenir.", "Havayolu ekle", "Havayolu ekle (ICAO kodu)", - "Konum on ayarlari", + "Konum ön ayarları", "Otomatik (GPS/IP)", - "On ayar", - "(bos)", - "On ayar ekle", - "Enlem (orn. 48.6833)", - "Boylam (orn. 9.8333)", + "Ön ayar", + "(boş)", + "Ön ayar ekle", + "Enlem (örn. 48.6833)", + "Boylam (örn. 9.8333)", - "Istatistikler", - "Bugun kaydedilen ucaklar", - "Toplam kaydedilen ucaklar", - "Gozlem yapilan gunler", - "Gunluk ortalama", - "Kayit defterini sifirla", - "Onaylamak icin tekrar dokunun!", - "Kayit defteri sifirlandi.", - "Calisma suresi: ", + "İstatistikler", + "Bugün kaydedilen uçaklar", + "Toplam kaydedilen uçaklar", + "Gözlem yapılan günler", + "Günlük ortalama", + "Kayıt defterini sıfırla", + "Onaylamak için tekrar dokunun!", + "Kayıt defteri sıfırlandı.", + "Çalışma süresi: ", - "Kayit defteri dosyalari", - "Henuz kayit yok.", + "Kayıt defteri dosyaları", + "Henüz kayıt yok.", "Son ", " / ", - " gun gosteriliyor", - " ucak", + " gün gösteriliyor", + " uçak", - "Detaylar icin dokunun", + "Detaylar için dokunun", "Model: ", "Tip: ", - "yukleniyor...", + "yükleniyor...", "bilinmiyor", - "Yuk.: ", - "Hiz: ", + "Yük.: ", + "Hız: ", "Mesafe: ", - "Yon: ", + "Yön: ", "Koltuk (tahmini): ", "Koltuk: bilinmiyor", - "Kapatmak icin baska yere dokunun", - "Tirmanis: ", - "Alcalma: ", - "Duz ucus", + "Kapatmak için başka yere dokunun", + "Tırmanış: ", + "Alçalma: ", + "Düz uçuş", "Squawk: ", "Kaydedildi: ", - "Ekran goruntusu basarisiz", + "Ekran görüntüsü başarısız", "Birimler", - "Otomatik (konuma gore)", + "Otomatik (konuma göre)", "Metrik (km, m, km/sa)", - "Imperyal (ft, kt, nm)", + "İmperyal (ft, kt, nm)", "Dil", + + "Bölge ve Dil", + "WiFi / Ağ", + "Sistem", + "Uçuş Seçenekleri", + + "WiFi ağları", + "Ağ ekle", + "(boş)", }; static_assert(sizeof(I18N_TR) / sizeof(I18N_TR[0]) == (size_t)StringId::COUNT, diff --git a/src/language_screen.cpp b/src/language_screen.cpp index 71d8807..c16501c 100644 --- a/src/language_screen.cpp +++ b/src/language_screen.cpp @@ -14,11 +14,13 @@ namespace { } }; - void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { + void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool active = false) { + uint16_t bg = active ? TFT_GREEN : TFT_BLACK; + uint16_t fg = active ? TFT_BLACK : TFT_GREEN; tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); - tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, bg); + tft.setTextColor(fg, bg); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.setTextDatum(TL_DATUM); } @@ -37,7 +39,7 @@ void run(TFT_eSPI& tft) { bool done = false; while (!done) { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 6); tft.println(I18n::t(StringId::LANGUAGE_TITLE)); @@ -46,8 +48,7 @@ void run(TFT_eSPI& tft) { for (uint8_t i = 0; i < I18n::LANG_COUNT; i++) { langRects[i] = rowRect(i); - drawButton(tft, langRects[i], I18n::languageName(i), - (i == current) ? TFT_DARKGREEN : TFT_NAVY); + drawButton(tft, langRects[i], I18n::languageName(i), i == current); } Rect backBtn = rowRect(I18n::LANG_COUNT); diff --git a/src/location_presets_screen.cpp b/src/location_presets_screen.cpp index a0b89d5..c99f7ed 100644 --- a/src/location_presets_screen.cpp +++ b/src/location_presets_screen.cpp @@ -14,11 +14,15 @@ namespace { } }; - void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { + void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, + bool active = false, bool danger = false) { + uint16_t accent = danger ? TFT_RED : TFT_GREEN; + uint16_t bg = active ? accent : TFT_BLACK; + uint16_t fg = active ? TFT_BLACK : accent; tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); - tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, bg); + tft.setTextColor(fg, bg); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.setTextDatum(TL_DATUM); } @@ -43,10 +47,6 @@ namespace { KEY_W, KEY_H}; } - // WICHTIG: Backspace muss VOR den Cancel/OK-Buttons enden (mit - // Abstand), sonst ueberlappen sich die Tap-Flaechen - Backspace wird - // in der Pruef-Reihenfolge zuerst getestet, wuerde also faelschlich - // jeden Tap auf "Cancel" abfangen (Bug: Cancel-Button reagierte nicht). Rect backspaceBtn = {(int16_t)GRID_LEFT, (int16_t)(GRID_TOP + 4 * (KEY_H + KEY_GAP)), (int16_t)(3 * KEY_W + 2 * KEY_GAP), 30}; Rect cancelBtn = {10, (int16_t)(Config::SCREEN_HEIGHT - 50), 110, 40}; @@ -57,21 +57,21 @@ namespace { auto redraw = [&]() { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 10); tft.println(title); - tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_NAVY); - tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_DARKGREY); + tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_BLACK); + tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_GREEN); tft.setTextSize(2); - tft.setTextColor(TFT_WHITE, TFT_NAVY); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(14, 49); tft.print(buf); tft.setTextSize(1); for (uint8_t i = 0; i < 12; i++) drawButton(tft, keyRects[i], keys[i]); drawButton(tft, backspaceBtn, "<- Backspace"); - drawButton(tft, cancelBtn, I18n::t(StringId::CANCEL), TFT_MAROON); + drawButton(tft, cancelBtn, I18n::t(StringId::CANCEL), false, true); drawButton(tft, confirmBtn, I18n::t(StringId::OK)); }; @@ -133,7 +133,7 @@ void run(TFT_eSPI& tft) { bool done = false; while (!done) { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 8); tft.println(I18n::t(StringId::LOCATION_TITLE)); @@ -143,8 +143,7 @@ void run(TFT_eSPI& tft) { Rect autoRect = {10, y, (int16_t)(Config::SCREEN_WIDTH - 20), ROW_H}; String autoLabel = I18n::t(StringId::LOCATION_AUTO); - drawButton(tft, autoRect, active < 0 ? ("> " + autoLabel) : autoLabel, - active < 0 ? TFT_DARKGREEN : TFT_NAVY); + drawButton(tft, autoRect, active < 0 ? ("> " + autoLabel) : autoLabel, active < 0); y += ROW_H + ROW_GAP; Rect rowRects[LocationPresets::MAX_PRESETS]; @@ -163,13 +162,13 @@ void run(TFT_eSPI& tft) { char coords[24]; snprintf(coords, sizeof(coords), "%d: %.3f, %.3f", i + 1, lat, lon); String label = (active == (int8_t)i ? "> " : "") + presetWord + " " + coords; - drawButton(tft, rowRect, label, (active == (int8_t)i) ? TFT_DARKGREEN : TFT_NAVY); - drawButton(tft, removeRect, "X", TFT_MAROON); + drawButton(tft, rowRect, label, active == (int8_t)i); + drawButton(tft, removeRect, "X", false, true); } else { tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_BLACK); - tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREY); + tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREEN); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + tft.setTextColor(TFT_DARKGREEN, TFT_BLACK); String label = presetWord + " " + String(i + 1) + " " + I18n::t(StringId::LOCATION_PRESET_EMPTY); tft.drawString(label, rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2); tft.setTextDatum(TL_DATUM); diff --git a/src/logbook_files_screen.cpp b/src/logbook_files_screen.cpp index 573a2f2..8a06686 100644 --- a/src/logbook_files_screen.cpp +++ b/src/logbook_files_screen.cpp @@ -15,10 +15,10 @@ namespace { }; void drawButton(TFT_eSPI& tft, const Rect& r, const String& label) { - tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_NAVY); - tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK); + tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, TFT_NAVY); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.setTextDatum(TL_DATUM); } @@ -29,10 +29,10 @@ namespace { void run(TFT_eSPI& tft) { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 8); tft.println(I18n::t(StringId::LOGFILES_TITLE)); - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + tft.setTextColor(TFT_DARKGREEN, TFT_BLACK); tft.setCursor(10, 30); tft.print(I18n::t(StringId::LOADING)); @@ -42,12 +42,12 @@ void run(TFT_eSPI& tft) { Rect backBtn = {10, (int16_t)(Config::SCREEN_HEIGHT - 50), (int16_t)(Config::SCREEN_WIDTH - 20), 40}; tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 8); tft.println(I18n::t(StringId::LOGFILES_TITLE)); if (count == 0) { - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + tft.setTextColor(TFT_DARKGREEN, TFT_BLACK); tft.setCursor(10, 40); tft.println(I18n::t(StringId::LOGFILES_EMPTY)); } else { @@ -55,7 +55,7 @@ void run(TFT_eSPI& tft) { int16_t y = 30; if (count > VISIBLE_ROWS) { - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + tft.setTextColor(TFT_DARKGREEN, TFT_BLACK); tft.setCursor(10, y); tft.print(String(I18n::t(StringId::LOGFILES_SHOWING_PREFIX)) + VISIBLE_ROWS + I18n::t(StringId::LOGFILES_OF) + count + I18n::t(StringId::LOGFILES_DAYS_SUFFIX)); @@ -66,7 +66,7 @@ void run(TFT_eSPI& tft) { tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, y); tft.print(days[i].date); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(110, y); tft.print(String(days[i].count) + I18n::t(StringId::LOGFILES_AIRCRAFT_SUFFIX)); y += 20; diff --git a/src/main.cpp b/src/main.cpp index 46c6d53..52ae2be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ #include "screenshot.h" #include "i18n.h" #include "first_run_language_screen.h" +#include "ui_font.h" TFT_eSPI tft = TFT_eSPI(); @@ -59,35 +60,70 @@ struct Rect { } }; -Rect menuBtn = {Config::SCREEN_WIDTH - 60, 3, 54, 22}; +Rect menuBtn = {Config::SCREEN_WIDTH - 90, 3, 54, 22}; Rect camBtn = {(int16_t)(menuBtn.x - 46), 3, 42, 22}; void drawMenuButton() { - tft.fillRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_NAVY); - tft.drawRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_DARKGREY); + tft.fillRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_BLACK); + tft.drawRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_GREEN); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, TFT_NAVY); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.drawString("Menu", menuBtn.x + menuBtn.w / 2, menuBtn.y + menuBtn.h / 2); tft.setTextDatum(TL_DATUM); } void drawCamButton() { - tft.fillRoundRect(camBtn.x, camBtn.y, camBtn.w, camBtn.h, 4, TFT_NAVY); - tft.drawRoundRect(camBtn.x, camBtn.y, camBtn.w, camBtn.h, 4, TFT_DARKGREY); + tft.fillRoundRect(camBtn.x, camBtn.y, camBtn.w, camBtn.h, 4, TFT_BLACK); + tft.drawRoundRect(camBtn.x, camBtn.y, camBtn.w, camBtn.h, 4, TFT_GREEN); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, TFT_NAVY); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.drawString("Cam", camBtn.x + camBtn.w / 2, camBtn.y + camBtn.h / 2); tft.setTextDatum(TL_DATUM); } +void drawWifiIcon(int16_t rightX, int16_t rowTop, int16_t rowH, int8_t rssi) { + uint8_t level; + if (rssi >= -55) level = 4; + else if (rssi >= -65) level = 3; + else if (rssi >= -75) level = 2; + else if (rssi >= -85) level = 1; + else level = 0; + + constexpr uint8_t BAR_W = 3; + constexpr uint8_t BAR_GAP = 2; + constexpr uint8_t BAR_COUNT = 4; + int16_t baseline = rowTop + rowH - 1; + int16_t x = rightX - BAR_COUNT * (BAR_W + BAR_GAP); + + for (uint8_t i = 0; i < BAR_COUNT; i++) { + int16_t barH = 3 + i * 2; + uint16_t color = (i < level) ? TFT_GREEN : TFT_DARKGREY; + tft.fillRect(x, baseline - barH, BAR_W, barH, color); + x += BAR_W + BAR_GAP; + } +} + +constexpr int16_t WIFI_ICON_W = 22; +constexpr int16_t WIFI_ICON_H = 14; + +void updateWifiIcon() { + int16_t iconX = Config::SCREEN_WIDTH - WIFI_ICON_W - 2; + int16_t iconY = 2; + tft.fillRect(iconX, iconY, WIFI_ICON_W, WIFI_ICON_H, TFT_BLACK); + if (WiFi.status() == WL_CONNECTED) { + drawWifiIcon(Config::SCREEN_WIDTH - 4, iconY, WIFI_ICON_H, WiFi.RSSI()); + } +} + void drawHeader() { tft.fillRect(0, 0, Config::SCREEN_WIDTH, CONTENT_TOP, TFT_BLACK); tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(1); tft.setCursor(6, 10); - tft.println("Eiswolfs Flightradar"); + tft.println("Eiswolfs FR"); drawCamButton(); drawMenuButton(); + updateWifiIcon(); } void updateStatusLine() { @@ -106,13 +142,7 @@ void updateStatusLine() { tft.print(timeBuf); } - if (WiFi.status() == WL_CONNECTED) { - char rssiBuf[14]; - snprintf(rssiBuf, sizeof(rssiBuf), "WiFi %ddBm", WiFi.RSSI()); - tft.setTextDatum(TR_DATUM); - tft.drawString(rssiBuf, Config::SCREEN_WIDTH - 4, HEADER_TITLE_H + 2); - tft.setTextDatum(TL_DATUM); - } + updateWifiIcon(); } void takeScreenshotWithFeedback() { @@ -183,6 +213,7 @@ void setup() { tft.init(); tft.setRotation(0); + tft.setFreeFont(&UiFont11pt); tft.invertDisplay(true); tft.fillScreen(TFT_BLACK); @@ -200,10 +231,6 @@ void setup() { } SdStorage::seedDefaultDataFiles(); - // "Allererster Start" erkennen: config.txt existiert noch nicht (wird - // erst beim ersten SettingsStore::save()-Aufruf angelegt). Muss VOR - // SettingsStore::load() geprueft werden, da load() die Datei nicht - // erzeugt, nur liest. bool isFirstRun = !SD.exists(Config::SD_SETTINGS_FILE); SettingsStore::load(); @@ -239,9 +266,6 @@ void setup() { } } - // --- Sprachauswahl: NUR beim allerersten Start, direkt nach Touch- - // Kalibrierung und WLAN-Ersteinrichtung, bevor irgendwelche weiteren - // Texte in (falscher) Standardsprache gezeigt werden. --- if (isFirstRun) { FirstRunLanguageScreen::run(tft); SplashScreen::begin(tft); diff --git a/src/menu_screen.cpp b/src/menu_screen.cpp index 26a6631..e593ec4 100644 --- a/src/menu_screen.cpp +++ b/src/menu_screen.cpp @@ -23,20 +23,33 @@ namespace { } }; - constexpr int16_t ROW_H = 26; - constexpr int16_t ROW_GAP = 3; - constexpr int16_t ROW_START_Y = 20; + constexpr int16_t ROW_H = 24; + constexpr int16_t ROW_GAP = 2; + constexpr int16_t ROW_START_Y = 18; Rect rowRect(uint8_t index) { return {10, (int16_t)(ROW_START_Y + index * (ROW_H + ROW_GAP)), (int16_t)(Config::SCREEN_WIDTH - 20), ROW_H}; } - void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { + constexpr int16_t CAT_ROW_H = 50; + constexpr int16_t CAT_ROW_GAP = 10; + constexpr int16_t CAT_START_Y = 30; + + Rect catRowRect(uint8_t index) { + return {10, (int16_t)(CAT_START_Y + index * (CAT_ROW_H + CAT_ROW_GAP)), + (int16_t)(Config::SCREEN_WIDTH - 20), CAT_ROW_H}; + } + + void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, + bool active = false, bool danger = false) { + uint16_t accent = danger ? TFT_RED : TFT_GREEN; + uint16_t bg = active ? accent : TFT_BLACK; + uint16_t fg = active ? TFT_BLACK : accent; tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); - tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, bg); + tft.setTextColor(fg, bg); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.setTextDatum(TL_DATUM); } @@ -57,28 +70,113 @@ namespace { tft.setTextDatum(TL_DATUM); delay(1200); } + + enum class Page { Main, Region, Wifi, System, Flight }; } void run(TFT_eSPI& tft) { - uint8_t page = 0; + Page page = Page::Main; bool done = false; while (!done) { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(10, 2); - tft.println(page == 0 ? I18n::t(StringId::MENU_SETTINGS) : I18n::t(StringId::MENU_SETTINGS_PAGE2)); - if (page == 0) { - Rect calibBtn = rowRect(0); - Rect invertBtn = rowRect(1); - Rect wifiBtn = rowRect(2); - Rect locationBtn = rowRect(3); - Rect timeoutBtn = rowRect(4); - Rect statsBtn = rowRect(5); - Rect logFilesBtn = rowRect(6); - Rect moreBtn = rowRect(7); - Rect backBtn = rowRect(8); + if (page == Page::Main) { + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 6); + tft.println(I18n::t(StringId::MENU_SETTINGS)); + + Rect regionBtn = catRowRect(0); + Rect wifiBtn = catRowRect(1); + Rect systemBtn = catRowRect(2); + Rect flightBtn = catRowRect(3); + Rect backBtn = catRowRect(4); + + drawButton(tft, regionBtn, I18n::t(StringId::MENU_CATEGORY_REGION)); + drawButton(tft, wifiBtn, I18n::t(StringId::MENU_CATEGORY_WIFI)); + drawButton(tft, systemBtn, I18n::t(StringId::MENU_CATEGORY_SYSTEM)); + drawButton(tft, flightBtn, I18n::t(StringId::MENU_CATEGORY_FLIGHT)); + drawButton(tft, backBtn, I18n::t(StringId::BACK)); + + TouchInput::Point tap; + while (true) { + if (TouchInput::wasTapped(tap)) break; + delay(20); + } + + if (regionBtn.contains(tap.x, tap.y)) { + page = Page::Region; + } else if (wifiBtn.contains(tap.x, tap.y)) { + page = Page::Wifi; + } else if (systemBtn.contains(tap.x, tap.y)) { + page = Page::System; + } else if (flightBtn.contains(tap.x, tap.y)) { + page = Page::Flight; + } else if (backBtn.contains(tap.x, tap.y)) { + done = true; + } + + } else if (page == Page::Region) { + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 2); + tft.println(I18n::t(StringId::MENU_CATEGORY_REGION)); + + Rect languageBtn = rowRect(0); + Rect unitsBtn = rowRect(1); + Rect backBtn = rowRect(2); + + drawButton(tft, languageBtn, String(I18n::t(StringId::MENU_LANGUAGE)) + ": " + I18n::languageName(SettingsStore::language())); + drawButton(tft, unitsBtn, I18n::t(StringId::MENU_UNITS)); + drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW)); + + TouchInput::Point tap; + while (true) { + if (TouchInput::wasTapped(tap)) break; + delay(20); + } + + if (languageBtn.contains(tap.x, tap.y)) { + LanguageScreen::run(tft); + } else if (unitsBtn.contains(tap.x, tap.y)) { + UnitsScreen::run(tft); + } else if (backBtn.contains(tap.x, tap.y)) { + page = Page::Main; + } + + } else if (page == Page::Wifi) { + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 2); + tft.println(I18n::t(StringId::MENU_CATEGORY_WIFI)); + + Rect wifiManageBtn = rowRect(0); + Rect backBtn = rowRect(1); + + drawButton(tft, wifiManageBtn, I18n::t(StringId::MENU_MANAGE_WIFI)); + drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW)); + + TouchInput::Point tap; + while (true) { + if (TouchInput::wasTapped(tap)) break; + delay(20); + } + + if (wifiManageBtn.contains(tap.x, tap.y)) { + WifiManageScreen::run(tft); + } else if (backBtn.contains(tap.x, tap.y)) { + page = Page::Main; + } + + } else if (page == Page::System) { + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 2); + tft.println(I18n::t(StringId::MENU_CATEGORY_SYSTEM)); + + Rect calibBtn = rowRect(0); + Rect invertBtn = rowRect(1); + Rect timeoutBtn = rowRect(2); + Rect backupBtn = rowRect(3); + Rect restoreBtn = rowRect(4); + Rect backBtn = rowRect(5); drawButton(tft, calibBtn, I18n::t(StringId::MENU_CALIBRATE)); @@ -87,13 +185,10 @@ void run(TFT_eSPI& tft) { : I18n::t(StringId::MENU_DISPLAY_NORMAL); drawButton(tft, invertBtn, invertLabel); - drawButton(tft, wifiBtn, I18n::t(StringId::MENU_MANAGE_WIFI)); - drawButton(tft, locationBtn, I18n::t(StringId::MENU_LOCATION_PRESETS)); drawButton(tft, timeoutBtn, screenTimeoutLabel(SettingsStore::screenTimeoutMinutes())); - drawButton(tft, statsBtn, I18n::t(StringId::MENU_STATISTICS)); - drawButton(tft, logFilesBtn, I18n::t(StringId::MENU_LOGBOOK_FILES)); - drawButton(tft, moreBtn, I18n::t(StringId::MENU_MORE_SETTINGS)); - drawButton(tft, backBtn, I18n::t(StringId::BACK)); + drawButton(tft, backupBtn, I18n::t(StringId::MENU_BACKUP)); + drawButton(tft, restoreBtn, I18n::t(StringId::MENU_RESTORE)); + drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW)); TouchInput::Point tap; while (true) { @@ -107,89 +202,10 @@ void run(TFT_eSPI& tft) { bool newState = !SettingsStore::displayInverted(); SettingsStore::setDisplayInverted(newState); tft.invertDisplay(newState); - } else if (wifiBtn.contains(tap.x, tap.y)) { - WifiManageScreen::run(tft); - } else if (locationBtn.contains(tap.x, tap.y)) { - LocationPresetsScreen::run(tft); } else if (timeoutBtn.contains(tap.x, tap.y)) { uint8_t current = SettingsStore::screenTimeoutMinutes(); uint8_t next = (current >= 10) ? 0 : (current + 1); SettingsStore::setScreenTimeoutMinutes(next); - } else if (statsBtn.contains(tap.x, tap.y)) { - StatsScreen::run(tft); - } else if (logFilesBtn.contains(tap.x, tap.y)) { - LogbookFilesScreen::run(tft); - } else if (moreBtn.contains(tap.x, tap.y)) { - page = 1; - } else if (backBtn.contains(tap.x, tap.y)) { - done = true; - } - } else if (page == 1) { - Rect emergencyBtn = rowRect(0); - Rect proximityBtn = rowRect(1); - Rect heartbeatBtn = rowRect(2); - Rect logbookBtn = rowRect(3); - Rect groundBtn = rowRect(4); - Rect airlineBtn = rowRect(5); - Rect moreBtn = rowRect(6); - Rect backBtn = rowRect(7); - - drawButton(tft, emergencyBtn, I18n::t(StringId::MENU_EMERGENCY_ALERT) + onOff(SettingsStore::emergencyAlertEnabled())); - drawButton(tft, proximityBtn, I18n::t(StringId::MENU_PROXIMITY_LED) + onOff(SettingsStore::proximityAlertEnabled())); - drawButton(tft, heartbeatBtn, I18n::t(StringId::MENU_LED_HEARTBEAT) + onOff(SettingsStore::ledHeartbeatEnabled())); - drawButton(tft, logbookBtn, I18n::t(StringId::MENU_FLIGHT_LOGBOOK) + onOff(SettingsStore::flightLogbookEnabled())); - drawButton(tft, groundBtn, I18n::t(StringId::MENU_HIDE_GROUND) + onOff(SettingsStore::hideGroundVehicles())); - drawButton(tft, airlineBtn, I18n::t(StringId::MENU_AIRLINE_FILTER)); - drawButton(tft, moreBtn, I18n::t(StringId::MENU_MORE_SETTINGS)); - drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW)); - - TouchInput::Point tap; - while (true) { - if (TouchInput::wasTapped(tap)) break; - delay(20); - } - - if (emergencyBtn.contains(tap.x, tap.y)) { - SettingsStore::setEmergencyAlertEnabled(!SettingsStore::emergencyAlertEnabled()); - } else if (proximityBtn.contains(tap.x, tap.y)) { - SettingsStore::setProximityAlertEnabled(!SettingsStore::proximityAlertEnabled()); - } else if (heartbeatBtn.contains(tap.x, tap.y)) { - SettingsStore::setLedHeartbeatEnabled(!SettingsStore::ledHeartbeatEnabled()); - } else if (logbookBtn.contains(tap.x, tap.y)) { - SettingsStore::setFlightLogbookEnabled(!SettingsStore::flightLogbookEnabled()); - } else if (groundBtn.contains(tap.x, tap.y)) { - SettingsStore::setHideGroundVehicles(!SettingsStore::hideGroundVehicles()); - } else if (airlineBtn.contains(tap.x, tap.y)) { - AirlineFilterScreen::run(tft); - } else if (moreBtn.contains(tap.x, tap.y)) { - page = 2; - } else if (backBtn.contains(tap.x, tap.y)) { - page = 0; - } - } else { - Rect languageBtn = rowRect(0); - Rect unitsBtn = rowRect(1); - Rect backupBtn = rowRect(2); - Rect restoreBtn = rowRect(3); - Rect backBtn = rowRect(4); - - drawButton(tft, languageBtn, String(I18n::t(StringId::MENU_LANGUAGE)) + ": " + I18n::languageName(SettingsStore::language())); - drawButton(tft, unitsBtn, I18n::t(StringId::MENU_UNITS)); - drawButton(tft, backupBtn, I18n::t(StringId::MENU_BACKUP)); - drawButton(tft, restoreBtn, I18n::t(StringId::MENU_RESTORE), - SettingsBackup::hasBackup() ? TFT_NAVY : TFT_DARKGREY); - drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW)); - - TouchInput::Point tap; - while (true) { - if (TouchInput::wasTapped(tap)) break; - delay(20); - } - - if (languageBtn.contains(tap.x, tap.y)) { - LanguageScreen::run(tft); - } else if (unitsBtn.contains(tap.x, tap.y)) { - UnitsScreen::run(tft); } else if (backupBtn.contains(tap.x, tap.y)) { bool ok = SettingsBackup::backup(); showBriefMessage(tft, I18n::t(ok ? StringId::MENU_BACKUP_SAVED : StringId::MENU_BACKUP_FAILED), @@ -201,7 +217,62 @@ void run(TFT_eSPI& tft) { ok ? TFT_GREEN : TFT_RED); } } else if (backBtn.contains(tap.x, tap.y)) { - page = 1; + page = Page::Main; + } + + } else { // Page::Flight + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.setCursor(10, 2); + tft.println(I18n::t(StringId::MENU_CATEGORY_FLIGHT)); + + Rect statsBtn = rowRect(0); + Rect logFilesBtn = rowRect(1); + Rect logbookBtn = rowRect(2); + Rect heartbeatBtn = rowRect(3); + Rect proximityBtn = rowRect(4); + Rect emergencyBtn = rowRect(5); + Rect locationBtn = rowRect(6); + Rect airlineBtn = rowRect(7); + Rect groundBtn = rowRect(8); + Rect backBtn = rowRect(9); + + drawButton(tft, statsBtn, I18n::t(StringId::MENU_STATISTICS)); + drawButton(tft, logFilesBtn, I18n::t(StringId::MENU_LOGBOOK_FILES)); + drawButton(tft, logbookBtn, I18n::t(StringId::MENU_FLIGHT_LOGBOOK) + onOff(SettingsStore::flightLogbookEnabled())); + drawButton(tft, heartbeatBtn, I18n::t(StringId::MENU_LED_HEARTBEAT) + onOff(SettingsStore::ledHeartbeatEnabled())); + drawButton(tft, proximityBtn, I18n::t(StringId::MENU_PROXIMITY_LED) + onOff(SettingsStore::proximityAlertEnabled())); + drawButton(tft, emergencyBtn, I18n::t(StringId::MENU_EMERGENCY_ALERT) + onOff(SettingsStore::emergencyAlertEnabled())); + drawButton(tft, locationBtn, I18n::t(StringId::MENU_LOCATION_PRESETS)); + drawButton(tft, airlineBtn, I18n::t(StringId::MENU_AIRLINE_FILTER)); + drawButton(tft, groundBtn, I18n::t(StringId::MENU_HIDE_GROUND) + onOff(SettingsStore::hideGroundVehicles())); + drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW)); + + TouchInput::Point tap; + while (true) { + if (TouchInput::wasTapped(tap)) break; + delay(20); + } + + if (statsBtn.contains(tap.x, tap.y)) { + StatsScreen::run(tft); + } else if (logFilesBtn.contains(tap.x, tap.y)) { + LogbookFilesScreen::run(tft); + } else if (logbookBtn.contains(tap.x, tap.y)) { + SettingsStore::setFlightLogbookEnabled(!SettingsStore::flightLogbookEnabled()); + } else if (heartbeatBtn.contains(tap.x, tap.y)) { + SettingsStore::setLedHeartbeatEnabled(!SettingsStore::ledHeartbeatEnabled()); + } else if (proximityBtn.contains(tap.x, tap.y)) { + SettingsStore::setProximityAlertEnabled(!SettingsStore::proximityAlertEnabled()); + } else if (emergencyBtn.contains(tap.x, tap.y)) { + SettingsStore::setEmergencyAlertEnabled(!SettingsStore::emergencyAlertEnabled()); + } else if (locationBtn.contains(tap.x, tap.y)) { + LocationPresetsScreen::run(tft); + } else if (airlineBtn.contains(tap.x, tap.y)) { + AirlineFilterScreen::run(tft); + } else if (groundBtn.contains(tap.x, tap.y)) { + SettingsStore::setHideGroundVehicles(!SettingsStore::hideGroundVehicles()); + } else if (backBtn.contains(tap.x, tap.y)) { + page = Page::Main; } } } diff --git a/src/radar_screen.cpp b/src/radar_screen.cpp index 6f80cac..e5606a3 100644 --- a/src/radar_screen.cpp +++ b/src/radar_screen.cpp @@ -30,7 +30,7 @@ namespace { int16_t infoTop; }; - constexpr int16_t INFO_BAR_H = 50; + constexpr int16_t INFO_BAR_H = 64; Layout computeLayout(int16_t top) { Layout L; @@ -45,7 +45,7 @@ namespace { return L; } - constexpr int16_t DETAIL_PANEL_H = 190; + constexpr int16_t DETAIL_PANEL_H = 264; struct HitPoint { int16_t x, y; @@ -177,10 +177,10 @@ namespace { } void drawButton(TFT_eSPI& gfx, const Rect& r, const String& label) { - gfx.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_NAVY); - gfx.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + gfx.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK); + gfx.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN); gfx.setTextDatum(MC_DATUM); - gfx.setTextColor(TFT_WHITE, TFT_NAVY); + gfx.setTextColor(TFT_GREEN, TFT_BLACK); gfx.drawString(label, r.x + r.w / 2, r.y + r.h / 2); gfx.setTextDatum(TL_DATUM); } @@ -210,7 +210,7 @@ namespace { gfx.setTextColor(TFT_WHITE, TFT_BLACK); for (uint8_t i = 0; i < 3; i++) { int16_t x0 = i * segW + 6; - gfx.fillCircle(x0, y + 4, 3, items[i].color); + gfx.fillCircle(x0, y - 5, 3, items[i].color); gfx.setCursor(x0 + 7, y); gfx.print(items[i].label); } @@ -279,8 +279,8 @@ namespace { void updateLine(TFT_eSPI& gfx, int16_t y, int16_t h, int16_t maxWidth, uint16_t fg, String& cached, const String& newText, bool forceFull) { if (!forceFull && cached == newText) return; - gfx.fillRect(0, y - 2, Config::SCREEN_WIDTH, h, TFT_NAVY); - gfx.setTextColor(fg, TFT_NAVY); + gfx.fillRect(0, y - 14, Config::SCREEN_WIDTH, h, TFT_BLACK); + gfx.setTextColor(fg, TFT_BLACK); printLineTruncated(gfx, 8, y, maxWidth, newText); cached = newText; } @@ -291,54 +291,55 @@ namespace { int16_t panelTop = Config::SCREEN_HEIGHT - DETAIL_PANEL_H; constexpr int16_t textMaxWidth = Config::SCREEN_WIDTH - 16; + constexpr int16_t LINE_H = 22; bool forceFull = !lastPanel.valid || strcmp(lastPanel.hex, a.hex) != 0; if (forceFull) { - gfx.fillRect(0, panelTop, Config::SCREEN_WIDTH, DETAIL_PANEL_H, TFT_NAVY); - gfx.drawRect(0, panelTop, Config::SCREEN_WIDTH, DETAIL_PANEL_H, TFT_DARKGREY); + gfx.fillRect(0, panelTop, Config::SCREEN_WIDTH, DETAIL_PANEL_H, TFT_BLACK); + gfx.drawRect(0, panelTop, Config::SCREEN_WIDTH, DETAIL_PANEL_H, TFT_GREEN); lastPanel = PanelState{}; strncpy(lastPanel.hex, a.hex, sizeof(lastPanel.hex) - 1); } - int16_t y = panelTop + 6; + int16_t y = panelTop + 26; { String txt = a.callsign[0] ? a.callsign : a.hex; if (forceFull || lastPanel.callsignText != txt) { - gfx.fillRect(0, y - 2, Config::SCREEN_WIDTH, 20, TFT_NAVY); - gfx.setTextColor(TFT_WHITE, TFT_NAVY); + gfx.fillRect(0, y - 22, Config::SCREEN_WIDTH, 28, TFT_BLACK); + gfx.setTextColor(TFT_GREEN, TFT_BLACK); gfx.setTextSize(2); printLineTruncated(gfx, 8, y, textMaxWidth, txt); gfx.setTextSize(1); lastPanel.callsignText = txt; } } - y += 22; + y += 30; - updateLine(gfx, y, 15, textMaxWidth, TFT_GREEN, lastPanel.airlineText, + updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.airlineText, String(a.airlineName), forceFull); - y += 15; + y += LINE_H; String modelLine = details.loading ? String(I18n::t(StringId::DETAIL_MODEL)) + I18n::t(StringId::DETAIL_LOADING_DOTS) : String(I18n::t(StringId::DETAIL_MODEL)) + (details.model[0] ? details.model : I18n::t(StringId::DETAIL_UNKNOWN)); - updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.modelText, modelLine, forceFull); - y += 15; + updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.modelText, modelLine, forceFull); + y += LINE_H; String typeLine = String(I18n::t(StringId::DETAIL_TYPE)) + (a.typeCode[0] ? a.typeCode : I18n::t(StringId::DETAIL_UNKNOWN)); - updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.typeText, typeLine, forceFull); - y += 15; + updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.typeText, typeLine, forceFull); + y += LINE_H; char buf[48]; snprintf(buf, sizeof(buf), "%s%.0fm / %.0fft", I18n::t(StringId::DETAIL_ALT), Units::feetToMeters((float)a.altBaroFt), (float)a.altBaroFt); - updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.altText, String(buf), forceFull); - y += 15; + updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.altText, String(buf), forceFull); + y += LINE_H; snprintf(buf, sizeof(buf), "%s%.0fkm/h / %.0fkt", I18n::t(StringId::DETAIL_SPEED), Units::ktToKmh(a.groundSpeedKt), a.groundSpeedKt); - updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.speedText, String(buf), forceFull); - y += 15; + updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.speedText, String(buf), forceFull); + y += LINE_H; String climbLine; if (a.vertRateFtMin > 100) { @@ -350,32 +351,66 @@ namespace { } else { climbLine = I18n::t(StringId::DETAIL_LEVEL); } - updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.climbText, climbLine, forceFull); - y += 15; + updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.climbText, climbLine, forceFull); + y += LINE_H; snprintf(buf, sizeof(buf), "%s%.0fkm / %.0fnm %s%.0f", I18n::t(StringId::DETAIL_DIST), a.distanceKm, Units::kmToNm(a.distanceKm), I18n::t(StringId::DETAIL_HDG), a.headingDeg); - updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.distHeadingText, String(buf), forceFull); - y += 15; + updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.distHeadingText, String(buf), forceFull); + y += LINE_H; String squawkLine = String(I18n::t(StringId::DETAIL_SQUAWK)) + (a.squawk[0] ? a.squawk : I18n::t(StringId::DETAIL_UNKNOWN)); - updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.squawkText, squawkLine, forceFull); - y += 15; + updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.squawkText, squawkLine, forceFull); + y += LINE_H; String seatsLine = a.estSeats > 0 ? String(I18n::t(StringId::DETAIL_SEATS_EST)) + a.estSeats : String(I18n::t(StringId::DETAIL_SEATS_UNKNOWN)); - updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.seatsText, seatsLine, forceFull); - y += 18; + updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.seatsText, seatsLine, forceFull); + y += 22; if (forceFull) { - gfx.setTextColor(TFT_DARKGREY, TFT_NAVY); + gfx.setTextColor(TFT_DARKGREEN, TFT_BLACK); gfx.setCursor(8, y); gfx.print(I18n::t(StringId::DETAIL_TAP_CLOSE)); } lastPanel.valid = true; } + + constexpr uint8_t STAR_COUNT = 28; + struct Star { + int16_t x, y; + uint8_t phase; + uint8_t speed; + }; + Star stars[STAR_COUNT]; + bool starsInitialized = false; + + void initStarsIfNeeded() { + if (starsInitialized) return; + int16_t panelTop = Config::SCREEN_HEIGHT - DETAIL_PANEL_H; + randomSeed((uint32_t)esp_random()); + for (uint8_t i = 0; i < STAR_COUNT; i++) { + stars[i].x = 6 + random(0, Config::SCREEN_WIDTH - 12); + stars[i].y = panelTop + 10 + random(0, DETAIL_PANEL_H - 20); + stars[i].phase = (uint8_t)random(0, 256); + stars[i].speed = 1 + random(0, 3); + } + starsInitialized = true; + } + + void updateDetailPanelStars(TFT_eSPI& gfx) { + initStarsIfNeeded(); + for (uint8_t i = 0; i < STAR_COUNT; i++) { + stars[i].phase += stars[i].speed; + uint8_t bright = (stars[i].phase < 128) + ? (uint8_t)(stars[i].phase * 2) + : (uint8_t)((255 - stars[i].phase) * 2); + uint16_t color = gfx.color565(0, bright, 0); + gfx.drawPixel(stars[i].x, stars[i].y, color); + } + } } void render(TFT_eSPI& tft, int16_t top) { @@ -470,12 +505,6 @@ void render(TFT_eSPI& tft, int16_t top) { tft.drawCircle(pt.x, pt.y, 12, TFT_RED); } - // Rufzeichen als kleines farbiges Label ueber dem Punkt - // Transparent zeichnen (KEIN schwarzer Hintergrund-Kasten) - sonst - // "beisst" der Label-Hintergrund ein Stueck der Ring-/Fadenkreuz- - // Linie weg, wenn sich beides ueberschneidet. Da das Label bei jedem - // Sweep-Tick am selben Fleck neu gezeichnet wird, sah das wie - // Dauerflackern genau an den Beschriftungen aus. tft.setTextColor(color); tft.setTextDatum(BC_DATUM); const char* label = a.callsign[0] ? a.callsign : a.hex; @@ -506,21 +535,26 @@ void render(TFT_eSPI& tft, int16_t top) { int16_t infoTop = L.infoTop; tft.drawFastHLine(0, infoTop, Config::SCREEN_WIDTH, TFT_DARKGREY); tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setCursor(8, infoTop + 6); + tft.setCursor(8, infoTop + 20); tft.print(I18n::t(StringId::RADAR_TAP_FOR_DETAILS)); char rangeLabel[8]; snprintf(rangeLabel, sizeof(rangeLabel), "%.0fkm", rangeKm); drawButton(tft, L.rangeBtn, rangeLabel); - drawLegend(tft, infoTop + 30); + drawLegend(tft, infoTop + 44); } tft.endWrite(); } void tick(TFT_eSPI& tft, int16_t top, uint32_t deltaMs) { - if (selectedHex[0]) return; + if (selectedHex[0]) { + tft.startWrite(); + updateDetailPanelStars(tft); + tft.endWrite(); + return; + } Layout L = computeLayout(top); float rangeKm = Config::RANGE_STEPS_KM[SettingsStore::rangeIndex()]; diff --git a/src/stats_screen.cpp b/src/stats_screen.cpp index 9e9d1da..84f2f01 100644 --- a/src/stats_screen.cpp +++ b/src/stats_screen.cpp @@ -14,17 +14,18 @@ namespace { } }; - void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { - tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); - tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool danger = false) { + uint16_t accent = danger ? TFT_RED : TFT_GREEN; + tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK); + tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, bg); + tft.setTextColor(accent, TFT_BLACK); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.setTextDatum(TL_DATUM); } void drawStatRow(TFT_eSPI& tft, int16_t y, const String& label, const String& value) { - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + tft.setTextColor(TFT_DARKGREEN, TFT_BLACK); tft.setCursor(10, y); tft.print(label); tft.setTextColor(TFT_GREEN, TFT_BLACK); @@ -39,10 +40,10 @@ namespace { void run(TFT_eSPI& tft) { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 10); tft.println(I18n::t(StringId::STATS_TITLE)); - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + tft.setTextColor(TFT_DARKGREEN, TFT_BLACK); tft.setCursor(10, 44); tft.print(I18n::t(StringId::LOADING)); @@ -60,7 +61,7 @@ void run(TFT_eSPI& tft) { auto redraw = [&]() { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 10); tft.println(I18n::t(StringId::STATS_TITLE)); @@ -84,12 +85,12 @@ void run(TFT_eSPI& tft) { uint32_t upM = (upSec % 3600) / 60; char upBuf[8]; snprintf(upBuf, sizeof(upBuf), "%luh %lum", (unsigned long)upH, (unsigned long)upM); - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + tft.setTextColor(TFT_DARKGREEN, TFT_BLACK); tft.setCursor(10, 210); tft.print(String(I18n::t(StringId::STATS_UPTIME_PREFIX)) + upBuf); if (confirmPending) { - drawButton(tft, resetBtn, I18n::t(StringId::STATS_RESET_CONFIRM), TFT_MAROON); + drawButton(tft, resetBtn, I18n::t(StringId::STATS_RESET_CONFIRM), true); } else { drawButton(tft, resetBtn, I18n::t(StringId::STATS_RESET_BTN)); } diff --git a/src/ui_font.h b/src/ui_font.h new file mode 100644 index 0000000..095c517 --- /dev/null +++ b/src/ui_font.h @@ -0,0 +1,461 @@ +#pragma once +// Automatisch generierter Font (DejaVu Sans Mono, 11px) fuer TFT_eSPI GFXFF. +// Deckt Basis-ASCII + Umlaute/Akzente fuer DE/TR/FR/ES/IT ab (U+0020-U+015F). +// NICHT von Hand bearbeiten - siehe Kommentar in i18n.h fuer den Generator-Hinweis. +#include + +const uint8_t UiFont11ptBitmaps[] PROGMEM = { + 0x00, 0xFD, 0xB6, 0x80, 0x14, 0x49, 0xF9, 0x42, 0x9F, 0x92, 0x28, 0x23, 0xE9, 0x47, 0x14, 0xBE, + 0x21, 0x00, 0xE2, 0x8E, 0x46, 0x23, 0x71, 0x47, 0x71, 0x04, 0x18, 0xB6, 0x58, 0x9F, 0xE0, 0x6A, + 0xAA, 0x90, 0xA5, 0x55, 0xA0, 0x25, 0x5C, 0xEA, 0x90, 0x21, 0x3E, 0x42, 0x00, 0x58, 0xE0, 0xC0, + 0x08, 0x84, 0x42, 0x11, 0x08, 0x80, 0x7B, 0x38, 0x65, 0x86, 0x1C, 0xDE, 0xE1, 0x08, 0x42, 0x10, + 0x9F, 0x7A, 0x10, 0x43, 0x18, 0xC4, 0x3F, 0x7A, 0x10, 0x5E, 0x0C, 0x18, 0x5E, 0x18, 0x62, 0x92, + 0xCB, 0xF0, 0x82, 0xFA, 0x08, 0x3E, 0x0C, 0x10, 0x7E, 0x3D, 0x08, 0x2E, 0xC6, 0x18, 0x5E, 0xFC, + 0x20, 0x84, 0x10, 0x82, 0x10, 0x7A, 0x18, 0x5E, 0x86, 0x18, 0x5E, 0x7A, 0x18, 0x61, 0x7C, 0x10, + 0xBC, 0xCC, 0x50, 0x58, 0x04, 0xEC, 0x1C, 0x0C, 0xFC, 0x0F, 0xC0, 0x81, 0xC0, 0xCE, 0xC0, 0xE1, + 0x36, 0x44, 0x04, 0x39, 0x38, 0x67, 0xA6, 0x99, 0xF0, 0x40, 0xE0, 0x30, 0xC3, 0x12, 0x49, 0xE8, + 0x61, 0xFA, 0x18, 0x7E, 0x86, 0x18, 0x7E, 0x39, 0x18, 0x20, 0x82, 0x04, 0x4E, 0xF2, 0x28, 0x61, + 0x86, 0x18, 0xBC, 0xFE, 0x08, 0x3F, 0x82, 0x08, 0x3F, 0xFE, 0x08, 0x3F, 0x82, 0x08, 0x20, 0x39, + 0x18, 0x20, 0x8E, 0x14, 0x4E, 0x86, 0x18, 0x7F, 0x86, 0x18, 0x61, 0xF9, 0x08, 0x42, 0x10, 0x9F, + 0x38, 0x42, 0x10, 0x86, 0x2E, 0x8A, 0x4A, 0x30, 0xA2, 0x48, 0xA1, 0x82, 0x08, 0x20, 0x82, 0x08, + 0x3F, 0x87, 0x3C, 0xED, 0xB6, 0x18, 0x61, 0x87, 0x1A, 0x69, 0x96, 0x58, 0xE1, 0x7B, 0x38, 0x61, + 0x86, 0x1C, 0xDE, 0xFA, 0x18, 0x61, 0xFA, 0x08, 0x20, 0x7B, 0x38, 0x61, 0x86, 0x1C, 0xDE, 0x0C, + 0xF9, 0x0A, 0x14, 0x2F, 0x91, 0x21, 0x41, 0x7A, 0x18, 0x3C, 0x0C, 0x18, 0x5E, 0xFE, 0x20, 0x40, + 0x81, 0x02, 0x04, 0x08, 0x86, 0x18, 0x61, 0x86, 0x18, 0x5E, 0x86, 0x14, 0x92, 0x48, 0xC3, 0x0C, + 0x83, 0x26, 0x4D, 0x56, 0xCD, 0x91, 0x22, 0x85, 0x24, 0x8C, 0x31, 0x24, 0xA1, 0xC6, 0x88, 0xA1, + 0xC1, 0x02, 0x04, 0x08, 0xFC, 0x20, 0x84, 0x21, 0x84, 0x3F, 0xEA, 0xAA, 0xB0, 0x82, 0x10, 0x42, + 0x10, 0x42, 0x08, 0xD5, 0x55, 0x70, 0x31, 0x28, 0x40, 0xFE, 0x44, 0xF0, 0x5F, 0x18, 0xBC, 0x84, + 0x21, 0xE8, 0xC6, 0x31, 0xF0, 0x7E, 0x21, 0x0C, 0x3C, 0x08, 0x42, 0xF8, 0xC6, 0x31, 0x78, 0x74, + 0x7F, 0x08, 0x3C, 0x19, 0x09, 0xF2, 0x10, 0x84, 0x20, 0x7C, 0x63, 0x18, 0xBC, 0x2E, 0x84, 0x21, + 0x6C, 0xC6, 0x31, 0x88, 0x20, 0x01, 0xC2, 0x10, 0x84, 0xF8, 0x20, 0x72, 0x49, 0x27, 0x00, 0x84, + 0x21, 0x2A, 0x62, 0x92, 0x88, 0xE1, 0x08, 0x42, 0x10, 0x84, 0x18, 0xFD, 0x6B, 0x5A, 0xD4, 0xB6, + 0x63, 0x18, 0xC4, 0x74, 0x63, 0x18, 0xB8, 0xF4, 0x63, 0x18, 0xFA, 0x10, 0x7C, 0x63, 0x18, 0xBC, + 0x21, 0xF9, 0x88, 0x88, 0x7C, 0x38, 0x30, 0xF8, 0x21, 0x3E, 0x42, 0x10, 0x87, 0x8C, 0x63, 0x18, + 0xBC, 0x8C, 0x54, 0xA5, 0x10, 0x83, 0x05, 0x52, 0xA2, 0x85, 0x00, 0xDA, 0x88, 0x45, 0x6C, 0x8C, + 0x94, 0xA6, 0x11, 0x18, 0xF8, 0x8C, 0xC4, 0x7C, 0x39, 0x08, 0x4C, 0x10, 0x84, 0x21, 0xC0, 0xFF, + 0xE0, 0xE1, 0x08, 0x41, 0x90, 0x84, 0x27, 0x00, 0xE0, 0x70, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, + 0x87, 0xF0, 0x00, 0xBF, 0x10, 0x9F, 0xA9, 0x4B, 0x4F, 0x10, 0x80, 0x3A, 0x10, 0x8F, 0x21, 0x1F, + 0x85, 0xE4, 0x92, 0x7A, 0x10, 0x82, 0x88, 0xA3, 0x61, 0x0F, 0x84, 0x08, 0xF7, 0x80, 0x7C, 0x19, + 0x68, 0xB4, 0x41, 0xF0, 0xA0, 0x38, 0xBA, 0x8D, 0x19, 0xE8, 0x8E, 0x00, 0xFF, 0x9F, 0x0F, 0x25, + 0x29, 0x12, 0x24, 0xFC, 0x10, 0x40, 0xE0, 0x38, 0xFA, 0xFD, 0x5A, 0x68, 0x8E, 0x00, 0xE0, 0xF7, + 0x80, 0x21, 0x3E, 0x42, 0x03, 0xE0, 0xE5, 0x70, 0xE8, 0xF0, 0x50, 0x8A, 0x28, 0xA2, 0x8B, 0xF8, + 0x20, 0x7F, 0xDF, 0x7D, 0x74, 0x51, 0x45, 0x14, 0xC0, 0x70, 0xC9, 0x70, 0x69, 0x96, 0x0F, 0x91, + 0x22, 0x52, 0x90, 0x60, 0x82, 0x1C, 0x1F, 0x80, 0x8E, 0x3C, 0x20, 0x60, 0x82, 0x1C, 0x1F, 0x81, + 0xC1, 0x08, 0x70, 0x70, 0x81, 0x18, 0x1F, 0x80, 0x8E, 0x3C, 0x20, 0x20, 0x22, 0x6C, 0x87, 0x40, + 0x80, 0x0C, 0x30, 0xC4, 0x92, 0x7A, 0x18, 0x40, 0x08, 0x40, 0x0C, 0x30, 0xC4, 0x92, 0x7A, 0x18, + 0x40, 0x31, 0x20, 0x0C, 0x30, 0xC4, 0x92, 0x7A, 0x18, 0x40, 0x69, 0x60, 0x0C, 0x30, 0xC4, 0x92, + 0x7A, 0x18, 0x40, 0x48, 0x03, 0x0C, 0x31, 0x24, 0x9E, 0x86, 0x10, 0x30, 0xC2, 0x08, 0x30, 0xC4, + 0x9E, 0x4A, 0x10, 0x3C, 0xC5, 0x17, 0x51, 0xC9, 0x27, 0x39, 0x18, 0x20, 0x82, 0x04, 0x4E, 0x20, + 0x80, 0x40, 0x80, 0x3F, 0x82, 0x0F, 0xE0, 0x82, 0x0F, 0xC0, 0x08, 0x40, 0x3F, 0x82, 0x0F, 0xE0, + 0x82, 0x0F, 0xC0, 0x21, 0x40, 0x3F, 0x82, 0x0F, 0xE0, 0x82, 0x0F, 0xC0, 0x50, 0x0F, 0xE0, 0x83, + 0xF8, 0x20, 0x83, 0xF0, 0x41, 0x01, 0xF2, 0x10, 0x84, 0x21, 0x3E, 0x08, 0x81, 0xF2, 0x10, 0x84, + 0x21, 0x3E, 0x22, 0x81, 0xF2, 0x10, 0x84, 0x21, 0x3E, 0x50, 0x3E, 0x42, 0x10, 0x84, 0x27, 0xC0, + 0x78, 0x89, 0x0F, 0x14, 0x28, 0x51, 0x3C, 0x69, 0x60, 0x21, 0xC6, 0x9A, 0x65, 0x96, 0x38, 0x40, + 0x40, 0x80, 0x1E, 0xCE, 0x18, 0x61, 0x87, 0x37, 0x80, 0x08, 0x40, 0x1E, 0xCE, 0x18, 0x61, 0x87, + 0x37, 0x80, 0x31, 0x20, 0x1E, 0xCE, 0x18, 0x61, 0x87, 0x37, 0x80, 0x69, 0x60, 0x1E, 0xCE, 0x18, + 0x61, 0x87, 0x37, 0x80, 0x48, 0x07, 0xB3, 0x86, 0x18, 0x61, 0xCD, 0xE0, 0x8A, 0x88, 0xA8, 0x80, + 0x3E, 0xCD, 0x1A, 0x55, 0x2C, 0x59, 0xDE, 0x40, 0x80, 0x21, 0x86, 0x18, 0x61, 0x86, 0x17, 0x80, + 0x08, 0x40, 0x21, 0x86, 0x18, 0x61, 0x86, 0x17, 0x80, 0x31, 0x20, 0x21, 0x86, 0x18, 0x61, 0x86, + 0x17, 0x80, 0x48, 0x08, 0x61, 0x86, 0x18, 0x61, 0x85, 0xE0, 0x08, 0x20, 0x06, 0x34, 0x45, 0x0E, + 0x08, 0x10, 0x20, 0x40, 0x83, 0xE8, 0x61, 0x87, 0xE8, 0x20, 0x64, 0xA5, 0x4A, 0x4A, 0x31, 0xB8, + 0x20, 0x81, 0xE0, 0xBE, 0x31, 0x78, 0x11, 0x01, 0xE0, 0xBE, 0x31, 0x78, 0x22, 0x81, 0xE0, 0xBE, + 0x31, 0x78, 0x6A, 0xC1, 0xE0, 0xBE, 0x31, 0x78, 0x50, 0x3C, 0x17, 0xC6, 0x2F, 0x72, 0x9C, 0x0F, + 0x05, 0xF1, 0x8B, 0xC0, 0xD9, 0x7F, 0x4A, 0x6C, 0x7E, 0x21, 0x0C, 0x3C, 0x84, 0x20, 0x80, 0xE8, + 0xFE, 0x10, 0x78, 0x18, 0x80, 0xE8, 0xFE, 0x10, 0x78, 0x22, 0x80, 0xE8, 0xFE, 0x10, 0x78, 0x50, + 0x1D, 0x1F, 0xC2, 0x0F, 0x20, 0x81, 0xC2, 0x10, 0x84, 0xF8, 0x11, 0x01, 0xC2, 0x10, 0x84, 0xF8, + 0x22, 0x81, 0xC2, 0x10, 0x84, 0xF8, 0x50, 0x38, 0x42, 0x10, 0x9F, 0x43, 0x84, 0xE8, 0xC6, 0x31, + 0x70, 0x6A, 0xC1, 0x6C, 0xC6, 0x31, 0x88, 0x20, 0x80, 0xE8, 0xC6, 0x31, 0x70, 0x11, 0x00, 0xE8, + 0xC6, 0x31, 0x70, 0x22, 0x80, 0xE8, 0xC6, 0x31, 0x70, 0xED, 0xC0, 0xE8, 0xC6, 0x31, 0x70, 0x50, + 0x1D, 0x18, 0xC6, 0x2E, 0x20, 0x3E, 0x02, 0x00, 0x3D, 0x15, 0xD9, 0x65, 0xE0, 0x20, 0x81, 0x18, + 0xC6, 0x31, 0x78, 0x11, 0x01, 0x18, 0xC6, 0x31, 0x78, 0x22, 0x81, 0x18, 0xC6, 0x31, 0x78, 0x50, + 0x23, 0x18, 0xC6, 0x2F, 0x11, 0x01, 0x19, 0x29, 0x4C, 0x22, 0x30, 0x84, 0x21, 0xE8, 0xC6, 0x31, + 0xF4, 0x20, 0x50, 0x23, 0x25, 0x29, 0x84, 0x46, 0x00, 0x78, 0x03, 0x0C, 0x31, 0x24, 0x9E, 0x86, + 0x10, 0x70, 0x3C, 0x17, 0xC6, 0x2F, 0x48, 0xC0, 0x0C, 0x30, 0xC4, 0x92, 0x7A, 0x18, 0x40, 0x53, + 0x81, 0xE0, 0xBE, 0x31, 0x78, 0x30, 0xC3, 0x12, 0x49, 0xE8, 0x61, 0x08, 0x20, 0xF0, 0x5F, 0x18, + 0xBC, 0x43, 0x08, 0x20, 0x0E, 0x46, 0x08, 0x20, 0x81, 0x13, 0x80, 0x08, 0x80, 0xFC, 0x42, 0x18, + 0x78, 0x30, 0xC0, 0x0E, 0x46, 0x08, 0x20, 0x81, 0x13, 0x80, 0x32, 0x80, 0xFC, 0x42, 0x18, 0x78, + 0x20, 0x03, 0x91, 0x82, 0x08, 0x20, 0x44, 0xE0, 0x20, 0x1F, 0x88, 0x43, 0x0F, 0x50, 0xC0, 0x0E, + 0x46, 0x08, 0x20, 0x81, 0x13, 0x80, 0x51, 0x80, 0xFC, 0x42, 0x18, 0x78, 0x61, 0x80, 0x3C, 0x8A, + 0x18, 0x61, 0x86, 0x2F, 0x00, 0x0A, 0x14, 0x23, 0xC8, 0x91, 0x22, 0x44, 0x78, 0x78, 0x89, 0x0F, + 0x14, 0x28, 0x51, 0x3C, 0x08, 0xF0, 0x9E, 0x8A, 0x28, 0xA2, 0x78, 0x70, 0x0F, 0xE0, 0x83, 0xF8, + 0x20, 0x83, 0xF0, 0x70, 0x1D, 0x1F, 0xC2, 0x0F, 0x50, 0xC0, 0x3F, 0x82, 0x0F, 0xE0, 0x82, 0x0F, + 0xC0, 0x51, 0x80, 0xE8, 0xFE, 0x10, 0x78, 0x20, 0x0F, 0xE0, 0x83, 0xF8, 0x20, 0x83, 0xF0, 0x20, + 0x1D, 0x1F, 0xC2, 0x0F, 0xFE, 0x08, 0x3F, 0x82, 0x08, 0x3F, 0x10, 0x60, 0x74, 0x7F, 0x08, 0x3C, + 0x86, 0x50, 0x80, 0x3F, 0x82, 0x0F, 0xE0, 0x82, 0x0F, 0xC0, 0x51, 0x00, 0xE8, 0xFE, 0x10, 0x78, + 0x21, 0x40, 0x0E, 0x46, 0x08, 0x23, 0x85, 0x13, 0x80, 0x22, 0x80, 0xF8, 0xC6, 0x31, 0x78, 0x5C, + 0x50, 0xC0, 0x0E, 0x46, 0x08, 0x23, 0x85, 0x13, 0x80, 0x53, 0x80, 0xF8, 0xC6, 0x31, 0x78, 0x5C, + 0x20, 0x03, 0x91, 0x82, 0x08, 0xE1, 0x44, 0xE0, 0x20, 0x1F, 0x18, 0xC6, 0x2F, 0x0B, 0x80, 0x39, + 0x18, 0x20, 0x8E, 0x14, 0x4E, 0x00, 0x82, 0x00, 0x21, 0x00, 0xF8, 0xC6, 0x31, 0x78, 0x5C, 0x31, + 0x20, 0x21, 0x86, 0x1F, 0xE1, 0x86, 0x18, 0x40, 0x22, 0x81, 0x08, 0x42, 0xD9, 0x8C, 0x63, 0x10, + 0x42, 0xFF, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x43, 0xC4, 0x1E, 0x45, 0x14, 0x51, 0x44, 0xED, + 0xC1, 0xF2, 0x10, 0x84, 0x21, 0x3E, 0x6A, 0xC1, 0xC2, 0x10, 0x84, 0xF8, 0x70, 0x3E, 0x42, 0x10, + 0x84, 0x27, 0xC0, 0x70, 0x38, 0x42, 0x10, 0x9F, 0x53, 0x81, 0xF2, 0x10, 0x84, 0x21, 0x3E, 0x53, + 0x81, 0xC2, 0x10, 0x84, 0xF8, 0xF9, 0x08, 0x42, 0x10, 0x9F, 0x43, 0x00, 0x20, 0x01, 0xC2, 0x10, + 0x84, 0xF9, 0x08, 0x20, 0x3E, 0x42, 0x10, 0x84, 0x27, 0xC0, 0xE1, 0x08, 0x42, 0x7C, 0xE6, 0x85, + 0x0A, 0x14, 0x28, 0x50, 0xF7, 0x24, 0x00, 0x1F, 0x24, 0x92, 0x49, 0xFC, 0x11, 0xC0, 0x22, 0x80, + 0x70, 0x84, 0x21, 0x0C, 0x5C, 0x25, 0x0E, 0x22, 0x22, 0x22, 0xC0, 0x8A, 0x4A, 0x30, 0xA2, 0x48, + 0xA1, 0x00, 0x82, 0x00, 0x84, 0x21, 0x2A, 0x62, 0x92, 0x88, 0x0C, 0x40, 0x95, 0x31, 0x49, 0x44, + 0x41, 0x00, 0x20, 0x82, 0x08, 0x20, 0x82, 0x0F, 0xC0, 0x08, 0x41, 0xC2, 0x10, 0x84, 0x21, 0x08, + 0x30, 0x82, 0x08, 0x20, 0x82, 0x08, 0x3F, 0x00, 0x82, 0x00, 0xE1, 0x08, 0x42, 0x10, 0x84, 0x18, + 0x0C, 0x40, 0x92, 0x48, 0x20, 0x82, 0x08, 0x3F, 0xE4, 0x92, 0x08, 0x20, 0x82, 0x08, 0x18, 0x82, + 0x08, 0xA2, 0x82, 0x08, 0x3F, 0xE0, 0x40, 0x81, 0x12, 0x24, 0x08, 0x10, 0x18, 0x40, 0x81, 0x63, + 0x0C, 0x08, 0x10, 0x3F, 0xE1, 0x0A, 0x66, 0x50, 0x84, 0x18, 0x08, 0x40, 0x21, 0xC6, 0x9A, 0x65, + 0x96, 0x38, 0x40, 0x18, 0x81, 0x6C, 0xC6, 0x31, 0x88, 0x87, 0x1A, 0x69, 0x96, 0x58, 0xE1, 0x00, + 0x84, 0x00, 0xB6, 0x63, 0x18, 0xC4, 0x04, 0x40, 0x50, 0x80, 0x21, 0xC6, 0x9A, 0x65, 0x96, 0x38, + 0x40, 0x51, 0x01, 0x6C, 0xC6, 0x31, 0x88, 0x41, 0x02, 0x01, 0xE2, 0x64, 0x48, 0x91, 0x22, 0xBB, + 0x18, 0x61, 0x86, 0x18, 0x61, 0x04, 0x60, 0xB6, 0x63, 0x18, 0xC4, 0x27, 0x78, 0x07, 0xB3, 0x86, + 0x18, 0x61, 0xCD, 0xE0, 0x70, 0x1D, 0x18, 0xC6, 0x2E, 0x48, 0xC0, 0x1E, 0xCE, 0x18, 0x61, 0x87, + 0x37, 0x80, 0x53, 0x80, 0xE8, 0xC6, 0x31, 0x70, 0x50, 0x07, 0xB3, 0x86, 0x18, 0x61, 0xCD, 0xE0, + 0x2A, 0x80, 0xE8, 0xC6, 0x31, 0x70, 0x7E, 0x49, 0x27, 0x92, 0x49, 0x1F, 0xDD, 0x6F, 0x4A, 0x6C, + 0x10, 0x20, 0x07, 0xC8, 0x50, 0xA1, 0x7C, 0x89, 0x0A, 0x08, 0x18, 0x81, 0xE9, 0x42, 0x10, 0x80, + 0xF9, 0x0A, 0x14, 0x2F, 0x91, 0x21, 0x41, 0x00, 0x40, 0x80, 0x7A, 0x50, 0x84, 0x20, 0x08, 0xC0, + 0x60, 0xC0, 0x07, 0xC8, 0x50, 0xA1, 0x7C, 0x89, 0x0A, 0x08, 0xA6, 0x0F, 0x98, 0x88, 0x80, 0x08, + 0x40, 0x1E, 0x86, 0x0F, 0x03, 0x06, 0x17, 0x80, 0x18, 0x80, 0xF8, 0x70, 0x61, 0xF0, 0x21, 0x40, + 0x1E, 0x86, 0x0F, 0x03, 0x06, 0x17, 0x80, 0x22, 0x80, 0xF8, 0x70, 0x61, 0xF0, 0x7A, 0x18, 0x3C, + 0x0C, 0x18, 0x5E, 0x21, 0x80, 0x7C, 0x38, 0x30, 0xF8, 0x8C, +}; + +const GFXglyph UiFont11ptGlyphs[] PROGMEM = { + { 0, 1, 1, 7, 0, -1 }, // 0x0020 + { 1, 1, 8, 7, 3, -8 }, // 0x0021 + { 2, 3, 3, 7, 2, -8 }, // 0x0022 + { 4, 7, 8, 7, 0, -8 }, // 0x0023 + { 11, 5, 10, 7, 1, -8 }, // 0x0024 + { 18, 6, 8, 7, 0, -8 }, // 0x0025 + { 24, 6, 8, 7, 1, -8 }, // 0x0026 + { 30, 1, 3, 7, 3, -8 }, // 0x0027 + { 31, 2, 10, 7, 2, -9 }, // 0x0028 + { 34, 2, 10, 7, 2, -9 }, // 0x0029 + { 37, 5, 6, 7, 1, -8 }, // 0x002A + { 41, 5, 5, 7, 1, -6 }, // 0x002B + { 45, 2, 3, 7, 2, -2 }, // 0x002C + { 46, 3, 1, 7, 2, -4 }, // 0x002D + { 47, 1, 2, 7, 3, -2 }, // 0x002E + { 48, 5, 9, 7, 1, -8 }, // 0x002F + { 54, 6, 8, 7, 1, -8 }, // 0x0030 + { 60, 5, 8, 7, 1, -8 }, // 0x0031 + { 65, 6, 8, 7, 1, -8 }, // 0x0032 + { 71, 6, 8, 7, 1, -8 }, // 0x0033 + { 77, 6, 8, 7, 1, -8 }, // 0x0034 + { 83, 6, 8, 7, 1, -8 }, // 0x0035 + { 89, 6, 8, 7, 1, -8 }, // 0x0036 + { 95, 6, 8, 7, 1, -8 }, // 0x0037 + { 101, 6, 8, 7, 1, -8 }, // 0x0038 + { 107, 6, 8, 7, 1, -8 }, // 0x0039 + { 113, 1, 6, 7, 3, -6 }, // 0x003A + { 114, 2, 7, 7, 2, -6 }, // 0x003B + { 116, 6, 5, 7, 1, -6 }, // 0x003C + { 120, 6, 3, 7, 0, -5 }, // 0x003D + { 123, 6, 5, 7, 1, -6 }, // 0x003E + { 127, 4, 8, 7, 2, -8 }, // 0x003F + { 131, 6, 10, 7, 1, -8 }, // 0x0040 + { 139, 6, 8, 7, 1, -8 }, // 0x0041 + { 145, 6, 8, 7, 1, -8 }, // 0x0042 + { 151, 6, 8, 7, 1, -8 }, // 0x0043 + { 157, 6, 8, 7, 1, -8 }, // 0x0044 + { 163, 6, 8, 7, 1, -8 }, // 0x0045 + { 169, 6, 8, 7, 1, -8 }, // 0x0046 + { 175, 6, 8, 7, 1, -8 }, // 0x0047 + { 181, 6, 8, 7, 1, -8 }, // 0x0048 + { 187, 5, 8, 7, 1, -8 }, // 0x0049 + { 192, 5, 8, 7, 1, -8 }, // 0x004A + { 197, 6, 8, 7, 1, -8 }, // 0x004B + { 203, 6, 8, 7, 1, -8 }, // 0x004C + { 209, 6, 8, 7, 1, -8 }, // 0x004D + { 215, 6, 8, 7, 1, -8 }, // 0x004E + { 221, 6, 8, 7, 1, -8 }, // 0x004F + { 227, 6, 8, 7, 1, -8 }, // 0x0050 + { 233, 6, 9, 7, 1, -8 }, // 0x0051 + { 240, 7, 8, 7, 1, -8 }, // 0x0052 + { 247, 6, 8, 7, 1, -8 }, // 0x0053 + { 253, 7, 8, 7, 0, -8 }, // 0x0054 + { 260, 6, 8, 7, 1, -8 }, // 0x0055 + { 266, 6, 8, 7, 1, -8 }, // 0x0056 + { 272, 7, 8, 7, 0, -8 }, // 0x0057 + { 279, 6, 8, 7, 1, -8 }, // 0x0058 + { 285, 7, 8, 7, 0, -8 }, // 0x0059 + { 292, 6, 8, 7, 1, -8 }, // 0x005A + { 298, 2, 10, 7, 2, -9 }, // 0x005B + { 301, 5, 9, 7, 1, -8 }, // 0x005C + { 307, 2, 10, 7, 2, -9 }, // 0x005D + { 310, 6, 3, 7, 0, -8 }, // 0x005E + { 313, 7, 1, 7, 0, 2 }, // 0x005F + { 314, 3, 2, 7, 2, -9 }, // 0x0060 + { 315, 5, 6, 7, 1, -6 }, // 0x0061 + { 319, 5, 9, 7, 1, -9 }, // 0x0062 + { 325, 5, 6, 7, 1, -6 }, // 0x0063 + { 329, 5, 9, 7, 1, -9 }, // 0x0064 + { 335, 5, 6, 7, 1, -6 }, // 0x0065 + { 339, 5, 9, 7, 1, -9 }, // 0x0066 + { 345, 5, 8, 7, 1, -6 }, // 0x0067 + { 350, 5, 9, 7, 1, -9 }, // 0x0068 + { 356, 5, 9, 7, 1, -9 }, // 0x0069 + { 362, 3, 11, 7, 1, -9 }, // 0x006A + { 367, 5, 9, 7, 1, -9 }, // 0x006B + { 373, 5, 9, 7, 0, -9 }, // 0x006C + { 379, 5, 6, 7, 1, -6 }, // 0x006D + { 383, 5, 6, 7, 1, -6 }, // 0x006E + { 387, 5, 6, 7, 1, -6 }, // 0x006F + { 391, 5, 8, 7, 1, -6 }, // 0x0070 + { 396, 5, 8, 7, 1, -6 }, // 0x0071 + { 401, 4, 6, 7, 2, -6 }, // 0x0072 + { 404, 5, 6, 7, 1, -6 }, // 0x0073 + { 408, 5, 8, 7, 0, -8 }, // 0x0074 + { 413, 5, 6, 7, 1, -6 }, // 0x0075 + { 417, 5, 6, 7, 1, -6 }, // 0x0076 + { 421, 7, 6, 7, 0, -6 }, // 0x0077 + { 427, 5, 6, 7, 1, -6 }, // 0x0078 + { 431, 5, 8, 7, 1, -6 }, // 0x0079 + { 436, 5, 6, 7, 1, -6 }, // 0x007A + { 440, 5, 10, 7, 1, -9 }, // 0x007B + { 447, 1, 11, 7, 3, -9 }, // 0x007C + { 449, 5, 10, 7, 1, -9 }, // 0x007D + { 456, 6, 2, 7, 1, -4 }, // 0x007E + { 458, 6, 10, 7, 1, -8 }, // 0x007F + { 466, 6, 10, 7, 1, -8 }, // 0x0080 + { 474, 6, 10, 7, 1, -8 }, // 0x0081 + { 482, 6, 10, 7, 1, -8 }, // 0x0082 + { 490, 6, 10, 7, 1, -8 }, // 0x0083 + { 498, 6, 10, 7, 1, -8 }, // 0x0084 + { 506, 6, 10, 7, 1, -8 }, // 0x0085 + { 514, 6, 10, 7, 1, -8 }, // 0x0086 + { 522, 6, 10, 7, 1, -8 }, // 0x0087 + { 530, 6, 10, 7, 1, -8 }, // 0x0088 + { 538, 6, 10, 7, 1, -8 }, // 0x0089 + { 546, 6, 10, 7, 1, -8 }, // 0x008A + { 554, 6, 10, 7, 1, -8 }, // 0x008B + { 562, 6, 10, 7, 1, -8 }, // 0x008C + { 570, 6, 10, 7, 1, -8 }, // 0x008D + { 578, 6, 10, 7, 1, -8 }, // 0x008E + { 586, 6, 10, 7, 1, -8 }, // 0x008F + { 594, 6, 10, 7, 1, -8 }, // 0x0090 + { 602, 6, 10, 7, 1, -8 }, // 0x0091 + { 610, 6, 10, 7, 1, -8 }, // 0x0092 + { 618, 6, 10, 7, 1, -8 }, // 0x0093 + { 626, 6, 10, 7, 1, -8 }, // 0x0094 + { 634, 6, 10, 7, 1, -8 }, // 0x0095 + { 642, 6, 10, 7, 1, -8 }, // 0x0096 + { 650, 6, 10, 7, 1, -8 }, // 0x0097 + { 658, 6, 10, 7, 1, -8 }, // 0x0098 + { 666, 6, 10, 7, 1, -8 }, // 0x0099 + { 674, 6, 10, 7, 1, -8 }, // 0x009A + { 682, 6, 10, 7, 1, -8 }, // 0x009B + { 690, 6, 10, 7, 1, -8 }, // 0x009C + { 698, 6, 10, 7, 1, -8 }, // 0x009D + { 706, 6, 10, 7, 1, -8 }, // 0x009E + { 714, 6, 10, 7, 1, -8 }, // 0x009F + { 722, 1, 1, 7, 0, -1 }, // 0x00A0 + { 723, 1, 8, 7, 3, -6 }, // 0x00A1 + { 724, 5, 10, 7, 1, -8 }, // 0x00A2 + { 731, 5, 8, 7, 1, -8 }, // 0x00A3 + { 736, 6, 6, 7, 1, -6 }, // 0x00A4 + { 741, 7, 8, 7, 0, -8 }, // 0x00A5 + { 748, 1, 9, 7, 3, -8 }, // 0x00A6 + { 750, 5, 9, 7, 1, -8 }, // 0x00A7 + { 756, 3, 1, 7, 2, -8 }, // 0x00A8 + { 757, 7, 7, 7, 0, -7 }, // 0x00A9 + { 764, 4, 6, 7, 1, -8 }, // 0x00AA + { 767, 6, 5, 7, 1, -6 }, // 0x00AB + { 771, 6, 3, 7, 1, -5 }, // 0x00AC + { 774, 3, 1, 7, 2, -4 }, // 0x00AD + { 775, 7, 7, 7, 0, -7 }, // 0x00AE + { 782, 3, 1, 7, 2, -8 }, // 0x00AF + { 783, 3, 3, 7, 2, -8 }, // 0x00B0 + { 785, 5, 7, 7, 1, -7 }, // 0x00B1 + { 790, 3, 4, 7, 2, -8 }, // 0x00B2 + { 792, 3, 4, 7, 2, -8 }, // 0x00B3 + { 794, 3, 2, 7, 3, -9 }, // 0x00B4 + { 795, 6, 8, 7, 1, -6 }, // 0x00B5 + { 801, 6, 9, 7, 1, -8 }, // 0x00B6 + { 808, 1, 2, 7, 3, -5 }, // 0x00B7 + { 809, 2, 2, 7, 2, 0 }, // 0x00B8 + { 810, 3, 4, 7, 2, -8 }, // 0x00B9 + { 812, 4, 6, 7, 1, -8 }, // 0x00BA + { 815, 6, 5, 7, 1, -6 }, // 0x00BB + { 819, 6, 10, 7, 0, -9 }, // 0x00BC + { 827, 6, 10, 7, 0, -9 }, // 0x00BD + { 835, 6, 10, 7, 0, -9 }, // 0x00BE + { 843, 4, 8, 7, 1, -6 }, // 0x00BF + { 847, 6, 11, 7, 1, -11 }, // 0x00C0 + { 856, 6, 11, 7, 1, -11 }, // 0x00C1 + { 865, 6, 11, 7, 1, -11 }, // 0x00C2 + { 874, 6, 11, 7, 1, -11 }, // 0x00C3 + { 883, 6, 10, 7, 1, -10 }, // 0x00C4 + { 891, 6, 10, 7, 1, -10 }, // 0x00C5 + { 899, 6, 8, 7, 0, -8 }, // 0x00C6 + { 905, 6, 10, 7, 1, -8 }, // 0x00C7 + { 913, 6, 11, 7, 1, -11 }, // 0x00C8 + { 922, 6, 11, 7, 1, -11 }, // 0x00C9 + { 931, 6, 11, 7, 1, -11 }, // 0x00CA + { 940, 6, 10, 7, 1, -10 }, // 0x00CB + { 948, 5, 11, 7, 1, -11 }, // 0x00CC + { 955, 5, 11, 7, 1, -11 }, // 0x00CD + { 962, 5, 11, 7, 1, -11 }, // 0x00CE + { 969, 5, 10, 7, 1, -10 }, // 0x00CF + { 976, 7, 8, 7, 0, -8 }, // 0x00D0 + { 983, 6, 11, 7, 1, -11 }, // 0x00D1 + { 992, 6, 11, 7, 1, -11 }, // 0x00D2 + { 1001, 6, 11, 7, 1, -11 }, // 0x00D3 + { 1010, 6, 11, 7, 1, -11 }, // 0x00D4 + { 1019, 6, 11, 7, 1, -11 }, // 0x00D5 + { 1028, 6, 10, 7, 1, -10 }, // 0x00D6 + { 1036, 5, 5, 7, 1, -6 }, // 0x00D7 + { 1040, 7, 8, 7, 0, -8 }, // 0x00D8 + { 1047, 6, 11, 7, 1, -11 }, // 0x00D9 + { 1056, 6, 11, 7, 1, -11 }, // 0x00DA + { 1065, 6, 11, 7, 1, -11 }, // 0x00DB + { 1074, 6, 10, 7, 1, -10 }, // 0x00DC + { 1082, 7, 11, 7, 0, -11 }, // 0x00DD + { 1092, 6, 8, 7, 1, -8 }, // 0x00DE + { 1098, 5, 9, 7, 1, -9 }, // 0x00DF + { 1104, 5, 9, 7, 1, -9 }, // 0x00E0 + { 1110, 5, 9, 7, 1, -9 }, // 0x00E1 + { 1116, 5, 9, 7, 1, -9 }, // 0x00E2 + { 1122, 5, 9, 7, 1, -9 }, // 0x00E3 + { 1128, 5, 8, 7, 1, -8 }, // 0x00E4 + { 1133, 5, 10, 7, 1, -10 }, // 0x00E5 + { 1140, 5, 6, 7, 1, -6 }, // 0x00E6 + { 1144, 5, 8, 7, 1, -6 }, // 0x00E7 + { 1149, 5, 9, 7, 1, -9 }, // 0x00E8 + { 1155, 5, 9, 7, 1, -9 }, // 0x00E9 + { 1161, 5, 9, 7, 1, -9 }, // 0x00EA + { 1167, 5, 8, 7, 1, -8 }, // 0x00EB + { 1172, 5, 9, 7, 1, -9 }, // 0x00EC + { 1178, 5, 9, 7, 1, -9 }, // 0x00ED + { 1184, 5, 9, 7, 1, -9 }, // 0x00EE + { 1190, 5, 8, 7, 1, -8 }, // 0x00EF + { 1195, 5, 9, 7, 1, -9 }, // 0x00F0 + { 1201, 5, 9, 7, 1, -9 }, // 0x00F1 + { 1207, 5, 9, 7, 1, -9 }, // 0x00F2 + { 1213, 5, 9, 7, 1, -9 }, // 0x00F3 + { 1219, 5, 9, 7, 1, -9 }, // 0x00F4 + { 1225, 5, 9, 7, 1, -9 }, // 0x00F5 + { 1231, 5, 8, 7, 1, -8 }, // 0x00F6 + { 1236, 5, 5, 7, 1, -6 }, // 0x00F7 + { 1240, 6, 6, 7, 0, -6 }, // 0x00F8 + { 1245, 5, 9, 7, 1, -9 }, // 0x00F9 + { 1251, 5, 9, 7, 1, -9 }, // 0x00FA + { 1257, 5, 9, 7, 1, -9 }, // 0x00FB + { 1263, 5, 8, 7, 1, -8 }, // 0x00FC + { 1268, 5, 11, 7, 1, -9 }, // 0x00FD + { 1275, 5, 11, 7, 1, -9 }, // 0x00FE + { 1282, 5, 10, 7, 1, -8 }, // 0x00FF + { 1289, 6, 10, 7, 1, -10 }, // 0x0100 + { 1297, 5, 8, 7, 1, -8 }, // 0x0101 + { 1302, 6, 11, 7, 1, -11 }, // 0x0102 + { 1311, 5, 9, 7, 1, -9 }, // 0x0103 + { 1317, 6, 10, 7, 1, -8 }, // 0x0104 + { 1325, 5, 8, 7, 1, -6 }, // 0x0105 + { 1330, 6, 11, 7, 1, -11 }, // 0x0106 + { 1339, 5, 9, 7, 1, -9 }, // 0x0107 + { 1345, 6, 11, 7, 1, -11 }, // 0x0108 + { 1354, 5, 9, 7, 1, -9 }, // 0x0109 + { 1360, 6, 10, 7, 1, -10 }, // 0x010A + { 1368, 5, 8, 7, 1, -8 }, // 0x010B + { 1373, 6, 11, 7, 1, -11 }, // 0x010C + { 1382, 5, 9, 7, 1, -9 }, // 0x010D + { 1388, 6, 11, 7, 1, -11 }, // 0x010E + { 1397, 7, 9, 7, 1, -9 }, // 0x010F + { 1405, 7, 8, 7, 0, -8 }, // 0x0110 + { 1412, 6, 9, 7, 1, -9 }, // 0x0111 + { 1419, 6, 10, 7, 1, -10 }, // 0x0112 + { 1427, 5, 8, 7, 1, -8 }, // 0x0113 + { 1432, 6, 11, 7, 1, -11 }, // 0x0114 + { 1441, 5, 9, 7, 1, -9 }, // 0x0115 + { 1447, 6, 10, 7, 1, -10 }, // 0x0116 + { 1455, 5, 8, 7, 1, -8 }, // 0x0117 + { 1460, 6, 10, 7, 1, -8 }, // 0x0118 + { 1468, 5, 8, 7, 1, -6 }, // 0x0119 + { 1473, 6, 11, 7, 1, -11 }, // 0x011A + { 1482, 5, 9, 7, 1, -9 }, // 0x011B + { 1488, 6, 11, 7, 1, -11 }, // 0x011C + { 1497, 5, 11, 7, 1, -9 }, // 0x011D + { 1504, 6, 11, 7, 1, -11 }, // 0x011E + { 1513, 5, 11, 7, 1, -9 }, // 0x011F + { 1520, 6, 10, 7, 1, -10 }, // 0x0120 + { 1528, 5, 10, 7, 1, -8 }, // 0x0121 + { 1535, 6, 11, 7, 1, -8 }, // 0x0122 + { 1544, 5, 11, 7, 1, -9 }, // 0x0123 + { 1551, 6, 11, 7, 1, -11 }, // 0x0124 + { 1560, 5, 12, 7, 1, -12 }, // 0x0125 + { 1568, 8, 8, 7, 0, -8 }, // 0x0126 + { 1576, 6, 9, 7, 0, -9 }, // 0x0127 + { 1583, 5, 11, 7, 1, -11 }, // 0x0128 + { 1590, 5, 9, 7, 1, -9 }, // 0x0129 + { 1596, 5, 10, 7, 1, -10 }, // 0x012A + { 1603, 5, 8, 7, 1, -8 }, // 0x012B + { 1608, 5, 11, 7, 1, -11 }, // 0x012C + { 1615, 5, 9, 7, 1, -9 }, // 0x012D + { 1621, 5, 10, 7, 1, -8 }, // 0x012E + { 1628, 5, 11, 7, 1, -9 }, // 0x012F + { 1635, 5, 10, 7, 1, -10 }, // 0x0130 + { 1642, 5, 6, 7, 1, -6 }, // 0x0131 + { 1646, 7, 8, 7, 0, -8 }, // 0x0132 + { 1653, 6, 11, 7, 0, -9 }, // 0x0133 + { 1662, 5, 11, 7, 1, -11 }, // 0x0134 + { 1669, 4, 11, 7, 1, -9 }, // 0x0135 + { 1675, 6, 11, 7, 1, -8 }, // 0x0136 + { 1684, 5, 12, 7, 1, -9 }, // 0x0137 + { 1692, 5, 6, 7, 1, -6 }, // 0x0138 + { 1696, 6, 11, 7, 1, -11 }, // 0x0139 + { 1705, 5, 12, 7, 0, -12 }, // 0x013A + { 1713, 6, 11, 7, 1, -8 }, // 0x013B + { 1722, 5, 12, 7, 0, -9 }, // 0x013C + { 1730, 6, 8, 7, 1, -8 }, // 0x013D + { 1736, 6, 9, 7, 0, -9 }, // 0x013E + { 1743, 6, 8, 7, 1, -8 }, // 0x013F + { 1749, 7, 9, 7, 0, -9 }, // 0x0140 + { 1757, 7, 8, 7, 0, -8 }, // 0x0141 + { 1764, 5, 9, 7, 0, -9 }, // 0x0142 + { 1770, 6, 11, 7, 1, -11 }, // 0x0143 + { 1779, 5, 9, 7, 1, -9 }, // 0x0144 + { 1785, 6, 11, 7, 1, -8 }, // 0x0145 + { 1794, 5, 9, 7, 1, -6 }, // 0x0146 + { 1800, 6, 11, 7, 1, -11 }, // 0x0147 + { 1809, 5, 9, 7, 1, -9 }, // 0x0148 + { 1815, 7, 9, 7, 0, -9 }, // 0x0149 + { 1823, 6, 10, 7, 1, -8 }, // 0x014A + { 1831, 5, 8, 7, 1, -6 }, // 0x014B + { 1836, 6, 10, 7, 1, -10 }, // 0x014C + { 1844, 5, 8, 7, 1, -8 }, // 0x014D + { 1849, 6, 11, 7, 1, -11 }, // 0x014E + { 1858, 5, 9, 7, 1, -9 }, // 0x014F + { 1864, 6, 10, 7, 1, -10 }, // 0x0150 + { 1872, 5, 9, 7, 1, -9 }, // 0x0151 + { 1878, 6, 8, 7, 1, -8 }, // 0x0152 + { 1884, 5, 6, 7, 1, -6 }, // 0x0153 + { 1888, 7, 11, 7, 1, -11 }, // 0x0154 + { 1898, 5, 9, 7, 2, -9 }, // 0x0155 + { 1904, 7, 11, 7, 1, -8 }, // 0x0156 + { 1914, 5, 9, 7, 1, -6 }, // 0x0157 + { 1920, 7, 11, 7, 1, -11 }, // 0x0158 + { 1930, 4, 9, 7, 2, -9 }, // 0x0159 + { 1935, 6, 11, 7, 1, -11 }, // 0x015A + { 1944, 5, 9, 7, 1, -9 }, // 0x015B + { 1950, 6, 11, 7, 1, -11 }, // 0x015C + { 1959, 5, 9, 7, 1, -9 }, // 0x015D + { 1965, 6, 10, 7, 1, -8 }, // 0x015E + { 1973, 5, 8, 7, 1, -6 }, // 0x015F +}; + +const GFXfont UiFont11pt PROGMEM = { + (uint8_t *)UiFont11ptBitmaps, + (GFXglyph *)UiFont11ptGlyphs, + 0x0020, 0x015F, 17 +}; diff --git a/src/units_screen.cpp b/src/units_screen.cpp index 117c8f1..d7cda71 100644 --- a/src/units_screen.cpp +++ b/src/units_screen.cpp @@ -14,11 +14,13 @@ namespace { } }; - void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { + void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool active = false) { + uint16_t bg = active ? TFT_GREEN : TFT_BLACK; + uint16_t fg = active ? TFT_BLACK : TFT_GREEN; tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); - tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, bg); + tft.setTextColor(fg, bg); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.setTextDatum(TL_DATUM); } @@ -40,7 +42,7 @@ void run(TFT_eSPI& tft) { bool done = false; while (!done) { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 10); tft.println(I18n::t(StringId::UNITS_TITLE)); @@ -49,8 +51,7 @@ void run(TFT_eSPI& tft) { for (uint8_t i = 0; i < MODE_COUNT; i++) { modeRects[i] = rowRect(i); - drawButton(tft, modeRects[i], I18n::t(labels[i]), - (i == current) ? TFT_DARKGREEN : TFT_NAVY); + drawButton(tft, modeRects[i], I18n::t(labels[i]), i == current); } Rect backBtn = rowRect(MODE_COUNT); diff --git a/src/wifi_manage_screen.cpp b/src/wifi_manage_screen.cpp index 0b9ad61..9d46d79 100644 --- a/src/wifi_manage_screen.cpp +++ b/src/wifi_manage_screen.cpp @@ -3,6 +3,7 @@ #include "wifi_setup_screen.h" #include "touch_input.h" #include "config.h" +#include "i18n.h" namespace WifiManageScreen { @@ -14,11 +15,12 @@ namespace { } }; - void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { - tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); - tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool danger = false) { + uint16_t accent = danger ? TFT_RED : TFT_GREEN; + tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK); + tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, bg); + tft.setTextColor(accent, TFT_BLACK); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.setTextDatum(TL_DATUM); } @@ -32,9 +34,9 @@ void run(TFT_eSPI& tft) { bool done = false; while (!done) { tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 10); - tft.println("WiFi networks"); + tft.println(I18n::t(StringId::WIFI_NETWORKS_TITLE)); uint8_t count = WifiMgr::networkCount(); int16_t y = 40; @@ -50,13 +52,13 @@ void run(TFT_eSPI& tft) { if (i < count) { drawButton(tft, rowRect, WifiMgr::networkSsid(i)); - drawButton(tft, removeRect, "X", TFT_MAROON); + drawButton(tft, removeRect, "X", true); } else { tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_BLACK); - tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREY); + tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREEN); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_DARKGREY, TFT_BLACK); - tft.drawString("(empty)", rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2); + tft.setTextColor(TFT_DARKGREEN, TFT_BLACK); + tft.drawString(I18n::t(StringId::WIFI_EMPTY_SLOT), rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2); tft.setTextDatum(TL_DATUM); } y += ROW_H + ROW_GAP; @@ -65,12 +67,12 @@ void run(TFT_eSPI& tft) { Rect addBtn = {10, y, (int16_t)(Config::SCREEN_WIDTH - 20), 40}; bool canAdd = count < Config::MAX_WIFI_NETWORKS; if (canAdd) { - drawButton(tft, addBtn, "Add network"); + drawButton(tft, addBtn, I18n::t(StringId::WIFI_ADD_NETWORK)); } y += 48; Rect backBtn = {10, (int16_t)(Config::SCREEN_HEIGHT - 50), (int16_t)(Config::SCREEN_WIDTH - 20), 40}; - drawButton(tft, backBtn, "Back"); + drawButton(tft, backBtn, I18n::t(StringId::BACK)); TouchInput::Point tap; while (true) { diff --git a/src/wifi_setup_screen.cpp b/src/wifi_setup_screen.cpp index 3a67237..d7b74e4 100644 --- a/src/wifi_setup_screen.cpp +++ b/src/wifi_setup_screen.cpp @@ -78,10 +78,6 @@ namespace { char passwordBuf[64] = {0}; uint8_t passwordLen = 0; - // Shift-Verhalten wie am Handy: einmal antippen = NUR der naechste - // Buchstabe wird gross geschrieben, danach automatisch wieder klein. - // Zweimal schnell hintereinander antippen (Doppeltipp) = Feststelltaste - // (bleibt an, bis erneut einmal angetippt wird). enum class ShiftState { Off, OneShot, Locked }; ShiftState shiftState = ShiftState::Off; uint32_t lastShiftTapMs = 0; @@ -95,11 +91,12 @@ namespace { Rect cancelBtn = {Config::SCREEN_WIDTH - 34, 4, 30, 24}; void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool highlighted = false) { - uint16_t bg = highlighted ? TFT_DARKGREEN : TFT_NAVY; + uint16_t bg = highlighted ? TFT_GREEN : TFT_BLACK; + uint16_t fg = highlighted ? TFT_BLACK : TFT_GREEN; tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); - tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); + tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN); tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, bg); + tft.setTextColor(fg, bg); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.setTextDatum(TL_DATUM); } @@ -134,9 +131,6 @@ namespace { bool upper = (shiftState != ShiftState::Off); passwordBuf[passwordLen++] = upper ? (char)toupper(k.ch) : k.ch; passwordBuf[passwordLen] = 0; - // Nach genau einem Buchstaben automatisch wieder auf - // klein zurueckfallen - AUSSER die Feststelltaste - // (Doppeltipp) ist aktiv, die bleibt an. if (shiftState == ShiftState::OneShot) shiftState = ShiftState::Off; } break; @@ -145,13 +139,10 @@ namespace { bool isDoubleTap = (nowMs - lastShiftTapMs) <= SHIFT_DOUBLE_TAP_MS; lastShiftTapMs = nowMs; if (isDoubleTap) { - // Doppeltipp: Feststelltaste an-/ausschalten. shiftState = (shiftState == ShiftState::Locked) ? ShiftState::Off : ShiftState::Locked; } else if (shiftState == ShiftState::Off) { - // Einfacher Tipp: nur der naechste Buchstabe wird gross. shiftState = ShiftState::OneShot; } else { - // Einfacher Tipp waehrend OneShot/Locked schaltet ab. shiftState = ShiftState::Off; } break; @@ -208,7 +199,7 @@ bool run(TFT_eSPI& tft) { WifiMgr::beginScan(); tft.fillScreen(TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setTextSize(1); tft.setCursor(10, 10); tft.println(I18n::t(StringId::WIFI_SCANNING)); @@ -231,7 +222,7 @@ bool run(TFT_eSPI& tft) { tft.fillScreen(TFT_BLACK); tft.setCursor(10, 10); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.println(ssidCount == 0 ? I18n::t(StringId::WIFI_NO_NETWORKS) : I18n::t(StringId::WIFI_SELECT)); drawCancelButton(tft); } @@ -244,7 +235,7 @@ bool run(TFT_eSPI& tft) { stage = Stage::Done; tft.fillScreen(TFT_BLACK); tft.setCursor(10, 10); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.println(I18n::t(StringId::WIFI_CONNECTED_BANG)); delay(900); return true; @@ -254,7 +245,7 @@ bool run(TFT_eSPI& tft) { tft.setCursor(10, 10); tft.setTextColor(TFT_RED, TFT_BLACK); tft.println(I18n::t(StringId::WIFI_CONNECTION_FAILED)); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 30); tft.println(I18n::t(StringId::WIFI_BACK_TO_LIST_MSG)); delay(1400); @@ -319,7 +310,7 @@ bool run(TFT_eSPI& tft) { tft.setCursor(10, 10); tft.setTextColor(TFT_RED, TFT_BLACK); tft.println(I18n::t(StringId::WIFI_ALREADY_3)); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 30); tft.println(I18n::t(StringId::WIFI_REMOVE_ONE_FIRST)); delay(1600); @@ -330,7 +321,7 @@ bool run(TFT_eSPI& tft) { stage = Stage::Connecting; tft.fillScreen(TFT_BLACK); tft.setCursor(10, 10); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.println(I18n::t(StringId::WIFI_CONNECTING)); } else if (backBtn.contains(tap.x, tap.y)) { stage = Stage::PickSsid; @@ -363,16 +354,16 @@ bool run(TFT_eSPI& tft) { drawCancelButton(tft); } else if (stage == Stage::EnterPassword) { tft.fillRect(0, 0, Config::SCREEN_WIDTH, KB_TOP - 4, TFT_BLACK); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 6); tft.printf("%s%s", I18n::t(StringId::WIFI_LABEL_PREFIX), ssidList[selectedIndex].c_str()); tft.setCursor(10, 22); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.println(I18n::t(StringId::WIFI_PASSWORD_LABEL)); - tft.fillRect(8, 38, Config::SCREEN_WIDTH - 16, 22, TFT_NAVY); - tft.drawRect(8, 38, Config::SCREEN_WIDTH - 16, 22, TFT_DARKGREY); + tft.fillRect(8, 38, Config::SCREEN_WIDTH - 16, 22, TFT_BLACK); + tft.drawRect(8, 38, Config::SCREEN_WIDTH - 16, 22, TFT_GREEN); tft.setCursor(12, 44); - tft.setTextColor(TFT_WHITE, TFT_NAVY); + tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.print(passwordBuf); int16_t rowY0 = KB_TOP;