localization
This commit is contained in:
@@ -35,18 +35,17 @@ namespace {
|
||||
}
|
||||
};
|
||||
|
||||
// ROW_GAP/ROW_START_Y bleiben unveraendert (werden von flightRowRect()
|
||||
// weiter unten mitbenutzt, siehe FLIGHT_ROW_H).
|
||||
// ROW_GAP/ROW_START_Y remain unchanged (also used by flightRowRect()
|
||||
// below, see FLIGHT_ROW_H).
|
||||
constexpr int16_t ROW_GAP = 1;
|
||||
constexpr int16_t ROW_START_Y = 18;
|
||||
|
||||
// Region-Unterseite (Sprache/Einheiten/Zurueck, nur 3 Eintraege):
|
||||
// Zeilenhoehe/-abstand werden aus der tatsaechlich verfuegbaren
|
||||
// Bildschirmflaeche errechnet (gleiches Muster wie bei SYSTEM_ROW_H
|
||||
// weiter unten), statt die kleine, fuer volle Seiten (z.B.
|
||||
// Flugoptionen: 14 Eintraege) gedachte feste Hoehe (vorher 22px) zu
|
||||
// benutzen - die liess bei nur 3 Eintraegen fast den ganzen Bildschirm
|
||||
// leer und machte die Buttons winzig und schwer zu treffen.
|
||||
// Region sub-page (Language/Units/Back, only 3 entries): row
|
||||
// height/spacing calculated from actual available screen area (same
|
||||
// pattern as SYSTEM_ROW_H below), instead of the small fixed height
|
||||
// (formerly 22px) intended for full pages (e.g. Flight Options: 14
|
||||
// entries) - that left nearly the whole screen empty with only 3
|
||||
// entries and made buttons tiny and hard to hit.
|
||||
constexpr uint8_t REGION_ROW_COUNT = 3;
|
||||
constexpr int16_t REGION_ROW_GAP = 10;
|
||||
constexpr int16_t REGION_END_Y = Config::SCREEN_HEIGHT - 10;
|
||||
@@ -67,33 +66,31 @@ namespace {
|
||||
(int16_t)(Config::SCREEN_WIDTH - 20), CAT_ROW_H};
|
||||
}
|
||||
|
||||
// Eigene, etwas kompaktere Zeilenhoehe NUR fuer die Flugoptionen-Seite:
|
||||
// die normale rowRect()-Hoehe (22px) liess bei 13 Eintraegen schon keinen
|
||||
// Platz mehr fuer einen weiteren - andere Seiten (Region/System) bleiben
|
||||
// bei der normalen Hoehe unveraendert, um dort nichts zu verschieben.
|
||||
// Custom, slightly more compact row height ONLY for the Flight Options
|
||||
// page: the normal rowRect() height (22px) already left no room for
|
||||
// another entry with 13 entries - other pages (Region/System) keep
|
||||
// their normal height unchanged to avoid shifting anything there.
|
||||
constexpr int16_t FLIGHT_ROW_H = 19;
|
||||
Rect flightRowRect(uint8_t index) {
|
||||
return {10, (int16_t)(ROW_START_Y + index * (FLIGHT_ROW_H + ROW_GAP)),
|
||||
(int16_t)(Config::SCREEN_WIDTH - 20), FLIGHT_ROW_H};
|
||||
}
|
||||
|
||||
// Eigene, groesser berechnete Zeilenhoehe NUR fuer die System-Seite: mit
|
||||
// nur 10 Eintraegen (vs. z.B. 14 auf der Flugoptionen-Seite) liess die
|
||||
// normale rowRect()-Hoehe (22px) unten viel ungenutzten Platz frei. Die
|
||||
// Hoehe wird hier stattdessen aus der tatsaechlich verfuegbaren
|
||||
// Bildschirmflaeche errechnet, sodass die Buttons den Platz bis knapp
|
||||
// ueber den Bildschirmrand ausfuellen - andere Seiten bleiben bei ihren
|
||||
// eigenen, unveraenderten Zeilenhoehen.
|
||||
// War 10, bevor "Sicherung"/"Wiederherstellen" in die neue
|
||||
// "Sicherung & Reset"-Unterseite ausgelagert wurden (siehe
|
||||
// BACKUP_RESET_ROW_COUNT unten) und dort durch einen einzelnen
|
||||
// Ordner-Button ersetzt wurden: -2 (Backup/Restore raus) +1 (neuer
|
||||
// Ordner-Button) = 9. Danach +1 fuer den neuen "Nach Update
|
||||
// suchen"-Punkt (OTA-Update, siehe ota_update.h) = 10. Der
|
||||
// Ruhebildschirm-Umschalter, der zwischenzeitlich hier eine eigene
|
||||
// Zeile hatte, ist in den neuen Bildschirm-Timeout-Screen umgezogen
|
||||
// (siehe timeout_screen.cpp) - Zeilenzahl bleibt dadurch bei 10.
|
||||
constexpr uint8_t SYSTEM_ROW_COUNT = 11; // +1 fuer Eco-Mode
|
||||
// Custom, larger calculated row height ONLY for the System page: with
|
||||
// only 10 entries (vs. e.g. 14 on the Flight Options page), the
|
||||
// normal rowRect() height (22px) left a lot of unused space at the
|
||||
// bottom. Height is instead calculated from actual available screen
|
||||
// area so buttons fill the space down to just above the screen
|
||||
// edge - other pages keep their own unchanged row heights.
|
||||
// Was 10 before "Backup"/"Restore" were moved to the new
|
||||
// "Backup & Reset" sub-page (see BACKUP_RESET_ROW_COUNT below) and
|
||||
// replaced there by a single folder button: -2 (Backup/Restore
|
||||
// removed) +1 (new folder button) = 9. Then +1 for the new
|
||||
// "Check for update" item (OTA update, see ota_update.h) = 10. The
|
||||
// screensaver toggle, which temporarily had its own row here, moved
|
||||
// to the new screen timeout screen (see timeout_screen.cpp) - row
|
||||
// count therefore stays at 10.
|
||||
constexpr uint8_t SYSTEM_ROW_COUNT = 11; // +1 for Eco-Mode
|
||||
constexpr int16_t SYSTEM_ROW_GAP = 4;
|
||||
constexpr int16_t SYSTEM_START_Y = 18;
|
||||
constexpr int16_t SYSTEM_END_Y = Config::SCREEN_HEIGHT - 10;
|
||||
@@ -104,11 +101,11 @@ namespace {
|
||||
(int16_t)(Config::SCREEN_WIDTH - 20), SYSTEM_ROW_H};
|
||||
}
|
||||
|
||||
// Eigene Zeilenhoehe fuer die neue "Sicherung & Reset"-Unterseite (4
|
||||
// Eintraege: Sichern/Wiederherstellen/Zuruecksetzen/Zurueck) - gleiches
|
||||
// "aus verfuegbarem Platz errechnen"-Muster wie bei SYSTEM_ROW_H.
|
||||
// ROW_START_Y wird mitbenutzt (siehe oben, gemeinsam mit rowRect()/
|
||||
// flightRowRect()), nur GAP/COUNT/H sind eigene Werte.
|
||||
// Custom row height for the new "Backup & Reset" sub-page (4
|
||||
// entries: Backup/Restore/Factory Reset/Back) - same "calculate from
|
||||
// available space" pattern as SYSTEM_ROW_H.
|
||||
// ROW_START_Y is reused (see above, shared with rowRect()/
|
||||
// flightRowRect()), only GAP/COUNT/H are custom values.
|
||||
constexpr uint8_t BACKUP_RESET_ROW_COUNT = 4;
|
||||
constexpr int16_t BACKUP_RESET_ROW_GAP = 10;
|
||||
constexpr int16_t BACKUP_RESET_END_Y = Config::SCREEN_HEIGHT - 10;
|
||||
@@ -134,14 +131,15 @@ namespace {
|
||||
|
||||
String onOff(bool on) { return I18n::t(on ? StringId::ON : StringId::OFF); }
|
||||
|
||||
// Fortschrittspunkte-Anzeige waehrend SettingsBackup::backup()/restore()
|
||||
// laufen (siehe Aufrufe unten in Page::BackupReset) - diese sind
|
||||
// synchrone, SD-lastige Vorgaenge, die spuerbar dauern koennen und den
|
||||
// Button vorher wie eingefroren wirken liessen. SettingsBackup ruft den
|
||||
// hier uebergebenen Funktionszeiger vor jedem der beiden Kopiervorgaenge
|
||||
// (erst Einstellungen, dann WLAN) auf. Namespace-globale Zeiger/Variablen
|
||||
// statt Lambda-Capture, da ein einfacher C-Funktionszeiger uebergeben
|
||||
// werden muss (kein std::function im Projekt).
|
||||
// Progress dots display while SettingsBackup::backup()/restore()
|
||||
// run (see calls below in Page::BackupReset) - these are
|
||||
// synchronous, SD-intensive operations that can take noticeable
|
||||
// time and previously made the button appear frozen.
|
||||
// SettingsBackup calls the function pointer passed here before each
|
||||
// of the two copy operations (first settings, then WiFi).
|
||||
// Namespace-global pointers/variables instead of lambda capture,
|
||||
// because a plain C function pointer must be passed (no
|
||||
// std::function in the project).
|
||||
TFT_eSPI* progressTft = nullptr;
|
||||
Rect progressBtnRect;
|
||||
String progressLabel;
|
||||
@@ -174,11 +172,11 @@ namespace {
|
||||
delay(1200);
|
||||
}
|
||||
|
||||
// Wie die einfache layoutWrapped()-Variante, aber mit optionalem
|
||||
// Scroll-Offset und Sichtfenster (scrollY/viewTop/viewBottom) - Zeilen
|
||||
// ausserhalb des Sichtfensters werden uebersprungen. Mit draw=false wird
|
||||
// nur die Gesamthoehe berechnet, ohne etwas zu zeichnen (fuer die
|
||||
// Scroll-Bedarfspruefung vorab).
|
||||
// Like the simple layoutWrapped() variant, but with optional scroll
|
||||
// offset and viewport (scrollY/viewTop/viewBottom) - lines outside
|
||||
// the viewport are skipped. With draw=false, only the total height
|
||||
// is calculated without drawing anything (for pre-checking scroll
|
||||
// need).
|
||||
int16_t layoutWrapped(TFT_eSPI& tft, int16_t x, int16_t startY, int16_t maxWidth,
|
||||
int16_t lineHeight, const String& text, int16_t scrollY,
|
||||
int16_t viewTop, int16_t viewBottom, bool draw) {
|
||||
@@ -209,10 +207,10 @@ namespace {
|
||||
return y;
|
||||
}
|
||||
|
||||
// Zerlegt einen Titel in bis zu maxLines Zeilen nach dem gleichen
|
||||
// Wortumbruch-Prinzip wie layoutWrapped() - ein Titel wird aber nie
|
||||
// gescrollt, sondern muss immer komplett sichtbar sein, daher eine
|
||||
// eigene, einfachere Variante ohne Scroll-Unterstuetzung.
|
||||
// Splits a title into up to maxLines lines using the same word-wrapping
|
||||
// principle as layoutWrapped() - but a title is never scrolled and must
|
||||
// always be fully visible, hence a separate, simpler variant without
|
||||
// scroll support.
|
||||
int wrapTitleLines(TFT_eSPI& tft, const String& text, int16_t maxWidth, String* outLines, int maxLines) {
|
||||
int count = 0;
|
||||
int32_t start = 0;
|
||||
@@ -240,29 +238,27 @@ namespace {
|
||||
return count;
|
||||
}
|
||||
|
||||
// Warn-Ueberlage, die praktisch den kompletten Bildschirm einnimmt (nur
|
||||
// ein paar Pixel Rand) - urspruenglich nur fuers Einschalten des
|
||||
// Flugbuchs gebaut (erklaert, warum es sich nach 24h automatisch
|
||||
// wieder abschaltet), jetzt generisch mit uebergebenen Titel-/Text-
|
||||
// StringIds, damit sie auch fuer die "Einstellungen zuruecksetzen"-
|
||||
// Bestaetigung (Werksreset) wiederverwendet werden kann - beides sind
|
||||
// seltene, potenziell folgenreiche Aktionen, die dieselbe deutliche
|
||||
// Warnung verdienen. "Achtung!!!" (titleId) steht ganz oben, mit einer
|
||||
// Leerzeile Abstand zum Fliesstext (bodyId) darunter; der Text
|
||||
// scrollt bei Bedarf (laengere Uebersetzungen) ueber eigene Pfeil-
|
||||
// Buttons, OK/Zurueck bleiben dabei immer unten fix und
|
||||
// kollisionsfrei sichtbar. Sternchen laufen im Hintergrund mit, wie auf
|
||||
// allen anderen Menue-Screens (nur der Radar-Screen selbst spart sich
|
||||
// das wegen der CPU-Last durch Abfragen/Zeichnen). Gibt true zurueck,
|
||||
// wenn "OK" angetippt wurde, false bei "Zurueck".
|
||||
// Warning overlay that covers nearly the entire screen (just a few
|
||||
// pixels margin) - originally built only for enabling the flight
|
||||
// logbook (explains why it auto-disables after 24h), now generic
|
||||
// with passed-in title/text to also be reusable for the "Reset
|
||||
// settings" confirmation (factory reset) - both are rare, potentially
|
||||
// consequential actions that deserve the same clear warning.
|
||||
// "Attention!!!" (titleId) appears at the top, with a blank line
|
||||
// before the body text (bodyId) below; the text scrolls if needed
|
||||
// (longer translations) via dedicated arrow buttons, OK/Back always
|
||||
// stay fixed at the bottom and collision-free visible. Stars run in
|
||||
// the background, as on all other menu screens (only the radar
|
||||
// screen skips them due to CPU load from polling/drawing). Returns
|
||||
// true if "OK" was tapped, false for "Back".
|
||||
//
|
||||
// title/body stehen VOR dem Aufruf per I18n::t() fest (statt StringIds
|
||||
// entgegenzunehmen), damit auch dynamisch zusammengesetzte Texte (z.B.
|
||||
// mit eingefuegter Versionsnummer bei OTA) moeglich sind. accentColor
|
||||
// (Default TFT_RED fuer die beiden bestehenden, wirklich destruktiven
|
||||
// Aufrufer) faerbt Rahmen und Titel - der OTA-Aufrufer uebergibt
|
||||
// TFT_GREEN, da ein Update zwar bestaetigt werden sollte, aber keine
|
||||
// "gefaehrliche" Loesch-Aktion wie Werksreset/Flugbuch ist.
|
||||
// title/body are fixed BEFORE the call via I18n::t() (instead of
|
||||
// accepting StringIds), so dynamically composed texts (e.g. with
|
||||
// inserted version number for OTA) are possible. accentColor
|
||||
// (default TFT_RED for the two existing, truly destructive callers)
|
||||
// colors frame and title - the OTA caller passes TFT_GREEN, since an
|
||||
// update should be confirmed but is not a "dangerous" deletion
|
||||
// action like factory reset/flight logbook.
|
||||
bool confirmWarningScreen(TFT_eSPI& tft, const String& title, const String& body, uint16_t accentColor = TFT_RED) {
|
||||
constexpr int16_t BOX_X = 4;
|
||||
constexpr int16_t BOX_Y = 4;
|
||||
@@ -272,11 +268,11 @@ namespace {
|
||||
constexpr int16_t LINE_H = 16;
|
||||
constexpr int16_t TITLE_Y = BOX_Y + 16;
|
||||
|
||||
// Titel-Text vorab in so viele Zeilen umbrechen, wie bei Größe 2
|
||||
// (oder bei zu langem Text Größe 1) nötig sind - siehe
|
||||
// wrapTitleLines() oben. VIEW_TOP hängt dadurch von der
|
||||
// tatsächlichen Zeilenzahl des Titels ab, ist also kein constexpr
|
||||
// mehr wie vorher (wo immer nur eine Titelzeile angenommen wurde).
|
||||
// Wrap title text into as many lines as needed at size 2 (or size 1
|
||||
// if text is too long) - see wrapTitleLines() above. VIEW_TOP
|
||||
// therefore depends on the actual number of title lines, so it is
|
||||
// no longer constexpr as before (where only one title line was
|
||||
// assumed).
|
||||
tft.setTextSize(2);
|
||||
uint8_t titleTextSize = 2;
|
||||
if (tft.textWidth(title) > TEXT_MAX_WIDTH) {
|
||||
@@ -286,13 +282,13 @@ namespace {
|
||||
constexpr int MAX_TITLE_LINES = 3;
|
||||
String titleLines[MAX_TITLE_LINES];
|
||||
int titleLineCount = wrapTitleLines(tft, title, TEXT_MAX_WIDTH, titleLines, MAX_TITLE_LINES);
|
||||
// Fließtext wird immer bei Größe 1 vermessen/gezeichnet (siehe
|
||||
// layoutWrapped()-Aufrufe unten) - Größe hier zurücksetzen, falls
|
||||
// obiger Titel-Breitentest sie auf 2 stehen gelassen hat, sonst
|
||||
// würde die gleich folgende totalH-Berechnung (vor dem ersten
|
||||
// redraw()) mit falscher (zu breiter) Schriftgröße rechnen.
|
||||
// Body text is always measured/drawn at size 1 (see
|
||||
// layoutWrapped() calls below) - reset size here in case the
|
||||
// title width check above left it at 2, otherwise the
|
||||
// immediately following totalH calculation (before the first
|
||||
// redraw()) would use the wrong (too wide) font size.
|
||||
tft.setTextSize(1);
|
||||
// Eine Leerzeile Abstand zwischen Titel und Fließtext.
|
||||
// One blank line between title and body text.
|
||||
int16_t VIEW_TOP = (int16_t)(TITLE_Y + titleLineCount * LINE_H + 12);
|
||||
|
||||
constexpr int16_t BTN_H = 36;
|
||||
@@ -303,9 +299,9 @@ namespace {
|
||||
constexpr int16_t SCROLL_ROW_H = 28;
|
||||
constexpr int16_t SCROLL_ROW_GAP = 8;
|
||||
|
||||
// Ohne Scroll-Pfeile verfuegbare Texthoehe zuerst pruefen - nur wenn
|
||||
// der Text da nicht reinpasst, wird zusaetzlich Platz fuer die
|
||||
// Pfeile reserviert (mehr Text-Platz bei kurzen Uebersetzungen).
|
||||
// First check available text height without scroll arrows - only if
|
||||
// the text does not fit, additional space for arrows is reserved
|
||||
// (more text space for short translations).
|
||||
constexpr int16_t VIEW_BOTTOM_NO_SCROLL = OK_Y - 8;
|
||||
constexpr int16_t VIEW_BOTTOM_SCROLL = VIEW_BOTTOM_NO_SCROLL - SCROLL_ROW_H - SCROLL_ROW_GAP;
|
||||
|
||||
@@ -332,13 +328,12 @@ namespace {
|
||||
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.setTextColor(accentColor, TFT_BLACK);
|
||||
// Statische Titel ("Achtung!!!") sind kurz genug fuer Groesse 2,
|
||||
// aber der OTA-Aufrufer baut den Titel dynamisch mit
|
||||
// Versionsnummer zusammen (z.B. "Update verfuegbar: v3.5.0") -
|
||||
// das passt bei Groesse 2 nicht mehr in die Box und lief vorher
|
||||
// links/rechts ueber den Bildschirmrand hinaus. Deshalb hier
|
||||
// die Breite bei Groesse 2 pruefen und bei Bedarf auf Groesse 1
|
||||
// zurueckfallen, statt eine feste Groesse anzunehmen.
|
||||
// Static titles ("Attention!!!") are short enough for size 2, but
|
||||
// the OTA caller builds the title dynamically with a version
|
||||
// number (e.g. "Update available: v3.5.0") - this no longer
|
||||
// fits in the box at size 2 and previously overflowed left/right
|
||||
// beyond the screen edge. Hence check width at size 2 and fall
|
||||
// back to size 1 if needed, instead of assuming a fixed size.
|
||||
tft.setTextSize(titleTextSize);
|
||||
for (int i = 0; i < titleLineCount; i++) {
|
||||
tft.drawString(titleLines[i], BOX_X + BOX_W / 2, (int16_t)(TITLE_Y + i * LINE_H));
|
||||
@@ -379,16 +374,15 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// Einfacher Info-Screen mit nur EINEM Button (kein Abbrechen) - fuer
|
||||
// Endzustaende, bei denen es nichts mehr zu entscheiden gibt, nur zu
|
||||
// bestaetigen (z.B. Ergebnis eines OTA-Updates). Anders als
|
||||
// showBriefMessage() (kurze Meldung unten am Bildschirmrand,
|
||||
// verschwindet nach 1,2s automatisch von selbst) bleibt dieser Screen
|
||||
// stehen, bis aktiv bestaetigt wird - wichtig bei sicherheitsrelevanten
|
||||
// Meldungen wie einem fehlgeschlagenen oder erfolgreichen Firmware-
|
||||
// Update, die der Nutzer auf keinen Fall verpassen darf. Gleicher
|
||||
// Kasten-/Scroll-Aufbau wie confirmWarningScreen(), nur mit einem
|
||||
// einzigen, ueber die volle Breite gehenden Button statt OK/Zurueck.
|
||||
// Simple info screen with only ONE button (no cancel) - for
|
||||
// terminal states where there is nothing left to decide, only to
|
||||
// acknowledge (e.g. result of an OTA update). Unlike
|
||||
// showBriefMessage() (short message at the bottom of the screen,
|
||||
// auto-disappears after 1.2s), this screen stays until actively
|
||||
// acknowledged - important for safety-relevant messages like a
|
||||
// failed or successful firmware update that the user must not miss.
|
||||
// Same box/scroll layout as confirmWarningScreen(), but with a
|
||||
// single full-width button instead of OK/Back.
|
||||
void infoScreen(TFT_eSPI& tft, const String& title, const String& body, uint16_t accentColor, const String& buttonLabel) {
|
||||
constexpr int16_t BOX_X = 4;
|
||||
constexpr int16_t BOX_Y = 4;
|
||||
@@ -398,11 +392,11 @@ namespace {
|
||||
constexpr int16_t LINE_H = 16;
|
||||
constexpr int16_t TITLE_Y = BOX_Y + 16;
|
||||
|
||||
// Titel-Text vorab in so viele Zeilen umbrechen, wie bei Größe 2
|
||||
// (oder bei zu langem Text Größe 1) nötig sind - siehe
|
||||
// wrapTitleLines() oben. VIEW_TOP hängt dadurch von der
|
||||
// tatsächlichen Zeilenzahl des Titels ab, ist also kein constexpr
|
||||
// mehr wie vorher (wo immer nur eine Titelzeile angenommen wurde).
|
||||
// Wrap title text into as many lines as needed at size 2 (or size 1
|
||||
// if text is too long) - see wrapTitleLines() above. VIEW_TOP
|
||||
// therefore depends on the actual number of title lines, so it is
|
||||
// no longer constexpr as before (where only one title line was
|
||||
// assumed).
|
||||
tft.setTextSize(2);
|
||||
uint8_t titleTextSize = 2;
|
||||
if (tft.textWidth(title) > TEXT_MAX_WIDTH) {
|
||||
@@ -412,13 +406,13 @@ namespace {
|
||||
constexpr int MAX_TITLE_LINES = 3;
|
||||
String titleLines[MAX_TITLE_LINES];
|
||||
int titleLineCount = wrapTitleLines(tft, title, TEXT_MAX_WIDTH, titleLines, MAX_TITLE_LINES);
|
||||
// Fließtext wird immer bei Größe 1 vermessen/gezeichnet (siehe
|
||||
// layoutWrapped()-Aufrufe unten) - Größe hier zurücksetzen, falls
|
||||
// obiger Titel-Breitentest sie auf 2 stehen gelassen hat, sonst
|
||||
// würde die gleich folgende totalH-Berechnung (vor dem ersten
|
||||
// redraw()) mit falscher (zu breiter) Schriftgröße rechnen.
|
||||
// Body text is always measured/drawn at size 1 (see
|
||||
// layoutWrapped() calls below) - reset size here in case the
|
||||
// title width check above left it at 2, otherwise the
|
||||
// immediately following totalH calculation (before the first
|
||||
// redraw()) would use the wrong (too wide) font size.
|
||||
tft.setTextSize(1);
|
||||
// Eine Leerzeile Abstand zwischen Titel und Fließtext.
|
||||
// One blank line between title and body text.
|
||||
int16_t VIEW_TOP = (int16_t)(TITLE_Y + titleLineCount * LINE_H + 12);
|
||||
|
||||
constexpr int16_t BTN_H = 40;
|
||||
@@ -490,22 +484,21 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// Fortschrittsanzeige waehrend OtaUpdate::performUpdate() laeuft -
|
||||
// gleiches Namespace-globale-Zeiger-Prinzip wie progressTft oben (siehe
|
||||
// Settings-Backup-Fortschrittspunkte), da OtaUpdate::performUpdate()
|
||||
// ebenfalls einen einfachen C-Funktionszeiger erwartet, keine
|
||||
// Lambda-Capture erlaubt.
|
||||
// Progress display while OtaUpdate::performUpdate() runs - same
|
||||
// namespace-global pointer principle as progressTft above (see
|
||||
// Settings-Backup progress dots), since OtaUpdate::performUpdate()
|
||||
// also expects a plain C function pointer, no lambda capture
|
||||
// allowed.
|
||||
TFT_eSPI* otaProgressTft = nullptr;
|
||||
|
||||
void drawOtaProgress(uint8_t percent) {
|
||||
if (!otaProgressTft) return;
|
||||
TFT_eSPI& t = *otaProgressTft;
|
||||
// Zwei Zeilen statt einer langen: der Praefix-Text
|
||||
// (OTA_INSTALLING_PREFIX) ist in manchen Sprachen zu lang, um
|
||||
// zusammen mit der Prozentzahl auf einer Zeile bei lesbarer
|
||||
// Schriftgroesse zu passen (lief vorher links/rechts ueber den
|
||||
// Bildschirmrand hinaus). Jetzt: Beschriftung klein oben, Prozent
|
||||
// gross darunter.
|
||||
// Two lines instead of one long one: the prefix text
|
||||
// (OTA_INSTALLING_PREFIX) is too long in some languages to fit
|
||||
// together with the percentage on one line at a readable font size
|
||||
// (previously overflowed left/right beyond the screen edge).
|
||||
// Now: label small at top, percentage large below.
|
||||
constexpr int16_t BAND_H = 60;
|
||||
int16_t cy = Config::SCREEN_HEIGHT / 2;
|
||||
t.fillRect(0, (int16_t)(cy - BAND_H / 2), Config::SCREEN_WIDTH, BAND_H, TFT_BLACK);
|
||||
@@ -523,18 +516,17 @@ namespace {
|
||||
t.setTextDatum(TL_DATUM);
|
||||
}
|
||||
|
||||
// Kompletter Ablauf fuer "Nach Update suchen" (System-Menue) - Pruefung
|
||||
// gegen GitHub-Releases, bei verfuegbarem Update explizite Bestaetigung
|
||||
// (confirmWarningScreen() mit gruenem statt rotem Akzent - ein Update
|
||||
// ist keine destruktive Aktion wie Werksreset, verdient aber trotzdem
|
||||
// eine bewusste Bestaetigung, da WLAN/Strom waehrend des Vorgangs nicht
|
||||
// unterbrochen werden sollten), danach Fortschrittsanzeige waehrend
|
||||
// Download+Flash. WICHTIG: startet NICHT mehr automatisch neu und
|
||||
// springt bei einem Fehler auch nicht einfach stillschweigend zurueck
|
||||
// ins Menue - jedes Ergebnis (Erfolg wie Fehler) wird ueber infoScreen()
|
||||
// als eigener, stehenbleibender Screen angezeigt, den der Nutzer aktiv
|
||||
// bestaetigen muss. Bei Erfolg startet erst ein expliziter Tap auf
|
||||
// "Jetzt neu starten" tatsaechlich neu.
|
||||
// Complete flow for "Check for update" (System menu) - checks
|
||||
// against GitHub releases, explicit confirmation when update is
|
||||
// available (confirmWarningScreen() with green instead of red
|
||||
// accent - an update is not a destructive action like factory reset,
|
||||
// but still deserves conscious confirmation since WiFi/power should
|
||||
// not be interrupted during the process), then progress display
|
||||
// during download+flash. IMPORTANT: no longer auto-restarts and no
|
||||
// longer silently jumps back to the menu on error - every result
|
||||
// (success as well as failure) is shown via infoScreen() as its own
|
||||
// persistent screen that the user must actively acknowledge. On
|
||||
// success, only an explicit tap on "Restart now" actually restarts.
|
||||
void runOtaUpdateScreen(TFT_eSPI& tft) {
|
||||
MenuStars::reset();
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
@@ -570,18 +562,18 @@ namespace {
|
||||
otaProgressTft = nullptr;
|
||||
|
||||
if (ok) {
|
||||
// Bewusst KEIN automatischer Neustart mehr - der Nutzer
|
||||
// bestaetigt aktiv per Button, damit er den Erfolg auch wirklich
|
||||
// mitbekommt (vorher lief die Meldung nur 1,5s an, dann
|
||||
// Neustart - leicht zu verpassen).
|
||||
// Deliberately NO automatic restart anymore - the user
|
||||
// actively confirms via button so they actually notice the
|
||||
// success (previously the message showed for only 1.5s,
|
||||
// then restart - easy to miss).
|
||||
infoScreen(tft, I18n::t(StringId::OTA_UPDATE_SUCCESS), I18n::t(StringId::OTA_SUCCESS_BODY),
|
||||
TFT_GREEN, I18n::t(StringId::OTA_RESTART_BUTTON));
|
||||
ESP.restart();
|
||||
} else {
|
||||
// Bewusst ein stehenbleibender Info-Screen statt der alten
|
||||
// showBriefMessage() (1,2s, dann automatisch zurueck ins Menue)
|
||||
// - ein fehlgeschlagenes Firmware-Update ist keine
|
||||
// Nebensaechlichkeit, die man verpassen darf.
|
||||
// Deliberately a persistent info screen instead of the old
|
||||
// showBriefMessage() (1.2s, then automatically back to menu)
|
||||
// - a failed firmware update is not a minor detail that one
|
||||
// can afford to miss.
|
||||
infoScreen(tft, I18n::t(StringId::OTA_UPDATE_FAILED), I18n::t(StringId::OTA_FAILED_BODY),
|
||||
TFT_RED, I18n::t(StringId::OK));
|
||||
}
|
||||
@@ -673,12 +665,12 @@ void run(TFT_eSPI& tft) {
|
||||
Rect timeoutBtn = systemRowRect(3);
|
||||
Rect nightDimBtn = systemRowRect(4);
|
||||
Rect webuiBtn = systemRowRect(6);
|
||||
// "Sicherung & Reset" (Backup/Restore/Werksreset, siehe
|
||||
// Page::BackupReset unten) und "Nach Update suchen" stehen
|
||||
// bewusst direkt ueber "Info", das seinerseits als letzter
|
||||
// Punkt direkt ueber dem Zurueck-Button steht - so bleiben
|
||||
// beide Positionen stabil, egal wie viele weitere Punkte davor
|
||||
// noch dazukommen.
|
||||
// "Backup & Reset" (Backup/Restore/Factory Reset, see
|
||||
// Page::BackupReset below) and "Check for update" are
|
||||
// deliberately placed directly above "About", which in turn
|
||||
// is the last item directly above the Back button - this
|
||||
// keeps both positions stable regardless of how many further
|
||||
// items come before them.
|
||||
Rect ecoModeBtn = systemRowRect(5);
|
||||
Rect backupResetBtn = systemRowRect(7);
|
||||
Rect checkUpdateBtn = systemRowRect(8);
|
||||
@@ -717,12 +709,12 @@ void run(TFT_eSPI& tft) {
|
||||
} else if (brightnessBtn.contains(tap.x, tap.y)) {
|
||||
BrightnessScreen::run(tft);
|
||||
} else if (timeoutBtn.contains(tap.x, tap.y)) {
|
||||
// Vorher: Durchklicken per wiederholtem Antippen (0-10, ein
|
||||
// Tipp pro Minute - bei z.B. 10 Minuten also zehn Tipps).
|
||||
// Jetzt: eigener Screen mit Schieberegler, siehe
|
||||
// timeout_screen.cpp - dort lebt jetzt auch der
|
||||
// Ruhebildschirm-Umschalter (inhaltlich eng verwandt, und
|
||||
// dort ist Platz fuer eine kurze Erklaerung).
|
||||
// Previously: cycling through values by repeated tapping
|
||||
// (0-10, one tap per minute - e.g. 10 taps for 10 minutes).
|
||||
// Now: dedicated screen with a slider, see
|
||||
// timeout_screen.cpp - the screensaver toggle now also
|
||||
// lives there (closely related in content, and there is
|
||||
// room for a brief explanation).
|
||||
TimeoutScreen::run(tft);
|
||||
} else if (nightDimBtn.contains(tap.x, tap.y)) {
|
||||
SettingsStore::setNightDimmingEnabled(!SettingsStore::nightDimmingEnabled());
|
||||
@@ -752,9 +744,9 @@ void run(TFT_eSPI& tft) {
|
||||
|
||||
drawButton(tft, backupBtn, I18n::t(StringId::MENU_BACKUP));
|
||||
drawButton(tft, restoreBtn, I18n::t(StringId::MENU_RESTORE));
|
||||
// Danger-Akzent (rot) - deutlich von Sichern/Wiederherstellen
|
||||
// abgesetzt, da diese Aktion (nach Bestaetigung) ALLE Daten
|
||||
// unwiderruflich loescht, siehe confirmWarningScreen() unten.
|
||||
// Danger accent (red) - clearly distinguished from Backup/Restore
|
||||
// since this action (after confirmation) irreversibly DELETES
|
||||
// ALL data, see confirmWarningScreen() below.
|
||||
drawButton(tft, resetBtn, I18n::t(StringId::MENU_FACTORY_RESET), false, true);
|
||||
drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW));
|
||||
|
||||
@@ -794,10 +786,9 @@ void run(TFT_eSPI& tft) {
|
||||
tft.drawString(I18n::t(StringId::MENU_FACTORY_RESET_DELETING),
|
||||
Config::SCREEN_WIDTH / 2, Config::SCREEN_HEIGHT / 2);
|
||||
tft.setTextDatum(TL_DATUM);
|
||||
// Erfolgsfall: factoryReset() startet das Geraet neu und
|
||||
// kehrt nie zurueck - dieser Code danach laeuft nur im
|
||||
// (seltenen) Fehlerfall (SD nicht eingehaengt) ueberhaupt
|
||||
// weiter.
|
||||
// On success: factoryReset() restarts the device and never
|
||||
// returns - this code after it only continues in the
|
||||
// (rare) error case (SD not mounted).
|
||||
bool ok = SettingsBackup::factoryReset();
|
||||
if (!ok) {
|
||||
showBriefMessage(tft, I18n::t(StringId::MENU_FACTORY_RESET_FAILED), TFT_RED);
|
||||
@@ -812,13 +803,13 @@ void run(TFT_eSPI& tft) {
|
||||
tft.setCursor(10, 14);
|
||||
tft.println(I18n::t(StringId::MENU_CATEGORY_FLIGHT));
|
||||
|
||||
// Reine An/Aus-Schalter (Heartbeat, Notfall-Alarm, Naeherungs-LED,
|
||||
// Flugbuch, Beobachtungsalarm, Bodenfahrzeuge-Filter) stehen
|
||||
// bewusst gemeinsam am Ende der Liste, direkt ueber dem
|
||||
// Zurueck-Button - zuerst die LED-gesteuerten Alarme (Heartbeat,
|
||||
// Notfall, Naeherung; siehe LedAlert::Mode) zusammenhaengend,
|
||||
// danach die beiden nicht-LED-basierten Schalter Flugbuch und
|
||||
// Beobachtungsalarm sowie zuletzt der Bodenfahrzeuge-Filter.
|
||||
// Pure on/off toggles (Heartbeat, Emergency Alert, Proximity LED,
|
||||
// Flight Logbook, Watchlist Alert, Ground Vehicles Filter) are
|
||||
// deliberately grouped together at the end of the list,
|
||||
// directly above the Back button - first the LED-driven alerts
|
||||
// (Heartbeat, Emergency, Proximity; see LedAlert::Mode)
|
||||
// together, then the two non-LED-based toggles Flight Logbook
|
||||
// and Watchlist Alert, and finally the Ground Vehicles Filter.
|
||||
Rect aircraftListBtn = flightRowRect(0);
|
||||
Rect statsBtn = flightRowRect(1);
|
||||
Rect statsHistoryBtn = flightRowRect(2);
|
||||
@@ -860,9 +851,9 @@ drawButton(tft, watchlistBtn, I18n::t(StringId::MENU_WATCHLIST));
|
||||
|
||||
if (aircraftListBtn.contains(tap.x, tap.y)) {
|
||||
if (AircraftListScreen::run(tft)) {
|
||||
// Ein Flugzeug wurde in der Liste ausgewaehlt - direkt bis
|
||||
// zum Radar zurueckspringen (mit offenem Detail-Panel),
|
||||
// statt in der Flugoptionen-Seite stehen zu bleiben.
|
||||
// An aircraft was selected in the list - jump directly back
|
||||
// to the radar (with detail panel open), instead of
|
||||
// staying on the Flight Options page.
|
||||
done = true;
|
||||
}
|
||||
} else if (statsBtn.contains(tap.x, tap.y)) {
|
||||
@@ -887,7 +878,7 @@ drawButton(tft, watchlistBtn, I18n::t(StringId::MENU_WATCHLIST));
|
||||
SettingsStore::setProximityAlertEnabled(!SettingsStore::proximityAlertEnabled());
|
||||
} else if (logbookBtn.contains(tap.x, tap.y)) {
|
||||
if (SettingsStore::flightLogbookEnabled()) {
|
||||
// Ausschalten ist immer unbedenklich - keine Bestaetigung noetig.
|
||||
// Turning off is always harmless - no confirmation needed.
|
||||
SettingsStore::setFlightLogbookEnabled(false);
|
||||
SettingsStore::setFlightLogbookEnabledAtEpoch(0);
|
||||
SettingsStore::setFlightLogbookSessionFile("");
|
||||
@@ -895,9 +886,9 @@ drawButton(tft, watchlistBtn, I18n::t(StringId::MENU_WATCHLIST));
|
||||
I18n::t(StringId::MENU_LOGBOOK_WARNING_BODY))) {
|
||||
SettingsStore::setFlightLogbookEnabled(true);
|
||||
SettingsStore::setFlightLogbookEnabledAtEpoch((uint32_t)time(nullptr));
|
||||
// Leerer Eintrag erzwingt eine frische Sitzungsdatei beim
|
||||
// naechsten FlightLogbook::update() statt eine evtl. noch
|
||||
// vorhandene alte Datei weiterzuschreiben.
|
||||
// Empty entry forces a fresh session file on the next
|
||||
// FlightLogbook::update() instead of continuing to
|
||||
// write to a possibly existing old file.
|
||||
SettingsStore::setFlightLogbookSessionFile("");
|
||||
}
|
||||
} else if (watchlistAlertBtn.contains(tap.x, tap.y)) {
|
||||
|
||||
Reference in New Issue
Block a user