Add progress-dot feedback to Settings Backup/Restore buttons

The SD-bound copy in SettingsBackup::backup()/restore() blocked
noticeably with no visual feedback, making the button look frozen.
Both functions now accept an optional callback invoked before each
of the two file copies, which menu_screen.cpp uses to append dots to
the button label in place.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Eiswolf-BG
2026-08-12 21:34:53 +02:00
parent 6f3713f348
commit bfdb8a9fd4
3 changed files with 47 additions and 6 deletions

View File

@@ -126,6 +126,27 @@ namespace {
String onOff(bool on) { return I18n::t(on ? StringId::ON : StringId::OFF); } 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).
TFT_eSPI* progressTft = nullptr;
Rect progressBtnRect;
String progressLabel;
uint8_t progressDots = 0;
void drawProgressStep() {
if (!progressTft) return;
progressDots++;
String label = progressLabel;
for (uint8_t i = 0; i < progressDots; i++) label += ".";
drawButton(*progressTft, progressBtnRect, label);
}
String screenTimeoutLabel(uint8_t minutes) { String screenTimeoutLabel(uint8_t minutes) {
String prefix = I18n::t(StringId::MENU_SCREEN_TIMEOUT_PREFIX); String prefix = I18n::t(StringId::MENU_SCREEN_TIMEOUT_PREFIX);
if (minutes == 0) return prefix + I18n::t(StringId::NEVER); if (minutes == 0) return prefix + I18n::t(StringId::NEVER);
@@ -449,12 +470,22 @@ void run(TFT_eSPI& tft) {
} }
if (backupBtn.contains(tap.x, tap.y)) { if (backupBtn.contains(tap.x, tap.y)) {
bool ok = SettingsBackup::backup(); progressTft = &tft;
progressBtnRect = backupBtn;
progressLabel = I18n::t(StringId::MENU_BACKUP);
progressDots = 0;
bool ok = SettingsBackup::backup(drawProgressStep);
progressTft = nullptr;
showBriefMessage(tft, I18n::t(ok ? StringId::MENU_BACKUP_SAVED : StringId::MENU_BACKUP_FAILED), showBriefMessage(tft, I18n::t(ok ? StringId::MENU_BACKUP_SAVED : StringId::MENU_BACKUP_FAILED),
ok ? TFT_GREEN : TFT_RED); ok ? TFT_GREEN : TFT_RED);
} else if (restoreBtn.contains(tap.x, tap.y)) { } else if (restoreBtn.contains(tap.x, tap.y)) {
if (SettingsBackup::hasBackup()) { if (SettingsBackup::hasBackup()) {
bool ok = SettingsBackup::restore(); progressTft = &tft;
progressBtnRect = restoreBtn;
progressLabel = I18n::t(StringId::MENU_RESTORE);
progressDots = 0;
bool ok = SettingsBackup::restore(drawProgressStep);
progressTft = nullptr;
showBriefMessage(tft, I18n::t(ok ? StringId::MENU_RESTORED : StringId::MENU_RESTORE_FAILED), showBriefMessage(tft, I18n::t(ok ? StringId::MENU_RESTORED : StringId::MENU_RESTORE_FAILED),
ok ? TFT_GREEN : TFT_RED); ok ? TFT_GREEN : TFT_RED);
} }

View File

@@ -33,20 +33,24 @@ namespace {
} }
} }
bool backup() { bool backup(void (*onStep)()) {
if (!SdStorage::isMounted()) return false; if (!SdStorage::isMounted()) return false;
SdMutex::Guard guard; SdMutex::Guard guard;
if (onStep) onStep();
bool okSettings = copyFile(Config::SD_SETTINGS_FILE, SETTINGS_BACKUP_FILE); bool okSettings = copyFile(Config::SD_SETTINGS_FILE, SETTINGS_BACKUP_FILE);
if (onStep) onStep();
bool okWifi = copyFile(Config::SD_WIFI_CREDENTIALS_FILE, WIFI_BACKUP_FILE); bool okWifi = copyFile(Config::SD_WIFI_CREDENTIALS_FILE, WIFI_BACKUP_FILE);
return okSettings || okWifi; return okSettings || okWifi;
} }
bool restore() { bool restore(void (*onStep)()) {
if (!SdStorage::isMounted()) return false; if (!SdStorage::isMounted()) return false;
SdMutex::Guard guard; SdMutex::Guard guard;
if (onStep) onStep();
bool okSettings = copyFile(SETTINGS_BACKUP_FILE, Config::SD_SETTINGS_FILE); bool okSettings = copyFile(SETTINGS_BACKUP_FILE, Config::SD_SETTINGS_FILE);
if (onStep) onStep();
bool okWifi = copyFile(WIFI_BACKUP_FILE, Config::SD_WIFI_CREDENTIALS_FILE); bool okWifi = copyFile(WIFI_BACKUP_FILE, Config::SD_WIFI_CREDENTIALS_FILE);
return okSettings || okWifi; return okSettings || okWifi;
} }

View File

@@ -2,8 +2,14 @@
#include <Arduino.h> #include <Arduino.h>
namespace SettingsBackup { namespace SettingsBackup {
bool backup(); // onStep (falls angegeben) wird bei jedem der beiden Kopiervorgaenge
bool restore(); // (erst Einstellungen, dann WLAN-Zugangsdaten) direkt VOR dem
// jeweiligen Kopieren aufgerufen - der aufrufende Screen
// (menu_screen.cpp) nutzt das, um waehrend des SD-bedingt spuerbar
// langsamen Sicherns/Wiederherstellens Fortschrittspunkte auf dem
// Button anzuzeigen, statt dass der Button eingefroren wirkt.
bool backup(void (*onStep)() = nullptr);
bool restore(void (*onStep)() = nullptr);
bool hasBackup(); bool hasBackup();
// Loescht den kompletten Flightradar-Ordner von der SD-Karte und // Loescht den kompletten Flightradar-Ordner von der SD-Karte und