diff --git a/generated/ui_320x240/screens.c b/generated/ui_320x240/screens.c index 6d7f1ce..9e41f1f 100644 --- a/generated/ui_320x240/screens.c +++ b/generated/ui_320x240/screens.c @@ -1255,6 +1255,39 @@ void create_screen_main_screen() { lv_obj_set_style_pad_column(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); { lv_obj_t *parent_obj = obj; + { + // BasicSettingsApplyButton + lv_obj_t *obj = lv_btn_create(parent_obj); + objects.basic_settings_apply_button = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(95), 40); + add_style_settings_button_style(obj); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xff4db270), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_text_color(obj, lv_color_hex(0xffb24d8f), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xff4a5e4a), LV_PART_MAIN | LV_STATE_DISABLED); + lv_obj_set_style_align(obj, LV_ALIGN_TOP_MID, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_shadow_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xfffafaf4), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(obj, lv_color_hex(0xff294337), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(obj, lv_color_hex(0xff101010), LV_PART_MAIN | LV_STATE_DISABLED); + lv_obj_set_style_margin_left(obj, 40, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_margin_right(obj, 40, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_add_state(obj, LV_STATE_DISABLED); + { + lv_obj_t *parent_obj = obj; + { + // BasicSettingsApplyLabel + lv_obj_t *obj = lv_label_create(parent_obj); + objects.basic_settings_apply_label = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); + lv_label_set_long_mode(obj, LV_LABEL_LONG_DOT); + lv_label_set_text(obj, _("Apply & Reboot")); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DISABLED); + lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); + } + } + } { // BasicSettingsUserButton lv_obj_t *obj = lv_btn_create(parent_obj); @@ -1267,6 +1300,7 @@ void create_screen_main_screen() { lv_obj_set_style_bg_color(obj, lv_color_hex(0xff4db270), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_text_color(obj, lv_color_hex(0xff015114), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DISABLED); + lv_obj_center(obj); { lv_obj_t *parent_obj = obj; { @@ -2062,6 +2096,18 @@ void create_screen_main_screen() { lv_label_set_text(obj, _("Settings & Tools")); lv_obj_set_style_align(obj, LV_ALIGN_LEFT_MID, LV_PART_MAIN | LV_STATE_DEFAULT); } + { + // TopBasicSettingsUnsavedLabel + lv_obj_t *obj = lv_label_create(parent_obj); + objects.top_basic_settings_unsaved_label = obj; + lv_obj_set_pos(obj, 25, 0); + lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + lv_label_set_long_mode(obj, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_style_text_color(obj, lv_color_hex(0xffde3232), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_label_set_text(obj, _("Unsaved changes ...")); + lv_obj_set_style_align(obj, LV_ALIGN_LEFT_MID, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); + } { // TopBasicSettingsImage lv_obj_t *obj = lv_img_create(parent_obj); diff --git a/generated/ui_320x240/screens.h b/generated/ui_320x240/screens.h index 9897914..6ef4d6e 100644 --- a/generated/ui_320x240/screens.h +++ b/generated/ui_320x240/screens.h @@ -1,70 +1,70 @@ -#ifndef EEZ_LVGL_UI_SCREENS_H -#define EEZ_LVGL_UI_SCREENS_H - -#include "lvgl.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -// advanced settings -extern lv_obj_t * ui_AdvancedSettingsPanel; -extern lv_obj_t * ui_SettingsTabView; -extern lv_obj_t * ui_TabPageGeneral; -extern lv_obj_t * ui_GeneralLanguageButton; -extern lv_obj_t * ui_LanguageLabel; -extern lv_obj_t * ui_GeneralTimezoneButton; -extern lv_obj_t * ui_TimezoneLabel; -extern lv_obj_t * ui_GeneralScreenButton; -extern lv_obj_t * ui_ScreenLabel; -extern lv_obj_t * ui_GeneralMapsButton; -extern lv_obj_t * ui_MapsLabel; -extern lv_obj_t * ui_GeneralAudioButton; -extern lv_obj_t * ui_AudioLabel1; -extern lv_obj_t * ui_TabPageRadio; -extern lv_obj_t * ui_RadioBluetoothButton; -extern lv_obj_t * ui_BluetoothLabel; -extern lv_obj_t * ui_RadioDeviceButton; -extern lv_obj_t * ui_DeviceLabel; -extern lv_obj_t * ui_RadioDisplayButton; -extern lv_obj_t * ui_DisplayLabel; -extern lv_obj_t * ui_RadioLoRaButton; -extern lv_obj_t * ui_LoRaLabel; -extern lv_obj_t * ui_RadioNetworkButton; -extern lv_obj_t * ui_NetworkLabel; -extern lv_obj_t * ui_RadioPositionButton; -extern lv_obj_t * ui_PositionLabel; -extern lv_obj_t * ui_RadioPowerButton; -extern lv_obj_t * ui_PowerLabel; -extern lv_obj_t * ui_TabPageModules; -extern lv_obj_t * ui_ModuleCannedMsgButton; -extern lv_obj_t * ui_CannedMsgLabel; -extern lv_obj_t * ui_ModuleSaFButton; -extern lv_obj_t * ui_StoreAndForwardLabel; -extern lv_obj_t * ui_ModuleTelemetryButton; -extern lv_obj_t * ui_TelemetryLabel; -extern lv_obj_t * ui_ModuleMQTTButton; -extern lv_obj_t * ui_MQTTLabel; -extern lv_obj_t * ui_ModuleRangeTestButton; -extern lv_obj_t * ui_RangeTestLabel; -extern lv_obj_t * ui_ModuleAudioButton; -extern lv_obj_t * ui_AudioLabel; -extern lv_obj_t * ui_ModuleSerialButton; -extern lv_obj_t * ui_SerialLabel; -extern lv_obj_t * ui_ModuleExtNotificationButton; -extern lv_obj_t * ui_ExtNotificationLabel; -extern lv_obj_t * ui_ModuleNeighborInfoButton; -extern lv_obj_t * ui_NeighborInfoLabel; -extern lv_obj_t * ui_ModuleAmbientLightingButton; -extern lv_obj_t * ui_AmbientLightingLabel; -extern lv_obj_t * ui_ModuleDetectionSensorButton; -extern lv_obj_t * ui_DetectionSensorLabel; -extern lv_obj_t * ui_ModuleRemoteHardwareButton; -extern lv_obj_t * ui_RemoteHardwareLabel; - -void create_tabview_settings(void); - +#ifndef EEZ_LVGL_UI_SCREENS_H +#define EEZ_LVGL_UI_SCREENS_H + +#include "lvgl.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +// advanced settings +extern lv_obj_t * ui_AdvancedSettingsPanel; +extern lv_obj_t * ui_SettingsTabView; +extern lv_obj_t * ui_TabPageGeneral; +extern lv_obj_t * ui_GeneralLanguageButton; +extern lv_obj_t * ui_LanguageLabel; +extern lv_obj_t * ui_GeneralTimezoneButton; +extern lv_obj_t * ui_TimezoneLabel; +extern lv_obj_t * ui_GeneralScreenButton; +extern lv_obj_t * ui_ScreenLabel; +extern lv_obj_t * ui_GeneralMapsButton; +extern lv_obj_t * ui_MapsLabel; +extern lv_obj_t * ui_GeneralAudioButton; +extern lv_obj_t * ui_AudioLabel1; +extern lv_obj_t * ui_TabPageRadio; +extern lv_obj_t * ui_RadioBluetoothButton; +extern lv_obj_t * ui_BluetoothLabel; +extern lv_obj_t * ui_RadioDeviceButton; +extern lv_obj_t * ui_DeviceLabel; +extern lv_obj_t * ui_RadioDisplayButton; +extern lv_obj_t * ui_DisplayLabel; +extern lv_obj_t * ui_RadioLoRaButton; +extern lv_obj_t * ui_LoRaLabel; +extern lv_obj_t * ui_RadioNetworkButton; +extern lv_obj_t * ui_NetworkLabel; +extern lv_obj_t * ui_RadioPositionButton; +extern lv_obj_t * ui_PositionLabel; +extern lv_obj_t * ui_RadioPowerButton; +extern lv_obj_t * ui_PowerLabel; +extern lv_obj_t * ui_TabPageModules; +extern lv_obj_t * ui_ModuleCannedMsgButton; +extern lv_obj_t * ui_CannedMsgLabel; +extern lv_obj_t * ui_ModuleSaFButton; +extern lv_obj_t * ui_StoreAndForwardLabel; +extern lv_obj_t * ui_ModuleTelemetryButton; +extern lv_obj_t * ui_TelemetryLabel; +extern lv_obj_t * ui_ModuleMQTTButton; +extern lv_obj_t * ui_MQTTLabel; +extern lv_obj_t * ui_ModuleRangeTestButton; +extern lv_obj_t * ui_RangeTestLabel; +extern lv_obj_t * ui_ModuleAudioButton; +extern lv_obj_t * ui_AudioLabel; +extern lv_obj_t * ui_ModuleSerialButton; +extern lv_obj_t * ui_SerialLabel; +extern lv_obj_t * ui_ModuleExtNotificationButton; +extern lv_obj_t * ui_ExtNotificationLabel; +extern lv_obj_t * ui_ModuleNeighborInfoButton; +extern lv_obj_t * ui_NeighborInfoLabel; +extern lv_obj_t * ui_ModuleAmbientLightingButton; +extern lv_obj_t * ui_AmbientLightingLabel; +extern lv_obj_t * ui_ModuleDetectionSensorButton; +extern lv_obj_t * ui_DetectionSensorLabel; +extern lv_obj_t * ui_ModuleRemoteHardwareButton; +extern lv_obj_t * ui_RemoteHardwareLabel; + +void create_tabview_settings(void); + typedef struct _objects_t { lv_obj_t *boot_screen; lv_obj_t *main_screen; @@ -491,6 +491,9 @@ typedef struct _objects_t { lv_obj_t *blank_screen_button; lv_obj_t *screen_lock_button_matrix; lv_obj_t *lock_screen_digits_label; + lv_obj_t *basic_settings_apply_button; + lv_obj_t *basic_settings_apply_label; + lv_obj_t *top_basic_settings_unsaved_label; } objects_t; extern objects_t objects; @@ -520,13 +523,13 @@ void tick_screen_calibration_screen(); void create_user_widget_ok_cancel_widget(lv_obj_t *parent_obj, int startWidgetIndex); void tick_user_widget_ok_cancel_widget(int startWidgetIndex); - + void create_screens(); void tick_screen(int screen_index); - - -#ifdef __cplusplus -} -#endif - + + +#ifdef __cplusplus +} +#endif + #endif /*EEZ_LVGL_UI_SCREENS_H*/ \ No newline at end of file diff --git a/include/TFTView_320x240.h b/include/TFTView_320x240.h index 81ba8e5..4606f5d 100644 --- a/include/TFTView_320x240.h +++ b/include/TFTView_320x240.h @@ -76,6 +76,7 @@ class TFTView_320x240 : public MeshtasticView void notifyResync(bool show) override; void notifyReboot(bool show) override; void notifyShutdown(void) override; + void notifyConfigReboot(bool show); void blankScreen(bool enable) override; void screenSaving(bool enabled) override; bool isScreenLocked(void) override; @@ -194,6 +195,8 @@ class TFTView_320x240 : public MeshtasticView void ui_set_active(lv_obj_t *b, lv_obj_t *p, lv_obj_t *tp); void showKeyboard(lv_obj_t *textArea); lv_obj_t *showQrCode(lv_obj_t *parent, const char *data); + void configTransactionOpen(); + void checkForConfigTransactionClose(); void enablePanel(lv_obj_t *panel); void disablePanel(lv_obj_t *panel); @@ -267,6 +270,7 @@ class TFTView_320x240 : public MeshtasticView static void ui_event_message_ready(lv_event_t *e); + static void ui_event_apply_button(lv_event_t *e); static void ui_event_user_button(lv_event_t *e); static void ui_event_role_button(lv_event_t *e); static void ui_event_region_button(lv_event_t *e); @@ -362,6 +366,7 @@ class TFTView_320x240 : public MeshtasticView std::array ch_label; // indexable label list for settings meshtastic_Channel *channel_scratch; // temporary scratch copy of channel db lv_obj_t *qr; // qr code + bool configRebootRequired; // set when AdminModule will reboot if settings are applied // extended default device profile struct with additional required data struct meshtastic_DeviceProfile_ext : meshtastic_DeviceProfile { diff --git a/include/ViewController.h b/include/ViewController.h index ba241a7..345341f 100644 --- a/include/ViewController.h +++ b/include/ViewController.h @@ -44,6 +44,8 @@ class ViewController virtual bool sendConfig(meshtastic_Config_BluetoothConfig &&bluetooth, uint32_t nodeId = 0); virtual bool sendConfig(meshtastic_Config_SecurityConfig &&security, uint32_t nodeId = 0); virtual bool sendConfig(meshtastic_Channel &channel, uint32_t nodeId = 0); + virtual bool openConfigTransaction(uint32_t nodeId = 0); + virtual bool closeConfigTransaction(uint32_t nodeId = 0); // module config virtual bool sendConfig(meshtastic_ModuleConfig_MQTTConfig &&mqtt, uint32_t nodeId = 0); diff --git a/locale/lv_i18n.c b/locale/lv_i18n.c index c0f2fa1..42e7d58 100644 --- a/locale/lv_i18n.c +++ b/locale/lv_i18n.c @@ -158,6 +158,7 @@ static lv_i18n_phrase_t de_singulars[] = { {"Resynch ...", "Synchronisierung ..."}, {"Rebooting ...", "Neustart ..."}, {"Shutting down ...", "Herunterfahren ..."}, + {"Config changed!\nRebooting ...", "Konfiguration geändert!\nNeustart ..."}, {"silent", "stumm"}, {"WiFi: ", "WiFi: "}, {"LoRa TX off!", "LoRa TX ausgeschaltet!"}, @@ -173,6 +174,8 @@ static lv_i18n_phrase_t de_singulars[] = { {"!Enter Filter ...", "!Filter Name..."}, {"Enter Filter ...", "Filter Name..."}, {"region unset", "keine Region"}, + {"Unsaved changes ...", "Nicht gespeichert ..."}, + {"Apply & Reboot", "Anwenden & neu starten"}, {NULL, NULL} // End mark }; @@ -310,6 +313,7 @@ static lv_i18n_phrase_t es_singulars[] = { {"Resynch ...", "Resincronizando ..."}, {"Rebooting ...", "Reiniciando ..."}, {"Shutting down ...", "Apagando ..."}, + {"Config changed!\nRebooting ...", "¡Config. cambiada!\nReiniciando ..."}, {"silent", "silenciado"}, {"WiFi: ", "WiFi: "}, {"LoRa TX off!", "LoRa TX apagado!"}, @@ -326,6 +330,8 @@ static lv_i18n_phrase_t es_singulars[] = { {"!Enter Filter ...", "!Introduce filtro..."}, {"Enter Filter ...", "Introduce filtro ..."}, {"region unset", "región no configurada"}, + {"Unsaved changes ...", "Cambios no guardados ..."}, + {"Apply & Reboot", "Aplicar & reiniciar"}, {NULL, NULL} // End mark }; @@ -452,6 +458,9 @@ static lv_i18n_phrase_t fi_singulars[] = { {"Resynch ...", "Synkronoi uudelleen ..."}, {"Rebooting ...", "Käynnistetään uudelleen ..."}, {"Shutting down ...", "Sammutetetaan ..."}, + {"Config changed!\nRebooting ...", "Konfiguraatio muuttunut!\nKäynnistetään uudelleen ..."}, + {"Unsaved changes ...", "Ei tallennettu ..."}, + {"Apply & Reboot", "Käyttää & käynnistä uudelleen"}, {NULL, NULL} // End mark }; @@ -579,6 +588,7 @@ static lv_i18n_phrase_t fr_singulars[] = { {"Secondary Channels", "Canaux Secondaires"}, {"Rebooting ...", "Redémarrage ..."}, {"Shutting down ...", "Arrêt..."}, + {"Config changed!\nRebooting ...", "Configuration modifiée!\nRedémarrage ..."}, {"silent", "silencieux"}, {"WiFi: ", "WiFi: "}, {"Lock: off/off", "Verrouillage: non/non"}, @@ -597,6 +607,8 @@ static lv_i18n_phrase_t fr_singulars[] = { {"Banner only", "Alerte Visuelle"}, {"Sound only", "Sonnerie Seule"}, {"no signal", "aucun signal"}, + {"Unsaved changes ...", "Modif. non enregistrées ..."}, + {"Apply & Reboot", "Appliquer et redémarrer"}, {NULL, NULL} // End mark }; @@ -730,6 +742,7 @@ static lv_i18n_phrase_t it_singulars[] = { {"Resynch ...", "Risinc ..."}, {"Rebooting ...", "Riavvio ..."}, {"Shutting down ...", "Spegnimento ..."}, + {"Config changed!\nRebooting ...", "Config. modificata!\nRiavvio ..."}, {"silent", "silenzioso"}, {"WiFi: ", "WiFi: "}, {"Lock: off/off", "Blocco: disattivato/disattivato"}, @@ -747,6 +760,8 @@ static lv_i18n_phrase_t it_singulars[] = { {"Banner & Sound", "Banner e Suono"}, {"Banner only", "Solo Banner"}, {"Sound only", "Solo Suono"}, + {"Unsaved changes ...", "Modifiche non salvate ..."}, + {"Apply & Reboot", "Applica & riavvia"}, {NULL, NULL} // End mark }; @@ -902,6 +917,7 @@ static lv_i18n_phrase_t nl_singulars[] = { {"Resynch ...", "Hersync ..."}, {"Rebooting ...", "Herstarten ..."}, {"Shutting down ...", "Uitschakelen ..."}, + {"Config changed!\nRebooting ...", "Config gewijzigd!\nHerstarten ..."}, {"silent", "stil"}, {"WiFi: ", "WiFi: "}, {"Lock: off/off", "Slot: uit/uit"}, @@ -916,6 +932,8 @@ static lv_i18n_phrase_t nl_singulars[] = { {"Banner & Sound", "Strook & Geluid"}, {"Banner only", "Alleen strook"}, {"Sound only", "Alleen geluid"}, + {"Unsaved changes ...", "Niet opgeslagen ..."}, + {"Apply & Reboot", "Toepassen & herstarten"}, {NULL, NULL} // End mark }; @@ -1051,6 +1069,7 @@ static lv_i18n_phrase_t no_singulars[] = { {"Resynch ...", "Synkroniserer ..."}, {"Rebooting ...", "Starter på nytt ..."}, {"Shutting down ...", "Skrur av ..."}, + {"Config changed!\nRebooting ...", "Konfig endret!\nStarter på nytt ..."}, {"silent", "stille"}, {"WiFi: ", "WiFi: "}, {"Lock: off/off", "Lås: av/av"}, @@ -1066,6 +1085,8 @@ static lv_i18n_phrase_t no_singulars[] = { {"Banner & Sound", "Banner & Lyd"}, {"Banner only", "Bare banner"}, {"Sound only", "Bare lyd"}, + {"Unsaved changes ...", "Ulagrede endringer ..."}, + {"Apply & Reboot", "Bruk og start på nytt"}, {"no signal", "ingen signal"}, {NULL, NULL} // End mark }; @@ -1195,6 +1216,9 @@ static lv_i18n_phrase_t pl_singulars[] = { {"Secondary Channels", "Kanały dodatkowe"}, {"Rebooting ...", "Uruchamiam ponownie ..."}, {"Shutting down ...", "Wyłączam ..."}, + {"Config changed!\nRebooting ...", "Konfiguracja zmieniona!\nUruchamiam ponownie ..."}, + {"Unsaved changes ...", "Niezapisane zmiany ..."}, + {"Apply & Reboot", "Zastosuj & uruchom ponownie"}, {NULL, NULL} // End mark }; @@ -1345,6 +1369,9 @@ static lv_i18n_phrase_t pt_singulars[] = { {"Resynch ...", "Resincronizando..."}, {"Rebooting ...", "Reiniciando..."}, {"Shutting down ...", "Desligando..."}, + {"Config changed!\nRebooting ...", "Configuração alterada!\nReiniciando..."}, + {"Unsaved changes ...", "Alter. não guardadas ..."}, + {"Apply & Reboot", "Aplicar & reiniciar"}, {NULL, NULL} // End mark }; @@ -1498,6 +1525,7 @@ static lv_i18n_phrase_t ru_singulars[] = { {"Resynch ...", "Ресинхронизация ..."}, {"Rebooting ...", "Перезагрузка ..."}, {"Shutting down ...", "Выключение ..."}, + {"Config changed!\nRebooting ...", "Конфигурация изменена!\nПерезагрузка ..."}, {"silent", "тихий режим"}, {"WiFi: ", "WiFi: <не настроен>"}, {"Lock: off/off", "Блокировка: выкл/выкл"}, @@ -1514,6 +1542,8 @@ static lv_i18n_phrase_t ru_singulars[] = { {"Banner & Sound", "Баннер и звук"}, {"Banner only", "Только баннер"}, {"Sound only", "Только звук"}, + {"Unsaved changes ...", "Несохраненные изменения ..."}, + {"Apply & Reboot", "Применить & перезагрузить"}, {NULL, NULL} // End mark }; @@ -1654,6 +1684,7 @@ static lv_i18n_phrase_t se_singulars[] = { {"Resynch ...", "Synkroniserar om ..."}, {"Rebooting ...", "Startar om ..."}, {"Shutting down ...", "Stänger av ..."}, + {"Config changed!\nRebooting ...", "Konfiguration ändrad!\nStartar om ..."}, {"silent", "tyst"}, {"WiFi: ", "WiFi: "}, {"Lock: off/off", "Lås: av/av"}, @@ -1669,6 +1700,8 @@ static lv_i18n_phrase_t se_singulars[] = { {"Banner & Sound", "Banner & Ljud"}, {"Banner only", "Endast Banner"}, {"Sound only", "Endast ljud"}, + {"Unsaved changes ...", "Osparade ändringar ..."}, + {"Apply & Reboot", "Applicera & starta om"}, {NULL, NULL} // End mark }; @@ -1986,6 +2019,7 @@ static lv_i18n_phrase_t sr_singulars[] = { {"Resynch ...", "Sinhronizacija ..."}, {"Rebooting ...", "Resetovanje ..."}, {"Shutting down ...", "Gašenje ..."}, + {"Config changed!\nRebooting ...", "Konfiguracija promenjena!\nResetovanje ..."}, {"silent", "bez zvuka"}, {"WiFi: ", "WiFi: "}, {"LoRa TX off!", "LoRa TX ugašen!"}, @@ -1994,6 +2028,8 @@ static lv_i18n_phrase_t sr_singulars[] = { {"Banner & Sound", "Baner i zvuk"}, {"Banner only", "Samo baner"}, {"Sound only", "Samo zvuk"}, + {"Unsaved changes ...", "Nesačuvane promene ..."}, + {"Apply & Reboot", "Primeni & resetuj"}, {NULL, NULL} // End mark }; diff --git a/source/TFTView_320x240.cpp b/source/TFTView_320x240.cpp index 60fa3ef..0b099e0 100644 --- a/source/TFTView_320x240.cpp +++ b/source/TFTView_320x240.cpp @@ -102,10 +102,26 @@ TFTView_320x240 *TFTView_320x240::instance(const DisplayDriverConfig &cfg) } TFTView_320x240::TFTView_320x240(const DisplayDriverConfig *cfg, DisplayDriver *driver) - : MeshtasticView(cfg, driver, new ViewController), screensInitialised(false), nodesFiltered(0), nodesChanged(true), - processingFilter(false), packetLogEnabled(false), detectorRunning(false), packetCounter(0), actTime(0), uptime(0), - lastHeard(0), hasPosition(false), topNodeLL(nullptr), scans(0), selectedHops(0), chooseNodeSignalScanner(false), - chooseNodeTraceRoute(false), qr(nullptr), db{} + : MeshtasticView(cfg, driver, new ViewController) + , screensInitialised(false) + , nodesFiltered(0) + , nodesChanged(true) + , processingFilter(false) + , packetLogEnabled(false) + , detectorRunning(false) + , packetCounter(0) + , actTime(0) + , uptime(0) + , lastHeard(0) + , hasPosition(false) + , topNodeLL(nullptr) + , scans(0) + , selectedHops(0) + , chooseNodeSignalScanner(false) + , chooseNodeTraceRoute(false) + , qr(nullptr) + , configRebootRequired(false) + , db{} { filter.active = false; highlight.active = false; @@ -327,6 +343,8 @@ void TFTView_320x240::init_screens(void) objects.home_wlan_button->user_data = (void *)0; objects.home_memory_button->user_data = (void *)0; + lv_obj_set_state(objects.basic_settings_apply_button, LV_STATE_DISABLED, true); + updateFreeMem(); screensInitialised = true; @@ -502,9 +520,11 @@ void TFTView_320x240::ui_events_init(void) auto ui_event_HomeButton = [](lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); TFTView_320x240 &view = *static_cast(e->user_data); view.ui_set_active(objects.home_button, objects.home_panel, objects.top_panel); } else if (event_code == LV_EVENT_LONG_PRESSED) { + THIS->checkForConfigTransactionClose(); // force re-sync with node THIS->controller->setConfigRequested(true); THIS->notifyResync(true); @@ -568,6 +588,7 @@ void TFTView_320x240::ui_events_init(void) // basic settings buttons lv_obj_add_event_cb(objects.basic_settings_user_button, ui_event_user_button, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(objects.basic_settings_apply_button, ui_event_apply_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_role_button, ui_event_role_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_region_button, ui_event_region_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_modem_preset_button, ui_event_preset_button, LV_EVENT_CLICKED, NULL); @@ -683,6 +704,7 @@ void TFTView_320x240::ui_event_NodesButton(lv_event_t *e) ignoreClicked = false; return; } + THIS->checkForConfigTransactionClose(); THIS->ui_set_active(objects.nodes_button, objects.nodes_panel, objects.top_nodes_panel); if (filterNeedsUpdate) { THIS->updateNodesFiltered(true); @@ -690,6 +712,7 @@ void TFTView_320x240::ui_event_NodesButton(lv_event_t *e) filterNeedsUpdate = false; } } else if (event_code == LV_EVENT_LONG_PRESSED) { + THIS->checkForConfigTransactionClose(); filterNeedsUpdate = true; ignoreClicked = true; THIS->ui_set_active(objects.nodes_button, objects.node_options_panel, objects.top_node_options_panel); @@ -752,10 +775,30 @@ void TFTView_320x240::ui_event_NodeButton(lv_event_t *e) } } +void TFTView_320x240::configTransactionOpen() +{ + if (!THIS->configRebootRequired) { + lv_obj_remove_state(objects.basic_settings_apply_button, LV_STATE_DISABLED); + lv_obj_add_flag(objects.top_basic_settings_label, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(objects.top_basic_settings_unsaved_label, LV_OBJ_FLAG_HIDDEN); + THIS->configRebootRequired = true; + THIS->controller->openConfigTransaction(); + } +} + +void TFTView_320x240::checkForConfigTransactionClose() +{ + if (THIS->configRebootRequired) { + THIS->notifyConfigReboot(true); + THIS->controller->closeConfigTransaction(); + } +} + void TFTView_320x240::ui_event_GroupsButton(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); THIS->ui_set_active(objects.groups_button, objects.groups_panel, objects.top_groups_panel); } } @@ -764,6 +807,7 @@ void TFTView_320x240::ui_event_ChannelButton(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); uint8_t ch = (uint8_t)(unsigned long)e->user_data; if (THIS->db.channel[ch].role != meshtastic_Channel_Role_DISABLED) THIS->showMessages(ch); @@ -776,6 +820,7 @@ void TFTView_320x240::ui_event_MessagesButton(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); THIS->ui_set_active(objects.messages_button, objects.chats_panel, objects.top_chats_panel); } } @@ -784,6 +829,7 @@ void TFTView_320x240::ui_event_MapButton(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); THIS->ui_set_active(objects.map_button, objects.map_panel, objects.top_map_panel); } } @@ -1076,8 +1122,8 @@ void TFTView_320x240::ui_event_WLANButton(lv_event_t *e) meshtastic_Config_NetworkConfig &network = THIS->db.config.network; network.wifi_enabled = !network.wifi_enabled; Themes::recolorButton(objects.home_wlan_button, network.wifi_enabled); + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_NetworkConfig{network}); - THIS->notifyReboot(true); } } } @@ -1230,6 +1276,14 @@ void TFTView_320x240::ui_event_message_ready(lv_event_t *e) // basic settings buttons +void TFTView_320x240::ui_event_apply_button(lv_event_t *e) +{ + lv_event_code_t event_code = lv_event_get_code(e); + if (event_code == LV_EVENT_CLICKED) { + THIS->checkForConfigTransactionClose(); + } +} + void TFTView_320x240::ui_event_user_button(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); @@ -1523,6 +1577,7 @@ void TFTView_320x240::ui_event_device_reboot_button(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED) { + THIS->controller->closeConfigTransaction(); THIS->controller->requestReboot(5, THIS->ownNode); lv_screen_load_anim(objects.blank_screen, LV_SCR_LOAD_ANIM_FADE_OUT, 4000, 1000, false); lv_obj_add_flag(objects.reboot_panel, LV_OBJ_FLAG_HIDDEN); @@ -1533,6 +1588,7 @@ void TFTView_320x240::ui_event_device_shutdown_button(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED) { + THIS->controller->closeConfigTransaction(); THIS->controller->requestShutdown(5, THIS->ownNode); lv_screen_load_anim(objects.blank_screen, LV_SCR_LOAD_ANIM_FADE_OUT, 4000, 1000, false); lv_obj_add_flag(objects.reboot_panel, LV_OBJ_FLAG_HIDDEN); @@ -2766,8 +2822,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) meshtastic_User user{}; // TODO: don't overwrite is_licensed strcpy(user.short_name, userShort); strcpy(user.long_name, userLong); + THIS->configTransactionOpen(); THIS->controller->sendConfig(user, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_username_panel, LV_OBJ_FLAG_HIDDEN); lv_group_focus_obj(objects.basic_settings_user_button); @@ -2785,8 +2841,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) lv_label_set_text(objects.basic_settings_role_label, buf2); device.role = role; + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_DeviceConfig{device}, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_device_role_panel, LV_OBJ_FLAG_HIDDEN); lv_group_focus_obj(objects.basic_settings_role_button); @@ -2813,8 +2869,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) uint32_t defaultSlot = LoRaPresets::getDefaultSlot(region, THIS->db.config.lora.modem_preset); lora.region = region; lora.channel_num = (defaultSlot <= numChannels ? defaultSlot : 1); + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_LoRaConfig{lora}, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_region_panel, LV_OBJ_FLAG_HIDDEN); lv_group_focus_obj(objects.basic_settings_region_button); @@ -2834,8 +2890,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) lora.use_preset = true; lora.modem_preset = preset; lora.channel_num = channelNum; + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_LoRaConfig{lora}, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_modem_preset_panel, LV_OBJ_FLAG_HIDDEN); lv_group_focus_obj(objects.basic_settings_modem_preset_button); @@ -2868,8 +2924,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) if (strcmp(THIS->db.config.network.wifi_ssid, ssid) != 0 || strcmp(THIS->db.config.network.wifi_psk, psk) != 0) { strcpy(THIS->db.config.network.wifi_ssid, ssid); strcpy(THIS->db.config.network.wifi_psk, psk); + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_NetworkConfig{THIS->db.config.network}, THIS->ownNode); - THIS->notifyReboot(true); } // THIS->enablePanel(objects.home_panel); lv_obj_add_flag(objects.settings_wifi_panel, LV_OBJ_FLAG_HIDDEN); @@ -2881,9 +2937,10 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) meshtastic_Language lang = THIS->val2language(value); if (lang != THIS->db.uiConfig.language) { THIS->db.uiConfig.language = lang; + THIS->configTransactionOpen(); THIS->controller->storeUIConfig(THIS->db.uiConfig); + THIS->checkForConfigTransactionClose(); THIS->controller->requestReboot(3, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_language_panel, LV_OBJ_FLAG_HIDDEN); @@ -3010,7 +3067,7 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) config.use_pwm = false; #endif } - THIS->notifyReboot(true); + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_ModuleConfig_ExternalNotificationConfig{config}, THIS->ownNode); } else if (config.alert_message_buzzer && !alert_message) { silent = true; @@ -3032,7 +3089,7 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) if (option == 2) { THIS->clearChatHistory(); } else { - THIS->notifyReboot(true); + THIS->controller->closeConfigTransaction(); THIS->controller->requestReset(option, THIS->ownNode); } lv_obj_add_flag(objects.settings_reset_panel, LV_OBJ_FLAG_HIDDEN); @@ -4540,6 +4597,11 @@ void TFTView_320x240::notifyShutdown(void) messageAlert(_("Shutting down ..."), true); } +void TFTView_320x240::notifyConfigReboot(bool show) +{ + messageAlert(_("Config changed!\nRebooting ..."), show); +} + void TFTView_320x240::blankScreen(bool enable) { ILOG_DEBUG("%s screen (%s)", enable ? "blank" : "unblank", screenLocked ? "locked" : "timeout"); @@ -5417,7 +5479,10 @@ void TFTView_320x240::enablePanel(lv_obj_t *panel) auto enableButtons = [](lv_obj_t *obj, void *) -> lv_obj_tree_walk_res_t { if (obj->class_p == &lv_button_class) { - lv_obj_clear_state(obj, LV_STATE_DISABLED); + // don't automatically enable the apply button if no settings were changed + if (obj != objects.basic_settings_apply_button || THIS->configRebootRequired) { + lv_obj_clear_state(obj, LV_STATE_DISABLED); + } } return LV_OBJ_TREE_WALK_NEXT; }; @@ -5466,7 +5531,7 @@ void TFTView_320x240::setGroupFocus(lv_obj_t *panel) } else if (panel == objects.settings_screen_lock_panel) { lv_group_focus_obj(objects.screen_lock_button_matrix); } else if (panel == objects.controller_panel) { - lv_group_focus_obj(objects.basic_settings_user_button); + lv_group_focus_obj(objects.basic_settings_apply_button); } else { for (int i = 0; i < lv_obj_get_child_count(panel); i++) { if (panel->spec_attr->children[i]->class_p == &lv_button_class) { diff --git a/source/ViewController.cpp b/source/ViewController.cpp index 697decb..4db2bef 100644 --- a/source/ViewController.cpp +++ b/source/ViewController.cpp @@ -385,6 +385,20 @@ bool ViewController::sendConfig(meshtastic_ModuleConfig_PaxcounterConfig &&paxCo nodeId ? nodeId : myNodeNum); } +bool ViewController::openConfigTransaction(uint32_t nodeId) +{ + return sendAdminMessage( + meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_begin_edit_settings_tag}, + nodeId ? nodeId : myNodeNum); +} + +bool ViewController::closeConfigTransaction(uint32_t nodeId) +{ + return sendAdminMessage( + meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_commit_edit_settings_tag}, + nodeId ? nodeId : myNodeNum); +} + /** * @brief Generic admin message (takes lvalue) *