diff --git a/radio/src/edgetx.cpp b/radio/src/edgetx.cpp index 95490627b..1c1d5b113 100644 --- a/radio/src/edgetx.cpp +++ b/radio/src/edgetx.cpp @@ -797,7 +797,7 @@ void checkAll(bool isBootCheck) MainWindow::instance()->blockUntilClose(true, [=]() { if (dlg->deleted()) return true; if ((tgtime < get_tmr10ms()) || !keyDown()) { - dlg->deleteLater(); + dlg->closeWindow(); return true; } return false; diff --git a/radio/src/gui/colorlcd/controls/color_editor.cpp b/radio/src/gui/colorlcd/controls/color_editor.cpp index da6c56747..77916048e 100644 --- a/radio/src/gui/colorlcd/controls/color_editor.cpp +++ b/radio/src/gui/colorlcd/controls/color_editor.cpp @@ -240,7 +240,7 @@ class BarColorType : public ColorType ~BarColorType() override { for (int i = 0; i < MAX_BARS; i++) { - bars[i]->deleteLater(); + bars[i]->closeWindow(); } }; diff --git a/radio/src/gui/colorlcd/controls/color_picker.cpp b/radio/src/gui/colorlcd/controls/color_picker.cpp index 5064efb5f..2cbde2e25 100644 --- a/radio/src/gui/colorlcd/controls/color_picker.cpp +++ b/radio/src/gui/colorlcd/controls/color_picker.cpp @@ -70,7 +70,7 @@ class ColorEditorPopup : public BaseDialog void onCancel() override { if (setValue) setValue(origColor); - this->deleteLater(); + this->closeWindow(); } public: @@ -164,7 +164,7 @@ class ColorEditorPopup : public BaseDialog new TextButton(hbox, rect_t{0, 0, BTN_W, 0}, STR_SAVE, [=]() -> int8_t { if (setValue) setValue(m_color); - this->deleteLater(); + this->closeWindow(); return 0; }); } diff --git a/radio/src/gui/colorlcd/controls/sourcechoice.cpp b/radio/src/gui/colorlcd/controls/sourcechoice.cpp index fa2b63ecd..c061365fa 100644 --- a/radio/src/gui/colorlcd/controls/sourcechoice.cpp +++ b/radio/src/gui/colorlcd/controls/sourcechoice.cpp @@ -205,7 +205,7 @@ void SourceChoice::openMenu() // fillMenu(menu); - called by MenuToolbar - menu->setCloseHandler([=]() { setEditMode(false); }); + menu->onClosing([=]() { setEditMode(false); }); } SourceChoice::SourceChoice(Window *parent, const rect_t &rect, int16_t vmin, diff --git a/radio/src/gui/colorlcd/controls/switch_warn_dialog.cpp b/radio/src/gui/colorlcd/controls/switch_warn_dialog.cpp index e8301e1d0..dd2b391ef 100644 --- a/radio/src/gui/colorlcd/controls/switch_warn_dialog.cpp +++ b/radio/src/gui/colorlcd/controls/switch_warn_dialog.cpp @@ -38,7 +38,7 @@ void SwitchWarnDialog::checkEvents() uint16_t bad_pots; if (!isSwitchWarningRequired(bad_pots)) { - deleteLater(); + closeWindow(); return; }; @@ -92,5 +92,5 @@ void ThrottleWarnDialog::checkEvents() extern bool isThrottleWarningAlertNeeded(); if (!isThrottleWarningAlertNeeded()) - deleteLater(); + closeWindow(); } diff --git a/radio/src/gui/colorlcd/controls/switchchoice.cpp b/radio/src/gui/colorlcd/controls/switchchoice.cpp index eb8891779..69ce25b17 100644 --- a/radio/src/gui/colorlcd/controls/switchchoice.cpp +++ b/radio/src/gui/colorlcd/controls/switchchoice.cpp @@ -156,7 +156,7 @@ void SwitchChoice::openMenu() // fillMenu(menu); - called by MenuToolbar - menu->setCloseHandler([=]() { setEditMode(false); }); + menu->onClosing([=]() { setEditMode(false); }); } SwitchChoice::SwitchChoice(Window* parent, const rect_t& rect, int vmin, diff --git a/radio/src/gui/colorlcd/lcd.cpp b/radio/src/gui/colorlcd/lcd.cpp index 22a7d5666..4f2f79049 100644 --- a/radio/src/gui/colorlcd/lcd.cpp +++ b/radio/src/gui/colorlcd/lcd.cpp @@ -25,6 +25,7 @@ #include "bitmapbuffer.h" #include "board.h" +#include "debug.h" #include "etx_lv_theme.h" #if !LV_USE_GPU_STM32_DMA2D && !defined(SIMU) #include "dma2d.h" diff --git a/radio/src/gui/colorlcd/libui/button.cpp b/radio/src/gui/colorlcd/libui/button.cpp index 168f1d493..2c1ca7733 100644 --- a/radio/src/gui/colorlcd/libui/button.cpp +++ b/radio/src/gui/colorlcd/libui/button.cpp @@ -65,7 +65,7 @@ ButtonBase::ButtonBase(Window* parent, const rect_t& rect, void ButtonBase::check(bool checked) { - if (!_deleted) { + if (!deleted()) { if (checked != this->checked()) { if (checked) lv_obj_add_state(lvobj, LV_STATE_CHECKED); diff --git a/radio/src/gui/colorlcd/libui/button_matrix.cpp b/radio/src/gui/colorlcd/libui/button_matrix.cpp index 6722fbd17..a57844c50 100644 --- a/radio/src/gui/colorlcd/libui/button_matrix.cpp +++ b/radio/src/gui/colorlcd/libui/button_matrix.cpp @@ -84,21 +84,27 @@ ButtonMatrix::ButtonMatrix(Window* parent, const rect_t& r) : setWindowFlag(NO_FOCUS); lv_obj_add_event_cb(lvobj, btn_matrix_event, LV_EVENT_VALUE_CHANGED, this); -} -ButtonMatrix::~ButtonMatrix() { deallocate(); } + onClosing([=]() { + deallocate(); + }); +} void ButtonMatrix::deallocate() { - if (txt_cnt == 0) return; - for (uint8_t i = 0; i < txt_cnt; i++) { char* txt = lv_btnm_map[i]; if (txt != _filler && txt != _newline && txt != _map_end) free(txt); } - free(lv_btnm_map); - free(txt_index); + if (lv_btnm_map) { + free(lv_btnm_map); + lv_btnm_map = nullptr; + } + if (txt_index) { + free(txt_index); + txt_index = nullptr; + } txt_cnt = 0; btn_cnt = 0; diff --git a/radio/src/gui/colorlcd/libui/button_matrix.h b/radio/src/gui/colorlcd/libui/button_matrix.h index ffef02236..272b2f308 100644 --- a/radio/src/gui/colorlcd/libui/button_matrix.h +++ b/radio/src/gui/colorlcd/libui/button_matrix.h @@ -29,7 +29,6 @@ class ButtonMatrix : public FormField public: ButtonMatrix(Window* parent, const rect_t& rect); - ~ButtonMatrix(); virtual void onPress(uint8_t btn_id) {} virtual bool isActive(uint8_t btn_id) { return false; } diff --git a/radio/src/gui/colorlcd/libui/choice.cpp b/radio/src/gui/colorlcd/libui/choice.cpp index 9566b0687..4402014ea 100644 --- a/radio/src/gui/colorlcd/libui/choice.cpp +++ b/radio/src/gui/colorlcd/libui/choice.cpp @@ -234,5 +234,5 @@ void Choice::openMenu() fillMenu(menu); - menu->setCloseHandler([=]() { setEditMode(false); }); + menu->onClosing([=]() { setEditMode(false); }); } diff --git a/radio/src/gui/colorlcd/libui/dialog.cpp b/radio/src/gui/colorlcd/libui/dialog.cpp index 76cd5ada6..f9257631d 100644 --- a/radio/src/gui/colorlcd/libui/dialog.cpp +++ b/radio/src/gui/colorlcd/libui/dialog.cpp @@ -94,7 +94,7 @@ void ProgressDialog::updateProgress(int percentage) void ProgressDialog::closeDialog() { - deleteLater(); + closeWindow(); onClose(); } @@ -114,7 +114,7 @@ MessageDialog::MessageDialog(const char* title, } } -void MessageDialog::onClicked() { deleteLater(); } +void MessageDialog::onClicked() { closeWindow(); } //----------------------------------------------------------------------------- @@ -130,7 +130,7 @@ DynamicMessageDialog::DynamicMessageDialog( textHandler, color, textFlags); } -void DynamicMessageDialog::onClicked() { deleteLater(); } +void DynamicMessageDialog::onClicked() { closeWindow(); } //----------------------------------------------------------------------------- @@ -158,7 +158,7 @@ ConfirmDialog::ConfirmDialog(const char* title, }); new TextButton(box, rect_t{0, 0, 96, 0}, STR_YES, [=]() -> int8_t { - this->deleteLater(); + this->closeWindow(); this->confirmHandler(); return 0; }); @@ -166,7 +166,7 @@ ConfirmDialog::ConfirmDialog(const char* title, void ConfirmDialog::onCancel() { - deleteLater(); + closeWindow(); if (cancelHandler) cancelHandler(); } @@ -209,12 +209,12 @@ LabelDialog::LabelDialog(const char *label, int length, const char* title, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_BETWEEN); new TextButton(box, rect_t{0, 0, 96, 0}, STR_CANCEL, [=]() { - deleteLater(); + closeWindow(); return 0; }); new TextButton(box, rect_t{0, 0, 96, 0}, STR_SAVE, [=]() { - deleteLater(); + closeWindow(); if (saveHandler != nullptr) saveHandler(this->label); return 0; }); diff --git a/radio/src/gui/colorlcd/libui/dialog.h b/radio/src/gui/colorlcd/libui/dialog.h index 0568f8ab6..99dffa879 100644 --- a/radio/src/gui/colorlcd/libui/dialog.h +++ b/radio/src/gui/colorlcd/libui/dialog.h @@ -44,7 +44,7 @@ class BaseDialog : public ModalWindow Window* form = nullptr; StaticText* header = nullptr; - void onCancel() override { deleteLater(); } + void onCancel() override { closeWindow(); } void onEvent(event_t event) override {} }; @@ -139,7 +139,7 @@ class LabelDialog : public ModalWindow static constexpr int MAX_LABEL_LENGTH = 255; - void onCancel() override { deleteLater(); } + void onCancel() override { closeWindow(); } protected: std::function saveHandler; diff --git a/radio/src/gui/colorlcd/libui/filechoice.cpp b/radio/src/gui/colorlcd/libui/filechoice.cpp index f3a47b3f7..ea9379cb9 100644 --- a/radio/src/gui/colorlcd/libui/filechoice.cpp +++ b/radio/src/gui/colorlcd/libui/filechoice.cpp @@ -191,7 +191,7 @@ void FileChoice::openMenu() // fillMenu(menu); - called by MenuToolbar - menu->setCloseHandler([=]() { setEditMode(false); }); + menu->onClosing([=]() { setEditMode(false); }); } else { new MessageDialog(STR_SDCARD, STR_NO_FILES_ON_SD); } diff --git a/radio/src/gui/colorlcd/libui/form.cpp b/radio/src/gui/colorlcd/libui/form.cpp index 576258658..f337b6c84 100644 --- a/radio/src/gui/colorlcd/libui/form.cpp +++ b/radio/src/gui/colorlcd/libui/form.cpp @@ -44,6 +44,10 @@ FormField::FormField(Window* parent, const rect_t& rect, LvglCreate objConstruct { setTextFlag(textFlags); lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS); + + onClosing([=]() { + if (isEditMode()) setEditMode(false); + }); } void FormField::setEditMode(bool newEditMode) @@ -73,14 +77,6 @@ void FormField::onCancel() } } -void FormField::deleteLater() -{ - if (_deleted) return; - - if (isEditMode()) setEditMode(false); - Window::deleteLater(); -} - FormLine::FormLine(Window* parent, FlexGridLayout& layout) : Window(parent, rect_t{}), layout(layout) { diff --git a/radio/src/gui/colorlcd/libui/form.h b/radio/src/gui/colorlcd/libui/form.h index 6b16915b7..0e36c47fa 100644 --- a/radio/src/gui/colorlcd/libui/form.h +++ b/radio/src/gui/colorlcd/libui/form.h @@ -95,7 +95,6 @@ class FormField : public Window void onClicked() override; void onCancel() override; - void deleteLater() override; protected: bool editMode = false; diff --git a/radio/src/gui/colorlcd/libui/fullscreen_dialog.cpp b/radio/src/gui/colorlcd/libui/fullscreen_dialog.cpp index 497816d32..49bc79662 100644 --- a/radio/src/gui/colorlcd/libui/fullscreen_dialog.cpp +++ b/radio/src/gui/colorlcd/libui/fullscreen_dialog.cpp @@ -103,7 +103,7 @@ void FullScreenDialog::build() auto btn = new TextButton( this, {LCD_W / 3 - TWOBTN_W / 2, LCD_H - TWOBTN_H - PAD_LARGE, TWOBTN_W, TWOBTN_H}, STR_CANCEL, [=]() { - deleteLater(); + closeWindow(); return 0; }); etx_bg_color(btn->getLvObj(), COLOR_THEME_SECONDARY3_INDEX); @@ -123,7 +123,7 @@ void FullScreenDialog::build() void FullScreenDialog::closeDialog() { if (confirmHandler) confirmHandler(); - deleteLater(); + closeWindow(); } bool FullScreenDialog::onLongPress() @@ -181,7 +181,7 @@ bool confirmationDialog(const char* title, const char* msg, bool checkPwr, if (dialog->deleted()) return true; if (closeCondition && closeCondition()) { confirmed = true; - dialog->deleteLater(); + dialog->closeWindow(); } return confirmed; }); diff --git a/radio/src/gui/colorlcd/libui/fullscreen_dialog.h b/radio/src/gui/colorlcd/libui/fullscreen_dialog.h index 6615e84d7..6ef89a9ab 100644 --- a/radio/src/gui/colorlcd/libui/fullscreen_dialog.h +++ b/radio/src/gui/colorlcd/libui/fullscreen_dialog.h @@ -44,7 +44,7 @@ class FullScreenDialog : public NavWindow void setMessage(const char* text); bool onLongPress() override; - void onCancel() override { deleteLater(); } + void onCancel() override { closeWindow(); } void closeDialog(); diff --git a/radio/src/gui/colorlcd/libui/keyboard_base.cpp b/radio/src/gui/colorlcd/libui/keyboard_base.cpp index 16a661bba..102f498ad 100644 --- a/radio/src/gui/colorlcd/libui/keyboard_base.cpp +++ b/radio/src/gui/colorlcd/libui/keyboard_base.cpp @@ -109,18 +109,15 @@ Keyboard::Keyboard(coord_t height) : // TODO: really needed ??? lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN); -} -Keyboard::~Keyboard() -{ - if (group) lv_group_del(group); -} - -void Keyboard::deleteLater() -{ - if (!_deleted) - hide(false); - NavWindow::deleteLater(); + onClosing([=]() { + // Can't use hide() here, it ignores a keyboard already marked as deleted + if (activeKeyboard == this) { + clearField(false); + activeKeyboard = nullptr; + } + if (group) lv_group_del(group); + }); } void Keyboard::clearField(bool wasCancelled) @@ -160,7 +157,7 @@ void Keyboard::clearField(bool wasCancelled) void Keyboard::hide(bool wasCancelled) { - if (activeKeyboard && !activeKeyboard->_deleted) { + if (activeKeyboard && !activeKeyboard->deleted()) { activeKeyboard->clearField(wasCancelled); lv_obj_add_flag(activeKeyboard->lvobj, LV_OBJ_FLAG_HIDDEN); activeKeyboard = nullptr; diff --git a/radio/src/gui/colorlcd/libui/keyboard_base.h b/radio/src/gui/colorlcd/libui/keyboard_base.h index 30ffce04a..dd8901735 100644 --- a/radio/src/gui/colorlcd/libui/keyboard_base.h +++ b/radio/src/gui/colorlcd/libui/keyboard_base.h @@ -26,7 +26,6 @@ class Keyboard : public NavWindow { public: explicit Keyboard(coord_t height); - ~Keyboard(); void clearField(bool wasCancelled); static void hide(bool wasCancelled); @@ -47,5 +46,4 @@ class Keyboard : public NavWindow void setField(FormField* newField); bool attachKeyboard(); - void deleteLater() override; }; diff --git a/radio/src/gui/colorlcd/libui/keyboard_number.cpp b/radio/src/gui/colorlcd/libui/keyboard_number.cpp index 4864f797e..5e69c6ce9 100644 --- a/radio/src/gui/colorlcd/libui/keyboard_number.cpp +++ b/radio/src/gui/colorlcd/libui/keyboard_number.cpp @@ -130,9 +130,11 @@ NumberKeyboard::NumberKeyboard() : Keyboard(KEYBOARD_HEIGHT) (const char**)number_kb_map, number_kb_ctrl_map); lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_USER_1); -} -NumberKeyboard::~NumberKeyboard() { _instance = nullptr; } + onClosing([=]() { + _instance = nullptr; + }); +} void NumberKeyboard::open(FormField* field) { diff --git a/radio/src/gui/colorlcd/libui/keyboard_number.h b/radio/src/gui/colorlcd/libui/keyboard_number.h index c396ac295..6c47d7d8c 100644 --- a/radio/src/gui/colorlcd/libui/keyboard_number.h +++ b/radio/src/gui/colorlcd/libui/keyboard_number.h @@ -24,7 +24,6 @@ class NumberKeyboard : public Keyboard { public: NumberKeyboard(); - ~NumberKeyboard() override; #if defined(DEBUG_WINDOWS) std::string getName() const override { return "NumberKeyboard"; } diff --git a/radio/src/gui/colorlcd/libui/keyboard_text.cpp b/radio/src/gui/colorlcd/libui/keyboard_text.cpp index ee85e60cc..d65a82f35 100644 --- a/radio/src/gui/colorlcd/libui/keyboard_text.cpp +++ b/radio/src/gui/colorlcd/libui/keyboard_text.cpp @@ -23,9 +23,12 @@ constexpr coord_t KEYBOARD_HEIGHT = LCD_H * 2 / 5; TextKeyboard* TextKeyboard::_instance = nullptr; -TextKeyboard::TextKeyboard() : Keyboard(KEYBOARD_HEIGHT) {} - -TextKeyboard::~TextKeyboard() { _instance = nullptr; } +TextKeyboard::TextKeyboard() : Keyboard(KEYBOARD_HEIGHT) +{ + onClosing([=]() { + _instance = nullptr; + }); +} #if defined(HARDWARE_KEYS) diff --git a/radio/src/gui/colorlcd/libui/keyboard_text.h b/radio/src/gui/colorlcd/libui/keyboard_text.h index 54eada5ab..5ee9ed792 100644 --- a/radio/src/gui/colorlcd/libui/keyboard_text.h +++ b/radio/src/gui/colorlcd/libui/keyboard_text.h @@ -39,7 +39,6 @@ class TextKeyboard : public Keyboard { public: TextKeyboard(); - ~TextKeyboard() override; #if defined(DEBUG_WINDOWS) std::string getName() const override { return "TextKeyboard"; } diff --git a/radio/src/gui/colorlcd/libui/list_line_button.cpp b/radio/src/gui/colorlcd/libui/list_line_button.cpp index e3f44effe..e82474fbc 100644 --- a/radio/src/gui/colorlcd/libui/list_line_button.cpp +++ b/radio/src/gui/colorlcd/libui/list_line_button.cpp @@ -67,11 +67,10 @@ InputMixButtonBase::InputMixButtonBase(Window* parent, uint8_t index) : setWidth(BTN_W); setHeight(ListLineButton::BTN_H); padAll(PAD_ZERO); -} -InputMixButtonBase::~InputMixButtonBase() -{ - if (fm_buffer) free(fm_buffer); + onClosing([=]() { + if (fm_buffer) free(fm_buffer); + }); } void InputMixButtonBase::setWeight(gvar_t value, gvar_t min, gvar_t max) @@ -196,7 +195,7 @@ void InputMixButtonBase::setFlightModes(uint16_t modes) void InputMixButtonBase::checkEvents() { ListLineButton::checkEvents(); - if (!_deleted) { + if (!deleted()) { if (fm_canvas) { bool chkd = lv_obj_get_state(fm_canvas) & LV_STATE_CHECKED; if (chkd != this->checked()) { diff --git a/radio/src/gui/colorlcd/libui/list_line_button.h b/radio/src/gui/colorlcd/libui/list_line_button.h index de7ac9a40..ae715881d 100644 --- a/radio/src/gui/colorlcd/libui/list_line_button.h +++ b/radio/src/gui/colorlcd/libui/list_line_button.h @@ -50,7 +50,6 @@ class InputMixButtonBase : public ListLineButton { public: InputMixButtonBase(Window* parent, uint8_t index); - ~InputMixButtonBase(); void setWeight(gvar_t value, gvar_t min, gvar_t max); void setSource(mixsrc_t idx); diff --git a/radio/src/gui/colorlcd/libui/mainwindow.cpp b/radio/src/gui/colorlcd/libui/mainwindow.cpp index b749aed39..e111b28e1 100644 --- a/radio/src/gui/colorlcd/libui/mainwindow.cpp +++ b/radio/src/gui/colorlcd/libui/mainwindow.cpp @@ -97,7 +97,7 @@ void MainWindow::shutdown() // clear layer stack first for (Window* w = Window::topWindow(); w; w = Window::topWindow()) - w->deleteLater(); + w->closeWindow(); clear(); emptyTrash(); @@ -126,6 +126,9 @@ bool MainWindow::setBackgroundImage(std::string& fileName) return false; } +// Note: windows closed while blocked are not destroyed until control returns +// to the top-level loop. Callers poll the closed window (e.g. dialog->deleted()) +// and outer stack frames may still reference it. void MainWindow::blockUntilClose(bool checkPwr, std::function closeCondition, bool isError) { // reset input devices to avoid diff --git a/radio/src/gui/colorlcd/libui/menu.cpp b/radio/src/gui/colorlcd/libui/menu.cpp index d0bd368d5..a118e01ca 100644 --- a/radio/src/gui/colorlcd/libui/menu.cpp +++ b/radio/src/gui/colorlcd/libui/menu.cpp @@ -190,7 +190,7 @@ class MenuBody : public TableField } else { // delete menu first to avoid // focus issues with onPress() - menu->deleteLater(); + menu->closeWindow(); lines[row]->onPress(); } } @@ -422,7 +422,7 @@ void Menu::removeLines() void Menu::onCancel() { if (cancelHandler) cancelHandler(); - deleteLater(); + closeWindow(); } void Menu::setCancelHandler(std::function handler) diff --git a/radio/src/gui/colorlcd/libui/menutoolbar.cpp b/radio/src/gui/colorlcd/libui/menutoolbar.cpp index 2d55ab304..3dced4091 100644 --- a/radio/src/gui/colorlcd/libui/menutoolbar.cpp +++ b/radio/src/gui/colorlcd/libui/menutoolbar.cpp @@ -87,9 +87,11 @@ MenuToolbar::MenuToolbar(Choice* choice, Menu* menu, const int columns) : else prevFilter(); }); -} -MenuToolbar::~MenuToolbar() { lv_group_del(group); } + onClosing([=]() { + lv_group_del(group); + }); +} void MenuToolbar::resetFilter() { diff --git a/radio/src/gui/colorlcd/libui/menutoolbar.h b/radio/src/gui/colorlcd/libui/menutoolbar.h index 548dc761a..39fedbef7 100644 --- a/radio/src/gui/colorlcd/libui/menutoolbar.h +++ b/radio/src/gui/colorlcd/libui/menutoolbar.h @@ -37,7 +37,6 @@ class MenuToolbar : public Window { public: MenuToolbar(Choice* choice, Menu* menu, const int columns); - ~MenuToolbar(); void resetFilter(); diff --git a/radio/src/gui/colorlcd/libui/messaging.h b/radio/src/gui/colorlcd/libui/messaging.h index 6bde89378..9cfcacbba 100644 --- a/radio/src/gui/colorlcd/libui/messaging.h +++ b/radio/src/gui/colorlcd/libui/messaging.h @@ -36,6 +36,7 @@ class Messaging MENU_CHANGE_FILTER, REFRESH, REFRESH_OUTPUTS_WIDGET, + ON_CLOSE, }; Messaging() {} diff --git a/radio/src/gui/colorlcd/libui/modal_window.cpp b/radio/src/gui/colorlcd/libui/modal_window.cpp index a2a11da61..b3bf33b0d 100644 --- a/radio/src/gui/colorlcd/libui/modal_window.cpp +++ b/radio/src/gui/colorlcd/libui/modal_window.cpp @@ -60,6 +60,6 @@ void ModalWindow::onClicked() if (children.size() > 0) children.front()->onCancel(); else - deleteLater(); + closeWindow(); } } diff --git a/radio/src/gui/colorlcd/libui/page.cpp b/radio/src/gui/colorlcd/libui/page.cpp index 15ee534cb..13d4556ba 100644 --- a/radio/src/gui/colorlcd/libui/page.cpp +++ b/radio/src/gui/colorlcd/libui/page.cpp @@ -113,8 +113,7 @@ Page::Page(EdgeTxIcon icon, PaddingSize padding, bool pauseRefresh) : void Page::onCancel() { - if (!_deleted) - deleteLater(); + closeWindow(); } void Page::onClicked() { Keyboard::hide(false); } diff --git a/radio/src/gui/colorlcd/libui/popups.cpp b/radio/src/gui/colorlcd/libui/popups.cpp index ab6cd42cd..6c8dce758 100644 --- a/radio/src/gui/colorlcd/libui/popups.cpp +++ b/radio/src/gui/colorlcd/libui/popups.cpp @@ -138,7 +138,7 @@ class BubbleDialog : public Window void checkEvents() override { if (lv_tick_elaps(startTime) >= timeout) { - deleteLater(); + closeWindow(); } } diff --git a/radio/src/gui/colorlcd/libui/slider.cpp b/radio/src/gui/colorlcd/libui/slider.cpp index 30dd8f920..6c0f204ec 100644 --- a/radio/src/gui/colorlcd/libui/slider.cpp +++ b/radio/src/gui/colorlcd/libui/slider.cpp @@ -43,6 +43,9 @@ SliderBase::SliderBase(Window* parent, coord_t width, coord_t height, int32_t vm _getValue(std::move(getValue)), _setValue(std::move(setValue)) { + onClosing([=]() { + if (tickPts) delete tickPts; + }); } void SliderBase::update() @@ -58,14 +61,6 @@ void SliderBase::update() } } -void SliderBase::deleteLater() -{ - if (!deleted()) { - if (tickPts) delete tickPts; - Window::deleteLater(); - } -} - void SliderBase::setValue(int value) { if (_setValue != nullptr) @@ -84,7 +79,7 @@ void SliderBase::checkEvents() void SliderBase::enable(bool enabled) { - if (!_deleted && slider) { + if (!deleted() && slider) { if (lv_obj_has_state(slider, LV_STATE_DISABLED) == enabled) { if (enabled) lv_obj_clear_state(slider, LV_STATE_DISABLED); diff --git a/radio/src/gui/colorlcd/libui/slider.h b/radio/src/gui/colorlcd/libui/slider.h index 033d84fab..be0bdc2d5 100644 --- a/radio/src/gui/colorlcd/libui/slider.h +++ b/radio/src/gui/colorlcd/libui/slider.h @@ -42,7 +42,6 @@ class SliderBase : public Window std::function _getValue; std::function _setValue; - void deleteLater() override; void checkEvents() override; static void slider_changed_cb(lv_event_t* e); diff --git a/radio/src/gui/colorlcd/libui/static.cpp b/radio/src/gui/colorlcd/libui/static.cpp index 9578e4aa5..b9d6645b2 100644 --- a/radio/src/gui/colorlcd/libui/static.cpp +++ b/radio/src/gui/colorlcd/libui/static.cpp @@ -176,14 +176,11 @@ StaticIcon::StaticIcon(Window* parent, coord_t x, coord_t y, const char* filenam } etx_img_color(lvobj, currentColor, LV_PART_MAIN); -} -void StaticIcon::deleteLater() -{ - if (_deleted) return; - if (mask) free(mask); - mask = nullptr; - Window::deleteLater(); + onClosing([=]() { + if (mask) free(mask); + mask = nullptr; + }); } void StaticIcon::setColor(LcdColorIndex color) @@ -279,6 +276,11 @@ StaticBitmap::StaticBitmap(Window* parent, const rect_t& rect, setWindowFlag(NO_FOCUS | NO_CLICK); setSource(filename); + + onClosing([=]() { + if (img) delete img; + img = nullptr; + }); } void StaticBitmap::setSource(const char *filename) @@ -302,11 +304,6 @@ void StaticBitmap::clearSource() img = nullptr; } -StaticBitmap::~StaticBitmap() -{ - if (img) delete img; -} - bool StaticBitmap::hasImage() const { return img; @@ -345,15 +342,11 @@ StaticLZ4Image::StaticLZ4Image(Window* parent, coord_t x, coord_t y, } lv_canvas_set_buffer(lvobj, imgData, w, h, LV_IMG_CF_TRUE_COLOR_ALPHA); -} -void StaticLZ4Image::deleteLater() -{ - if (!deleted()) { + onClosing([=]() { if (imgData) lv_mem_free(imgData); imgData = nullptr; - Window::deleteLater(); - } + }); } //----------------------------------------------------------------------------- diff --git a/radio/src/gui/colorlcd/libui/static.h b/radio/src/gui/colorlcd/libui/static.h index 9caf8859e..d2c977fc4 100644 --- a/radio/src/gui/colorlcd/libui/static.h +++ b/radio/src/gui/colorlcd/libui/static.h @@ -126,8 +126,6 @@ class StaticIcon : public Window StaticIcon(Window *parent, coord_t x, coord_t y, const char* filename, LcdColorIndex color); - void deleteLater() override; - #if defined(DEBUG_WINDOWS) std::string getName() const override { return "StaticIcon"; } #endif @@ -171,7 +169,6 @@ class StaticBitmap : public Window public: StaticBitmap(Window *parent, const rect_t &rect, const char *filename = nullptr); - ~StaticBitmap(); #if defined(DEBUG_WINDOWS) std::string getName() const override { return "StaticBitmap"; } @@ -199,8 +196,6 @@ class StaticLZ4Image : public Window protected: uint8_t *imgData = nullptr; - - void deleteLater() override; }; //----------------------------------------------------------------------------- diff --git a/radio/src/gui/colorlcd/libui/table.cpp b/radio/src/gui/colorlcd/libui/table.cpp index 798854bb8..74f4498ec 100644 --- a/radio/src/gui/colorlcd/libui/table.cpp +++ b/radio/src/gui/colorlcd/libui/table.cpp @@ -180,6 +180,16 @@ TableField::TableField(Window* parent, const rect_t& rect) : setWindowFlag(OPAQUE); lv_table_set_col_cnt(lvobj, 1); + + onClosing([=]() { + if (autoedit) { + lv_group_del(group); + if (oldGroup) + assignLvGroup(oldGroup, true); + else + lv_group_set_default(nullptr); + } + }); } void TableField::setRowCount(uint16_t rows) @@ -281,17 +291,3 @@ void TableField::setAutoEdit() } }); } - -void TableField::deleteLater() -{ - if (!deleted()) { - if (autoedit) { - lv_group_del(group); - if (oldGroup) - assignLvGroup(oldGroup, true); - else - lv_group_set_default(nullptr); - } - Window::deleteLater(); - } -} diff --git a/radio/src/gui/colorlcd/libui/table.h b/radio/src/gui/colorlcd/libui/table.h index 7a35ba589..426085e17 100644 --- a/radio/src/gui/colorlcd/libui/table.h +++ b/radio/src/gui/colorlcd/libui/table.h @@ -64,7 +64,5 @@ class TableField : public Window bool onLongPress() override; - void deleteLater() override; - static void force_editing(lv_group_t* g) { lv_group_set_editing(g, true); } }; diff --git a/radio/src/gui/colorlcd/libui/window.cpp b/radio/src/gui/colorlcd/libui/window.cpp index 0642c29a8..7bf001f73 100644 --- a/radio/src/gui/colorlcd/libui/window.cpp +++ b/radio/src/gui/colorlcd/libui/window.cpp @@ -253,19 +253,6 @@ Window::Window(Window *parent, const rect_t &rect, LvglCreate objConstruct) : } } -Window::~Window() -{ - TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); - - if (children.size() > 0) deleteChildren(); - - if (lvobj != nullptr) { - lv_obj_set_user_data(lvobj, nullptr); - lv_obj_del(lvobj); - lvobj = nullptr; - } -} - void Window::delayLoader(lv_event_t* e) { auto w = (Window*)lv_obj_get_user_data(lv_event_get_target(e)); @@ -370,9 +357,9 @@ void Window::clearTextFlag(LcdFlags flag) { textFlags &= ~flag; } void Window::attach(Window *newParent) { - if (parent) detach(); - parent = newParent; + detach(); if (newParent) { + parent = newParent; newParent->addChild(this); } } @@ -385,27 +372,45 @@ void Window::detach() } } -void Window::deleteLater() +void Window::onClosing(CloseHandler h) { - if (_deleted) return; - _deleted = true; + closeHandlers.push_back(std::move(h)); +} - TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); +// Close the window, and all its children. +// Remove from LVGL and save the window pointer for later destruction. +void Window::closeWindow() +{ + TRACE_WINDOWS("Close %p %s %s", this, getWindowDebugString().c_str(), deleted() ? "DELETED" : ""); + if (!deleted()) { + _deleted = true; - detach(); - deleteChildren(); + // Recursively delete child objects + deleteChildren(); - popLayer(); + // Remove from parent + detach(); - if (closeHandler) - closeHandler(); + // Remove layer (if needed) before calling handlers, so any windows + // created by a handler are added to the restored lv_group + popLayer(); - Window::trash.push_back(this); + // Call onClosing handler functions (reverse order of creation) + for (auto it = closeHandlers.rbegin(); it != closeHandlers.rend(); ++it) { + (*it)(); + } + closeHandlers.clear(); - if (lvobj != nullptr) { - auto obj = lvobj; - lvobj = nullptr; - lv_obj_del(obj); + // Save for destructor call + trash.push_back(this); + + // Remove from LVGL + if (lvobj != nullptr) { + auto obj = lvobj; + lvobj = nullptr; + lv_obj_set_user_data(obj, nullptr); + lv_obj_del(obj); + } } } @@ -421,7 +426,7 @@ void Window::clear() void Window::deleteChildren() { while (!children.empty()) - children.back()->deleteLater(); + children.back()->closeWindow(); } bool Window::hasFocus() const @@ -516,7 +521,7 @@ FormLine *Window::newLine(FlexGridLayout &layout) void Window::show(bool visible) { - if (!_deleted && lvobj) { + if (!deleted() && lvobj) { if (lv_obj_has_flag(lvobj, LV_OBJ_FLAG_HIDDEN) == visible) { if (visible) lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_HIDDEN); @@ -528,7 +533,7 @@ void Window::show(bool visible) bool Window::isVisible() { - return !_deleted && lvobj && !lv_obj_has_flag(lvobj, LV_OBJ_FLAG_HIDDEN); + return !deleted() && lvobj && !lv_obj_has_flag(lvobj, LV_OBJ_FLAG_HIDDEN); } bool Window::isOnScreen() @@ -542,7 +547,7 @@ bool Window::isOnScreen() void Window::enable(bool enabled) { - if (!_deleted && lvobj) { + if (!deleted() && lvobj) { if (lv_obj_has_state(lvobj, LV_STATE_DISABLED) == enabled) { if (enabled) lv_obj_clear_state(lvobj, LV_STATE_DISABLED); diff --git a/radio/src/gui/colorlcd/libui/window.h b/radio/src/gui/colorlcd/libui/window.h index bb287626a..66b7182fb 100644 --- a/radio/src/gui/colorlcd/libui/window.h +++ b/radio/src/gui/colorlcd/libui/window.h @@ -53,7 +53,7 @@ class Window Window(const rect_t &rect); Window(Window *parent, const rect_t &rect, LvglCreate objConstruct = nullptr); - virtual ~Window(); + virtual ~Window() {} #if defined(DEBUG_WINDOWS) virtual std::string getName() const; @@ -75,7 +75,7 @@ class Window LcdFlags getTextFlags() const { return textFlags; } typedef std::function CloseHandler; - void setCloseHandler(CloseHandler h) { closeHandler = std::move(h); } + void onClosing(CloseHandler h); typedef std::function FocusHandler; void setFocusHandler(FocusHandler h) { focusHandler = std::move(h); } @@ -84,7 +84,6 @@ class Window void setScrollHandler(ScrollHandler h) { scrollHandler = std::move(h); } virtual void clear(); - virtual void deleteLater(); bool hasFocus() const; @@ -189,8 +188,11 @@ class Window void assignLvGroup(lv_group_t* g, bool setDefault); + void closeWindow(); + protected: static std::list trash; + std::list closeHandlers; rect_t rect; @@ -208,7 +210,6 @@ class Window bool layerCreated = false; bool parentHidden = false; - CloseHandler closeHandler; FocusHandler focusHandler; ScrollHandler scrollHandler; diff --git a/radio/src/gui/colorlcd/mainview/layout.cpp b/radio/src/gui/colorlcd/mainview/layout.cpp index 207b37831..5e41fe83c 100644 --- a/radio/src/gui/colorlcd/mainview/layout.cpp +++ b/radio/src/gui/colorlcd/mainview/layout.cpp @@ -111,7 +111,7 @@ void LayoutFactory::deleteCustomScreens() { for (auto& screen : customScreens) { if (screen) { - screen->deleteLater(); + screen->closeWindow(); screen = nullptr; } } @@ -200,7 +200,7 @@ WidgetsContainer* LayoutFactory::createCustomScreen( auto& screen = customScreens[customScreenIndex]; if (screen != nullptr) - screen->deleteLater(); + screen->closeWindow(); auto viewMain = ViewMain::instance(); screen = create(viewMain, customScreenIndex); @@ -417,7 +417,7 @@ void Layout::load() for (unsigned int i = 0; i < count; i++) { // remove old widget if (widgets[i]) { - widgets[i]->deleteLater(); + widgets[i]->closeWindow(); widgets[i] = nullptr; } } diff --git a/radio/src/gui/colorlcd/mainview/screen_setup.cpp b/radio/src/gui/colorlcd/mainview/screen_setup.cpp index 261a1db6a..c9911cf4a 100644 --- a/radio/src/gui/colorlcd/mainview/screen_setup.cpp +++ b/radio/src/gui/colorlcd/mainview/screen_setup.cpp @@ -73,9 +73,8 @@ class LayoutChoice : public Button menu->select( std::distance(LayoutFactory::getRegisteredLayouts().begin(), it)); - menu->setCloseHandler([=]() { - if (!menu->deleted()) - update(); + menu->onClosing([=]() { + update(); }); } @@ -181,7 +180,7 @@ void ScreenSetupPage::build(Window* window) btn = new TextButton(line, rect_t{}, STR_SETUP_WIDGETS, [=]() -> uint8_t { auto idx = customScreenIndex; - window->getParent()->deleteLater(); + window->getParent()->closeWindow(); new SetupWidgetsPage(idx); return 0; }); @@ -302,7 +301,7 @@ void ScreenSetupPage::addScreen() TRACE("Add screen: add screen: LayoutId = %s", g_model.getScreenLayoutId(newIdx)); #if VERSION_MAJOR == 2 - Window::pageGroup()->deleteLater(); + Window::pageGroup()->closeWindow(); #endif QuickMenu::openPage((QMPage)(QM_UI_SCREEN1 + newIdx)); diff --git a/radio/src/gui/colorlcd/mainview/screen_user_interface.cpp b/radio/src/gui/colorlcd/mainview/screen_user_interface.cpp index 955b25d2b..d538abb3f 100644 --- a/radio/src/gui/colorlcd/mainview/screen_user_interface.cpp +++ b/radio/src/gui/colorlcd/mainview/screen_user_interface.cpp @@ -62,7 +62,7 @@ void ScreenUserInterfacePage::build(Window* window) new TextButton(line, rect_t{}, STR_SETUP_WIDGETS, [=]() -> uint8_t { - window->getParent()->deleteLater(); + window->getParent()->closeWindow(); new SetupTopBarWidgetsPage(); return 0; }); diff --git a/radio/src/gui/colorlcd/mainview/topbar.cpp b/radio/src/gui/colorlcd/mainview/topbar.cpp index 792d7c115..b81bd4383 100644 --- a/radio/src/gui/colorlcd/mainview/topbar.cpp +++ b/radio/src/gui/colorlcd/mainview/topbar.cpp @@ -103,7 +103,7 @@ void SetupTopBarWidgetsPage::onClicked() void SetupTopBarWidgetsPage::onCancel() { - deleteLater(); + closeWindow(); // restore screen setting tab on top QuickMenu::openPage(QM_UI_SETUP); } @@ -210,7 +210,7 @@ void TopBar::load() for (unsigned int i = 0; i < count; i++) { // remove old widget if (widgets[i]) { - widgets[i]->deleteLater(); + widgets[i]->closeWindow(); widgets[i] = nullptr; } } diff --git a/radio/src/gui/colorlcd/mainview/view_main.cpp b/radio/src/gui/colorlcd/mainview/view_main.cpp index e2daa12bd..8913b42c8 100644 --- a/radio/src/gui/colorlcd/mainview/view_main.cpp +++ b/radio/src/gui/colorlcd/mainview/view_main.cpp @@ -104,9 +104,11 @@ ViewMain::ViewMain() : // create last to be on top topbar = new TopBar(this); -} -ViewMain::~ViewMain() { _instance = nullptr; } + onClosing([=]() { + _instance = nullptr; + }); +} void ViewMain::addMainView(WidgetsContainer* view, uint32_t viewId) { @@ -370,7 +372,7 @@ void ViewMain::hideTopBarEdgeTxButton() void ViewMain::_refreshWidgets() { - if (!_deleted) { + if (!deleted()) { topbar->refreshWidgets(isVisible && hasTopbar()); for (int i = 0; i < MAX_CUSTOM_SCREENS; i += 1) { if (customScreens[i]) diff --git a/radio/src/gui/colorlcd/mainview/view_main.h b/radio/src/gui/colorlcd/mainview/view_main.h index 586e752e1..d0a49a9f2 100644 --- a/radio/src/gui/colorlcd/mainview/view_main.h +++ b/radio/src/gui/colorlcd/mainview/view_main.h @@ -32,8 +32,6 @@ class ViewMain : public NavWindow explicit ViewMain(); public: - ~ViewMain() override; - static ViewMain* instance(); #if defined(DEBUG_WINDOWS) diff --git a/radio/src/gui/colorlcd/mainview/widget.h b/radio/src/gui/colorlcd/mainview/widget.h index f4a97eb58..80aa7bd8f 100644 --- a/radio/src/gui/colorlcd/mainview/widget.h +++ b/radio/src/gui/colorlcd/mainview/widget.h @@ -75,8 +75,6 @@ class Widget : public ButtonBase Widget(const WidgetFactory* factory, Window* parent, const rect_t& rect, int screenNum, int zoneNum); - ~Widget() override = default; - const WidgetFactory* getFactory() const { return factory; } const WidgetOption* getOptionDefinitions() const; diff --git a/radio/src/gui/colorlcd/mainview/widget_settings.cpp b/radio/src/gui/colorlcd/mainview/widget_settings.cpp index 03d702dc2..2e274ad38 100644 --- a/radio/src/gui/colorlcd/mainview/widget_settings.cpp +++ b/radio/src/gui/colorlcd/mainview/widget_settings.cpp @@ -221,5 +221,5 @@ WidgetSettings::WidgetSettings(Widget* w) : void WidgetSettings::onCancel() { widget->updateWithoutRefresh(); - deleteLater(); + closeWindow(); } diff --git a/radio/src/gui/colorlcd/mainview/widgets_container.cpp b/radio/src/gui/colorlcd/mainview/widgets_container.cpp index 94ab7c552..684c66f54 100644 --- a/radio/src/gui/colorlcd/mainview/widgets_container.cpp +++ b/radio/src/gui/colorlcd/mainview/widgets_container.cpp @@ -27,16 +27,13 @@ WidgetsContainer::WidgetsContainer(Window* parent, const rect_t& rect, uint8_t z { widgets = new Widget*[zoneCount]; for (int i = 0; i < zoneCount; i += 1) widgets[i] = nullptr; -} -void WidgetsContainer::deleteLater() -{ - if (deleted()) return; - for (int i = 0; i < zoneCount; i += 1) - if (widgets[i]) widgets[i]->deleteLater(); - if (widgets) delete[] widgets; - widgets = nullptr; - Window::deleteLater(); + onClosing([=]() { + for (int i = 0; i < zoneCount; i += 1) + if (widgets[i]) widgets[i]->closeWindow(); + if (widgets) delete[] widgets; + widgets = nullptr; + }); } Widget* WidgetsContainer::getWidget(unsigned int index) @@ -51,7 +48,7 @@ void WidgetsContainer::removeWidget(unsigned int index) if (index >= zoneCount) return; if (widgets[index]) { - widgets[index]->deleteLater(); + widgets[index]->closeWindow(); } widgets[index] = nullptr; @@ -85,9 +82,9 @@ void WidgetsContainer::showWidgets(bool visible) void WidgetsContainer::refreshWidgets(bool inForeground) { - if (!_deleted) { + if (!deleted()) { for (int i = 0; i < zoneCount; i++) { - if (widgets[i]) { + if (widgets[i] && !widgets[i]->deleted()) { if ((inForeground && widgets[i]->isOnScreen()) || widgets[i]->isFullscreen()) widgets[i]->foreground(); else diff --git a/radio/src/gui/colorlcd/mainview/widgets_container.h b/radio/src/gui/colorlcd/mainview/widgets_container.h index 7fb9a8990..59c86a68a 100644 --- a/radio/src/gui/colorlcd/mainview/widgets_container.h +++ b/radio/src/gui/colorlcd/mainview/widgets_container.h @@ -46,8 +46,6 @@ class WidgetsContainer: public Window virtual bool isLayout() { return false; } virtual bool isAppMode() const { return false; } - void deleteLater() override; - protected: uint8_t zoneCount = 0; Widget** widgets = nullptr; diff --git a/radio/src/gui/colorlcd/mainview/widgets_setup.cpp b/radio/src/gui/colorlcd/mainview/widgets_setup.cpp index 880daf55c..b42b6034e 100644 --- a/radio/src/gui/colorlcd/mainview/widgets_setup.cpp +++ b/radio/src/gui/colorlcd/mainview/widgets_setup.cpp @@ -150,7 +150,7 @@ void SetupWidgetsPage::onClicked() void SetupWidgetsPage::onCancel() { - deleteLater(); + closeWindow(); QuickMenu::openPage((QMPage)(QM_UI_SCREEN1 + customScreenIdx)); diff --git a/radio/src/gui/colorlcd/model/input_edit.cpp b/radio/src/gui/colorlcd/model/input_edit.cpp index eb342a9e2..ec5a780a6 100644 --- a/radio/src/gui/colorlcd/model/input_edit.cpp +++ b/radio/src/gui/colorlcd/model/input_edit.cpp @@ -234,7 +234,7 @@ void InputEditWindow::buildBody(Window* form) void InputEditWindow::checkEvents() { - if (_deleted) return; + if (deleted()) return; ExpoData* input = expoAddress(index); diff --git a/radio/src/gui/colorlcd/model/model_curves.cpp b/radio/src/gui/colorlcd/model/model_curves.cpp index 48f2622cd..262977a0a 100644 --- a/radio/src/gui/colorlcd/model/model_curves.cpp +++ b/radio/src/gui/colorlcd/model/model_curves.cpp @@ -124,7 +124,7 @@ void ModelCurvesPage::rebuild(Window *window) void ModelCurvesPage::editCurve(Window *window, uint8_t curve) { Window *editWindow = new CurveEditWindow(curve); - editWindow->setCloseHandler([=]() { rebuild(window); }); + editWindow->onClosing([=]() { rebuild(window); }); } void ModelCurvesPage::presetMenu(Window *window, uint8_t index) diff --git a/radio/src/gui/colorlcd/model/model_flightmodes.cpp b/radio/src/gui/colorlcd/model/model_flightmodes.cpp index f1a1ee865..c39c0cf1d 100644 --- a/radio/src/gui/colorlcd/model/model_flightmodes.cpp +++ b/radio/src/gui/colorlcd/model/model_flightmodes.cpp @@ -486,7 +486,7 @@ void ModelFlightModesPage::build(Window* form) btn->setWidth(ListLineButton::GRP_W); btn->setPressHandler([=]() { - (new FlightModeEdit(i))->setCloseHandler([=]() { btn->refresh(); }); + (new FlightModeEdit(i))->onClosing([=]() { btn->refresh(); }); return btn->isActive(); }); } diff --git a/radio/src/gui/colorlcd/model/model_gvars.cpp b/radio/src/gui/colorlcd/model/model_gvars.cpp index 7854e17bb..358698dd3 100644 --- a/radio/src/gui/colorlcd/model/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model/model_gvars.cpp @@ -519,7 +519,7 @@ ModelGVarsPage::ModelGVarsPage(const PageDef& pageDef) : void ModelGVarsPage::cleanup() { if (hdr) - hdr->deleteLater(); + hdr->closeWindow(); hdr = nullptr; } @@ -549,7 +549,7 @@ void ModelGVarsPage::build(Window* window) Menu* menu = new Menu(); menu->addLine(STR_EDIT, [=]() { Window* editWindow = new GVarEditWindow(index); - editWindow->setCloseHandler([=]() { rebuild(window); }); + editWindow->onClosing([=]() { rebuild(window); }); }); menu->addLine(STR_CLEAR, [=]() { for (auto& flightMode : g_model.flightModeData) diff --git a/radio/src/gui/colorlcd/model/model_inputs.cpp b/radio/src/gui/colorlcd/model/model_inputs.cpp index c3da3242a..cac1cf743 100644 --- a/radio/src/gui/colorlcd/model/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model/model_inputs.cpp @@ -331,7 +331,7 @@ void ModelInputsPage::editInput(uint8_t input, uint8_t index) if (!line) return; auto edit = new InputEditWindow(input, index); - edit->setCloseHandler([=]() { + edit->onClosing([=]() { Messaging::send(Messaging::REFRESH); group->refresh(); group->adjustHeight(); @@ -368,10 +368,10 @@ void ModelInputsPage::deleteInput(uint8_t index) group->removeLine(line); if (group->getLineCount() == 0) { - group->deleteLater(); + group->closeWindow(); removeGroup(group); } else { - line->deleteLater(); + line->closeWindow(); } removeLine(line); diff --git a/radio/src/gui/colorlcd/model/model_logical_switches.cpp b/radio/src/gui/colorlcd/model/model_logical_switches.cpp index f8740d4a9..47e694356 100644 --- a/radio/src/gui/colorlcd/model/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model/model_logical_switches.cpp @@ -552,7 +552,7 @@ void ModelLogicalSwitchesPage::newLS(Window* window, bool pasteLS) rebuild(window); } else { Window* lsWindow = new LogicalSwitchEditPage(i); - lsWindow->setCloseHandler([=]() { + lsWindow->onClosing([=]() { if (ls->func != LS_FUNC_NONE) { focusIndex = i; rebuild(window); @@ -597,7 +597,7 @@ void ModelLogicalSwitchesPage::build(Window* window) Menu* menu = new Menu(); menu->addLine(STR_EDIT, [=]() { Window* lsWindow = new LogicalSwitchEditPage(i); - lsWindow->setCloseHandler([=]() { + lsWindow->onClosing([=]() { if (ls->func == LS_FUNC_NONE) rebuild(window); else diff --git a/radio/src/gui/colorlcd/model/model_mixer_scripts.cpp b/radio/src/gui/colorlcd/model/model_mixer_scripts.cpp index ca4ab8846..5d22472a6 100644 --- a/radio/src/gui/colorlcd/model/model_mixer_scripts.cpp +++ b/radio/src/gui/colorlcd/model/model_mixer_scripts.cpp @@ -306,5 +306,5 @@ void ModelMixerScriptsPage::build(Window* window, int8_t focusIdx) void ModelMixerScriptsPage::editLine(Window* window, uint8_t idx) { Window* editWindow = new ScriptEditWindow(idx); - editWindow->setCloseHandler([=]() { rebuild(window, idx); }); + editWindow->onClosing([=]() { rebuild(window, idx); }); } diff --git a/radio/src/gui/colorlcd/model/model_mixes.cpp b/radio/src/gui/colorlcd/model/model_mixes.cpp index 859844f32..2a0c5f530 100644 --- a/radio/src/gui/colorlcd/model/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model/model_mixes.cpp @@ -82,13 +82,12 @@ class MixLineButton : public InputMixButtonBase { mplex = new MPlexIcon(parent, index); - delayLoad(); - } + // mplex is a sibling (not a child) so must be closed explicitly + onClosing([=]() { + mplex->closeWindow(); + }); - void deleteLater() override - { - if (mplex) mplex->deleteLater(); - InputMixButtonBase::deleteLater(); + delayLoad(); } void delayedInit() override @@ -350,7 +349,7 @@ void ModelMixesPage::editMix(uint8_t channel, uint8_t index) if (!line) return; auto edit = new MixEditWindow(channel, index); - edit->setCloseHandler([=]() { + edit->onClosing([=]() { MixData* mix = mixAddress(index); if (is_memclear(mix, sizeof(MixData))) { deleteMix(index); @@ -395,10 +394,10 @@ void ModelMixesPage::deleteMix(uint8_t index) group->removeLine(line); if (group->getLineCount() == 0) { - group->deleteLater(); + group->closeWindow(); removeGroup(group); } else { - line->deleteLater(); + line->closeWindow(); } removeLine(line); } diff --git a/radio/src/gui/colorlcd/model/model_outputs.cpp b/radio/src/gui/colorlcd/model/model_outputs.cpp index b749a728c..ad17c19c2 100644 --- a/radio/src/gui/colorlcd/model/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model/model_outputs.cpp @@ -287,5 +287,5 @@ void ModelOutputsPage::build(Window* window) void ModelOutputsPage::editOutput(uint8_t channel, OutputLineButton* btn) { - (new OutputEditWindow(channel))->setCloseHandler([=]() { btn->refresh(); }); + (new OutputEditWindow(channel))->onClosing([=]() { btn->refresh(); }); } diff --git a/radio/src/gui/colorlcd/model/model_select.cpp b/radio/src/gui/colorlcd/model/model_select.cpp index 640281d51..8dc0e2d34 100644 --- a/radio/src/gui/colorlcd/model/model_select.cpp +++ b/radio/src/gui/colorlcd/model/model_select.cpp @@ -360,8 +360,6 @@ class ModelsPageBody : public Window } } - closeHandler(); - // Skip reloading model if re-selecting the active model if (model != modelCellManager.getCurrentModel()) { // store changes (if any) and load selected model @@ -392,6 +390,8 @@ class ModelsPageBody : public Window storageDirty(EE_GENERAL); storageCheck(true); } + + Messaging::send(Messaging::ON_CLOSE); } void duplicateModel(ModelCell *model) @@ -445,7 +445,7 @@ class ModelsPageBody : public Window if (labels.size()) { auto menu = new Menu(true); menu->setTitle(model->modelName); - menu->setCloseHandler([=]() { + menu->onClosing([=]() { if (isDirty) { isDirty = false; update(); @@ -701,7 +701,6 @@ void ModelLabelsWindow::buildBody(Window *window) { // Models List mdlselector = new ModelsPageBody(window, {MDLS_X, MDLS_Y, MDLS_W, MDLS_H}); - mdlselector->setCloseHandler([=]() { onCancel(); }); mdlselector->setLblRefreshFunc([=]() { labelRefreshRequest(); }); auto mdl_obj = mdlselector->getLvObj(); lv_obj_set_style_max_width(mdl_obj, MDLS_W, LV_PART_MAIN); @@ -711,6 +710,8 @@ void ModelLabelsWindow::buildBody(Window *window) if (mdlselector->getSortOrder() == NO_SORT) mdlselector->setSortOrder(NAME_ASC); + closeMessage.subscribe(Messaging::ON_CLOSE, [=](uint32_t) { onCancel(); }); + // Labels lblselector = new ListBox(window, rect_t{LABELS_X, LABELS_Y, LABELS_WIDTH, LABELS_HEIGHT}, getLabels()); diff --git a/radio/src/gui/colorlcd/model/model_select.h b/radio/src/gui/colorlcd/model/model_select.h index 54e54f882..690440af1 100644 --- a/radio/src/gui/colorlcd/model/model_select.h +++ b/radio/src/gui/colorlcd/model/model_select.h @@ -55,6 +55,7 @@ class ModelLabelsWindow : public Page ModelsPageBody *mdlselector; ModelLayoutButton *mdlLayout; std::string currentLabel; + Messaging closeMessage; LabelsVector getLabels() { diff --git a/radio/src/gui/colorlcd/model/model_telemetry.cpp b/radio/src/gui/colorlcd/model/model_telemetry.cpp index b7c23f384..ba650325f 100644 --- a/radio/src/gui/colorlcd/model/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model/model_telemetry.cpp @@ -784,7 +784,7 @@ void ModelTelemetryPage::editSensor(uint8_t index) { lastKnownIndex = -1; Window* editWindow = new SensorEditWindow(index); - editWindow->setCloseHandler([=]() { buildSensorList(index); }); + editWindow->onClosing([=]() { buildSensorList(index); }); } void ModelTelemetryPage::buildSensorList(int8_t focusSensorIndex) diff --git a/radio/src/gui/colorlcd/model/model_templates.cpp b/radio/src/gui/colorlcd/model/model_templates.cpp index 0e2223a74..ca9fad91d 100644 --- a/radio/src/gui/colorlcd/model/model_templates.cpp +++ b/radio/src/gui/colorlcd/model/model_templates.cpp @@ -128,7 +128,7 @@ class SelectTemplate : public TemplatePage auto tb = new TextButton( listWindow, rect_t{0, 0, lv_pct(100), EdgeTxStyles::STD_FONT_HEIGHT * 2}, name, [=]() -> uint8_t { - deleteLater(); + closeWindow(); templateFolderPage->doUpdate(folder, name); return 0; }); diff --git a/radio/src/gui/colorlcd/model/model_templates.h b/radio/src/gui/colorlcd/model/model_templates.h index 8c176ac8f..634599c15 100644 --- a/radio/src/gui/colorlcd/model/model_templates.h +++ b/radio/src/gui/colorlcd/model/model_templates.h @@ -55,7 +55,7 @@ class SelectTemplateFolder : public TemplatePage SelectTemplateFolder(std::function update); void doUpdate(std::string folder, std::string name) { - deleteLater(); + closeWindow(); if (update) update(folder, name); } diff --git a/radio/src/gui/colorlcd/model/model_usbjoystick.cpp b/radio/src/gui/colorlcd/model/model_usbjoystick.cpp index 748dc4b5d..611c61fe4 100644 --- a/radio/src/gui/colorlcd/model/model_usbjoystick.cpp +++ b/radio/src/gui/colorlcd/model/model_usbjoystick.cpp @@ -580,5 +580,5 @@ void ModelUSBJoystickPage::editChannel(uint8_t channel, USBChannelLineButton* btn) { auto chedit = new USBChannelEditWindow(channel); - chedit->setCloseHandler([=]() { this->update(); btn->refresh(); }); + chedit->onClosing([=]() { this->update(); btn->refresh(); }); } diff --git a/radio/src/gui/colorlcd/model/special_functions.cpp b/radio/src/gui/colorlcd/model/special_functions.cpp index da2186d4a..d8456972f 100644 --- a/radio/src/gui/colorlcd/model/special_functions.cpp +++ b/radio/src/gui/colorlcd/model/special_functions.cpp @@ -768,7 +768,7 @@ void FunctionsPage::editSpecialFunction(Window *window, uint8_t index, FunctionLineButton *button) { auto edit = editPage(index); - edit->setCloseHandler([=]() { + edit->onClosing([=]() { CustomFunctionData *cfn = customFunctionData(index); if (cfn->swtch != 0) { focusIndex = index; diff --git a/radio/src/gui/colorlcd/model/trainer_bluetooth.cpp b/radio/src/gui/colorlcd/model/trainer_bluetooth.cpp index bf76ef867..60a620da0 100644 --- a/radio/src/gui/colorlcd/model/trainer_bluetooth.cpp +++ b/radio/src/gui/colorlcd/model/trainer_bluetooth.cpp @@ -125,7 +125,7 @@ void BluetoothTrainerWindow::refresh() } else if (bluetooth.state == BLUETOOTH_STATE_DISCOVER_START) { if(reusableBuffer.moduleSetup.bt.devicesCount && !menuopened) { // On first item found, open menu auto btdm = new BTDiscoverMenu(); - btdm->setCloseHandler([=]() { + btdm->onClosing([=]() { menuopened = false; if(bluetooth.state != BLUETOOTH_STATE_BIND_REQUESTED) bluetooth.state = BLUETOOTH_STATE_IDLE; diff --git a/radio/src/gui/colorlcd/module/access_settings.cpp b/radio/src/gui/colorlcd/module/access_settings.cpp index 49ee88add..39ba8da8a 100644 --- a/radio/src/gui/colorlcd/module/access_settings.cpp +++ b/radio/src/gui/colorlcd/module/access_settings.cpp @@ -55,7 +55,7 @@ class BindWaitDialog : public BaseDialog { new StaticText(form, rect_t{}, STR_WAITING_FOR_RX); - setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); + onClosing([=]() { if (resetOnClose) moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); } void checkEvents() override @@ -66,7 +66,7 @@ class BindWaitDialog : public BaseDialog // returned to normal after bind if (bindInfo.step > BIND_INIT) { removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); - deleteLater(); + closeWindow(); if (bindInfo.step == BIND_OK) { POPUP_INFORMATION(STR_REG_OK); setPXX2ReceiverUsed(moduleIdx, receiverIdx); @@ -103,8 +103,8 @@ class BindWaitDialog : public BaseDialog if (bindInfo.step == BIND_INIT && bindInfo.candidateReceiversCount > 0) { // prevent module mode being reset to NORMAL before exiting - setCloseHandler(nullptr); - deleteLater(); + resetOnClose = false; + closeWindow(); // ... and create RX choice dialog new BindRxChoiceMenu(moduleIdx, receiverIdx); @@ -117,6 +117,7 @@ class BindWaitDialog : public BaseDialog protected: uint8_t moduleIdx; uint8_t receiverIdx; + bool resetOnClose = true; }; class RxOptions : public BaseDialog @@ -284,7 +285,7 @@ uint8_t ReceiverButton::pressBind() }); return 0; }); - menu->setCloseHandler( + menu->onClosing( [=]() { removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); }); } return 0; @@ -375,7 +376,7 @@ RegisterDialog::RegisterDialog(uint8_t moduleIdx) : box->padAll(PAD_MEDIUM); new TextButton(box, rect_t{}, STR_CANCEL, [=]() -> int8_t { - this->deleteLater(); + this->closeWindow(); return 0; }); @@ -385,7 +386,7 @@ RegisterDialog::RegisterDialog(uint8_t moduleIdx) : }); btn_ok->hide(); - setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); + onClosing([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); } void RegisterDialog::start() @@ -419,7 +420,7 @@ void RegisterDialog::checkEvents() // status->hide(); rx_name->update(); } else if (modSetup.registerStep == REGISTER_OK) { - deleteLater(); + closeWindow(); POPUP_INFORMATION(STR_REG_OK); // pop-up call garbage collector, // so that the dialog is alread destroyed @@ -442,7 +443,7 @@ ModuleOptions::ModuleOptions(uint8_t moduleIdx) : moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; state = MO_ReadModuleSettings; #endif - setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); + onClosing([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); } void ModuleOptions::checkEvents() @@ -481,12 +482,12 @@ void ModuleOptions::checkEvents() case MO_WritingSettings: #if defined(SIMU) statusText.clear(); - deleteLater(); + closeWindow(); #else if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && hwSettings.moduleSettings.state == PXX2_SETTINGS_OK) { statusText.clear(); - deleteLater(); + closeWindow(); } #endif break; @@ -605,7 +606,7 @@ void ModuleOptions::update() box->padAll(PAD_MEDIUM); new TextButton(box, rect_t{}, STR_CANCEL, [=]() -> int8_t { - this->deleteLater(); + this->closeWindow(); return 0; }); @@ -645,7 +646,7 @@ RxOptions::RxOptions(uint8_t moduleIdx, uint8_t rxIdx) : hwSettings.receiverSettings.outputsMapping[i] = i; } #endif - setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); + onClosing([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); } void RxOptions::checkEvents() @@ -705,12 +706,12 @@ void RxOptions::checkEvents() case RO_WritingSettings: #if defined(SIMU) statusText.clear(); - deleteLater(); + closeWindow(); #else if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && hwSettings.receiverSettings.state == PXX2_SETTINGS_OK) { statusText.clear(); - deleteLater(); + closeWindow(); } #endif break; @@ -961,7 +962,7 @@ void RxOptions::update() box->padAll(PAD_MEDIUM); new TextButton(box, rect_t{}, STR_CANCEL, [=]() -> int8_t { - this->deleteLater(); + this->closeWindow(); return 0; }); diff --git a/radio/src/gui/colorlcd/module/module_setup.cpp b/radio/src/gui/colorlcd/module/module_setup.cpp index d42fc2d70..946eae1da 100644 --- a/radio/src/gui/colorlcd/module/module_setup.cpp +++ b/radio/src/gui/colorlcd/module/module_setup.cpp @@ -534,7 +534,7 @@ class ModuleWindow : public Window getRxStatLabels()->label, 50, COLOR_THEME_SECONDARY1_INDEX, CENTERED | FONT(XL)); - rssiDialog->setCloseHandler([this, closeHandler]() { + rssiDialog->onClosing([this, closeHandler]() { rangeButton->check(false); moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; if (closeHandler) closeHandler(); @@ -719,7 +719,7 @@ class ModuleSubTypeChoice : public Choice auto menu = new Menu(); if (menuTitle) menu->setTitle(menuTitle); - menu->setCloseHandler([=]() { setEditMode(false); }); + menu->onClosing([=]() { setEditMode(false); }); setEditMode(true); diff --git a/radio/src/gui/colorlcd/radio/hw_inputs.cpp b/radio/src/gui/colorlcd/radio/hw_inputs.cpp index ac78dc1c8..59d60a1a3 100644 --- a/radio/src/gui/colorlcd/radio/hw_inputs.cpp +++ b/radio/src/gui/colorlcd/radio/hw_inputs.cpp @@ -126,7 +126,7 @@ HWPots::HWPots(Window* parent) : potsChanged = false; - setCloseHandler([=]() { + onClosing([=]() { if (potsChanged) LayoutFactory::loadCustomScreens(); }); diff --git a/radio/src/gui/colorlcd/radio/radio_calibration.cpp b/radio/src/gui/colorlcd/radio/radio_calibration.cpp index 2967b62f1..08d29b965 100644 --- a/radio/src/gui/colorlcd/radio/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio/radio_calibration.cpp @@ -236,7 +236,7 @@ void RadioCalibrationPage::nextStep() menuCalibrationState++; if (menuCalibrationState == CALIB_FINISHED) - deleteLater(); + closeWindow(); if (menuCalibrationState == CALIB_STORE) adcCalibStore(); diff --git a/radio/src/gui/colorlcd/radio/radio_diaganas.cpp b/radio/src/gui/colorlcd/radio/radio_diaganas.cpp index c8a375d0d..553eb55e7 100644 --- a/radio/src/gui/colorlcd/radio/radio_diaganas.cpp +++ b/radio/src/gui/colorlcd/radio/radio_diaganas.cpp @@ -240,6 +240,13 @@ class AnaCalibratedViewWindow : public AnaViewWindow #endif // defined(HARDWARE_TOUCH) setHeight(parent->height()); + +#if defined(HARDWARE_TOUCH) + onClosing([=]() { + lv_obj_del(touchLines[0]); + lv_obj_del(touchLines[1]); + }); +#endif } #if defined(HARDWARE_TOUCH) @@ -263,26 +270,16 @@ class AnaCalibratedViewWindow : public AnaViewWindow lv_obj_add_flag(touchLines[1], LV_OBJ_FLAG_HIDDEN); } } - - void deleteLater() override - { - if (!deleted()) { - // Attached to parent->parent window - lv_obj_del(touchLines[0]); - lv_obj_del(touchLines[1]); - AnaViewWindow::deleteLater(); - } - } #endif static LAYOUT_SIZE(TSI2CEventsCol, 5, 0) protected: + int16_t column3(int i) override { return anaIn(i); } #if defined(HARDWARE_TOUCH) lv_point_t touchPts[4]; lv_obj_t* touchLines[2]; #endif - int16_t column3(int i) override { return anaIn(i); } }; class AnaFilteredDevViewWindow : public AnaViewWindow diff --git a/radio/src/gui/colorlcd/radio/radio_ghost_module_config.cpp b/radio/src/gui/colorlcd/radio/radio_ghost_module_config.cpp index 0889ee0f2..b517ff341 100644 --- a/radio/src/gui/colorlcd/radio/radio_ghost_module_config.cpp +++ b/radio/src/gui/colorlcd/radio/radio_ghost_module_config.cpp @@ -169,7 +169,7 @@ void RadioGhostModuleConfig::onLongPressRTN() reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_CLOSE; moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; sleep_ms(10); - deleteLater(); + closeWindow(); #if defined(TRIMS_EMULATE_BUTTONS) setHatsAsKeys(false); // switch trims back to normal #endif @@ -187,7 +187,7 @@ void RadioGhostModuleConfig::checkEvents() moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; } else if (reusableBuffer.ghostMenu.menuStatus == GHST_MENU_STATUS_CLOSING) { sleep_ms(10); - deleteLater(); + closeWindow(); #if defined(TRIMS_EMULATE_BUTTONS) setHatsAsKeys(false); // switch trims back to normal #endif diff --git a/radio/src/gui/colorlcd/radio/radio_mic_recorder.cpp b/radio/src/gui/colorlcd/radio/radio_mic_recorder.cpp index 77d76155c..27df7cae8 100644 --- a/radio/src/gui/colorlcd/radio/radio_mic_recorder.cpp +++ b/radio/src/gui/colorlcd/radio/radio_mic_recorder.cpp @@ -393,17 +393,12 @@ RadioMicRecorder::RadioMicRecorder() : buildHeader(header); buildBody(body); enterIdle(); -} -void RadioMicRecorder::deleteLater() -{ - if (!deleted()) { + onClosing([=]() { if (recorder.isRecording()) recorder.stop(); if (isPlayingTake()) audioQueue.stopAll(); pdmStop(); - - Page::deleteLater(); - } + }); } void RadioMicRecorder::buildHeader(Window* window) diff --git a/radio/src/gui/colorlcd/radio/radio_mic_recorder.h b/radio/src/gui/colorlcd/radio/radio_mic_recorder.h index bd67557ce..8e6ab5eb0 100644 --- a/radio/src/gui/colorlcd/radio/radio_mic_recorder.h +++ b/radio/src/gui/colorlcd/radio/radio_mic_recorder.h @@ -75,7 +75,6 @@ class RadioMicRecorder : public Page void buildHeader(Window* window); void buildBody(Window* window); void checkEvents() override; - void deleteLater() override; void onActionPressed(); void onPlayPressed(); diff --git a/radio/src/gui/colorlcd/radio/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio/radio_sdmanager.cpp index f3707de22..0c1e4742a 100644 --- a/radio/src/gui/colorlcd/radio/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio/radio_sdmanager.cpp @@ -69,7 +69,7 @@ class FlashDialog: public FullScreenDialog progress->setValue(total > 0 ? count * 100 / total : 0); lv_refr_now(nullptr); }); - deleteLater(); + closeWindow(); } protected: @@ -104,7 +104,7 @@ class FrskyOtaFlashDialog : public BaseDialog reusableBuffer.sdManager.otaUpdateInformation.module = module; moduleState[reusableBuffer.sdManager.otaUpdateInformation.module].startBind(&reusableBuffer.sdManager.otaUpdateInformation, onUpdateStateChangedCallbackFor(this)); - setCloseHandler([=]() { moduleState[reusableBuffer.sdManager.otaUpdateInformation.module].mode = MODULE_MODE_NORMAL; }); + onClosing([=]() { moduleState[reusableBuffer.sdManager.otaUpdateInformation.module].mode = MODULE_MODE_NORMAL; }); } void onUpdateConfirmation() @@ -113,7 +113,7 @@ class FrskyOtaFlashDialog : public BaseDialog Pxx2OtaUpdate otaUpdate(reusableBuffer.sdManager.otaUpdateInformation.module, destination->candidateReceiversNames[destination->selectedReceiverIndex]); auto dialog = new FlashDialog(otaUpdate); dialog->flash(destination->filename); - deleteLater(); + closeWindow(); } void onUpdateStateChanged() @@ -136,9 +136,9 @@ class FrskyOtaFlashDialog : public BaseDialog updateConfirmDialog = new ConfirmDialog(getPXX2ReceiverName(modelId), std::string(reusableBuffer.sdManager.otaReceiverVersion).c_str(), [=]() { onUpdateConfirmation(); }, - [=]() { deleteLater(); }); + [=]() { closeWindow(); }); } else { - deleteLater(); + closeWindow(); POPUP_WARNING(STR_OTA_UPDATE_ERROR, STR_UNSUPPORTED_RX); } } @@ -156,8 +156,8 @@ class FrskyOtaFlashDialog : public BaseDialog rxChoiceMenu->setCancelHandler([=]() { // Seems menu didn't delete itself before call cancelHandler(). // Delete the menu explicity to ensure menu is deleted before dialog. - rxChoiceMenu->deleteLater(); - deleteLater(); + rxChoiceMenu->closeWindow(); + closeWindow(); }); } else { rxChoiceMenu->removeLines(); diff --git a/radio/src/gui/colorlcd/radio/radio_spectrum_analyser.cpp b/radio/src/gui/colorlcd/radio/radio_spectrum_analyser.cpp index 835dbefb1..db71980a1 100644 --- a/radio/src/gui/colorlcd/radio/radio_spectrum_analyser.cpp +++ b/radio/src/gui/colorlcd/radio/radio_spectrum_analyser.cpp @@ -323,7 +323,7 @@ class SpectrumWindow : public Window RadioSpectrumAnalyser::RadioSpectrumAnalyser(uint8_t moduleIdx) : Page(ICON_RADIO_TOOLS, PAD_ZERO), moduleIdx(moduleIdx) { - setCloseHandler([=]() { stop(); }); + onClosing([=]() { stop(); }); init(); buildHeader(header); buildBody(body); diff --git a/radio/src/gui/colorlcd/radio/radio_theme.cpp b/radio/src/gui/colorlcd/radio/radio_theme.cpp index 45554438c..3158b40cd 100644 --- a/radio/src/gui/colorlcd/radio/radio_theme.cpp +++ b/radio/src/gui/colorlcd/radio/radio_theme.cpp @@ -148,7 +148,7 @@ class ThemeDetailsDialog : public BaseDialog auto button = new TextButton(line, rect_t{0, 0, lv_pct(30), 0}, STR_CANCEL, [=]() { - deleteLater(); + closeWindow(); return 0; }); lv_obj_set_grid_cell(button->getLvObj(), LV_GRID_ALIGN_CENTER, 0, 1, @@ -163,7 +163,7 @@ class ThemeDetailsDialog : public BaseDialog if (!saveHandler(this->theme)) return 0; } - deleteLater(); + closeWindow(); return 0; }); lv_obj_set_grid_cell(button->getLvObj(), LV_GRID_ALIGN_CENTER, 1, 1, @@ -190,6 +190,10 @@ class ColorEditPage : public Page { buildHead(header); buildBody(body); + + onClosing([=]() { + if (_updateHandler != nullptr) _updateHandler(); + }); } void setActiveColorBar(int activeTab) @@ -221,12 +225,6 @@ class ColorEditPage : public Page ColorSwatch *_colorSquare = nullptr; StaticText *_hexBox = nullptr; - void deleteLater() override - { - if (_updateHandler != nullptr) _updateHandler(); - Page::deleteLater(); - } - void setHexStr(uint32_t rgb) { if (_hexBox) { @@ -347,11 +345,11 @@ class ThemeEditPage : public Page if (saveHandler != nullptr) { saveHandler(_theme); } - deleteLater(); + closeWindow(); }, - [=]() { deleteLater(); }); + [=]() { closeWindow(); }); } else { - deleteLater(); + closeWindow(); } } diff --git a/radio/src/gui/colorlcd/setup_menus/pagegroup.cpp b/radio/src/gui/colorlcd/setup_menus/pagegroup.cpp index b4084c2b4..c165a97e0 100644 --- a/radio/src/gui/colorlcd/setup_menus/pagegroup.cpp +++ b/radio/src/gui/colorlcd/setup_menus/pagegroup.cpp @@ -154,6 +154,12 @@ PageGroupHeaderBase::PageGroupHeaderBase(Window* parent, coord_t height, EdgeTxI selectedIcon = new SelectedTabIcon(carousel); #endif + + onClosing([=]() { + for (uint8_t i = 0; i < pages.size(); i += 1) + delete pages[i]; + pages.clear(); + }); } #if VERSION_MAJOR == 2 @@ -233,17 +239,6 @@ bool PageGroupHeaderBase::hasSubMenu(QMPage qmPage) return false; } -void PageGroupHeaderBase::deleteLater() -{ - if (deleted()) return; - - for (uint8_t i = 0; i < pages.size(); i += 1) - delete pages[i]; - pages.clear(); - - Window::deleteLater(); -} - #if VERSION_MAJOR == 2 void PageGroupHeaderBase::checkEvents() { @@ -323,9 +318,7 @@ void PageGroupBase::onClicked() { Keyboard::hide(false); } void PageGroupBase::onCancel() { - if (!_deleted) { - deleteLater(); - } + closeWindow(); } uint8_t PageGroupBase::tabCount() const @@ -349,7 +342,7 @@ void PageGroupBase::setCurrentTab(unsigned index) PageGroupItem* tab = header->pageTab(index); - if (tab != currentTab && !deleted()) { + if (tab != currentTab) { header->setTitle(tab->getTitle().c_str()); #if VERSION_MAJOR > 2 header->setIcon(tab->getIcon()); @@ -453,7 +446,7 @@ PageGroup::PageGroup(EdgeTxIcon icon, const char* title, const PageDef* pages) : #endif #endif - setCloseHandler([]{ + onClosing([=]{ storageCheck(true); ViewMain::instance()->updateTopbarVisibility(); }); diff --git a/radio/src/gui/colorlcd/setup_menus/pagegroup.h b/radio/src/gui/colorlcd/setup_menus/pagegroup.h index 2d860d9c6..b681b9451 100644 --- a/radio/src/gui/colorlcd/setup_menus/pagegroup.h +++ b/radio/src/gui/colorlcd/setup_menus/pagegroup.h @@ -122,8 +122,6 @@ class PageGroupHeaderBase : public Window bool isCurrent(uint8_t idx) const { return currentIndex == idx; } uint8_t tabCount() const { return pages.size(); } - void deleteLater() override; - #if VERSION_MAJOR == 2 static LAYOUT_VAL_SCALED(ICON_EXTRA_H, 10) static LAYOUT_VAL_SCALED(MENU_HEADER_BUTTON_WIDTH, 33) diff --git a/radio/src/gui/colorlcd/setup_menus/qmpagechoice.cpp b/radio/src/gui/colorlcd/setup_menus/qmpagechoice.cpp index 679da6e31..e410d3347 100644 --- a/radio/src/gui/colorlcd/setup_menus/qmpagechoice.cpp +++ b/radio/src/gui/colorlcd/setup_menus/qmpagechoice.cpp @@ -66,5 +66,5 @@ void QMPageChoice::openMenu() // fillMenu(menu); - called by MenuToolbar - menu->setCloseHandler([=]() { setEditMode(false); }); + menu->onClosing([=]() { setEditMode(false); }); } diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu.cpp b/radio/src/gui/colorlcd/setup_menus/quick_menu.cpp index 095b8fd90..a02ca58ba 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu.cpp +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu.cpp @@ -211,7 +211,7 @@ class QuickSubMenu void clearSubMenu() { if (subMenu) { - subMenu->deleteLater(); + subMenu->closeWindow(); subMenu = nullptr; } } @@ -249,7 +249,7 @@ void QuickMenu::openQuickMenu() void QuickMenu::shutdownQuickMenu() { - if (instance) instance->deleteLater(); + if (instance) instance->closeWindow(); } QuickMenu::QuickMenu() : @@ -298,14 +298,10 @@ QuickMenu::QuickMenu() : #endif } } -} -void QuickMenu::deleteLater() -{ - if (!_deleted) { + onClosing([=] () { instance = nullptr; - NavWindow::deleteLater(); - } + }); } void QuickMenu::openQM(PageGroupBase* newPageGroup, QMPage newCurPage) diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu.h b/radio/src/gui/colorlcd/setup_menus/quick_menu.h index 749117325..bfd932e1f 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu.h +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu.h @@ -163,8 +163,6 @@ class QuickMenu : public NavWindow void focusMainMenu(); - void deleteLater() override; - static void selected(); static void topMenuAction(int n); diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu_group.cpp b/radio/src/gui/colorlcd/setup_menus/quick_menu_group.cpp index 6bce83737..675cc73dd 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu_group.cpp +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu_group.cpp @@ -144,6 +144,10 @@ QuickMenuGroup::QuickMenuGroup(Window* parent) : { padAll(PAD_OUTLINE); group = lv_group_create(); + + onClosing([=]() { + if (group) lv_group_del(group); + }); } ButtonBase* QuickMenuGroup::addButton(EdgeTxIcon icon, const char* title, @@ -179,14 +183,6 @@ void QuickMenuGroup::setGroup() } } -void QuickMenuGroup::deleteLater() -{ - if (_deleted) return; - - if (group) lv_group_del(group); - Window::deleteLater(); -} - void QuickMenuGroup::setFocus() { if (!curBtn && btns.size() > 0) diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu_group.h b/radio/src/gui/colorlcd/setup_menus/quick_menu_group.h index c57e4b83d..53baee87b 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu_group.h +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu_group.h @@ -55,8 +55,6 @@ class QuickMenuGroup : public Window void prevEntry(); ButtonBase* getFocusedButton(); - void deleteLater() override; - static LAYOUT_VAL_SCALED(QM_ICON_SIZE, 30) static LAYOUT_ORIENTATION(QM_ICON_PAD, PAD_MEDIUM, PAD_SMALL) diff --git a/radio/src/gui/colorlcd/standalone_lua.cpp b/radio/src/gui/colorlcd/standalone_lua.cpp index 78c6c5c4b..a83115412 100644 --- a/radio/src/gui/colorlcd/standalone_lua.cpp +++ b/radio/src/gui/colorlcd/standalone_lua.cpp @@ -175,6 +175,31 @@ StandaloneLuaWindow::StandaloneLuaWindow(bool useLvgl, int initFn, int runFn) : #if defined(USE_HATS_AS_KEYS) setTransposeHatsForLUA(true); #endif + + onClosing([=]() { + luaL_unref(lsStandalone, LUA_REGISTRYINDEX, initFunction); + luaL_unref(lsStandalone, LUA_REGISTRYINDEX, runFunction); + luaLcdBuffer = nullptr; + + luaClose(&lsStandalone); + + if (lcdBuffer) delete lcdBuffer; + lcdBuffer = nullptr; + + luaScriptManager = nullptr; + + _instance = nullptr; + +#if defined(USE_HATS_AS_KEYS) + setTransposeHatsForLUA(false); +#endif + + luaState = prevLuaState; + + luaEmptyEventBuffer(); + + MainWindow::instance()->enableWidgetRefresh(true); + }); } void StandaloneLuaWindow::setup(bool useLvgl, int initFn, int runFn) @@ -188,36 +213,6 @@ StandaloneLuaWindow* StandaloneLuaWindow::instance() return _instance; } -void StandaloneLuaWindow::deleteLater() -{ - if (_deleted) return; - - luaL_unref(lsStandalone, LUA_REGISTRYINDEX, initFunction); - luaL_unref(lsStandalone, LUA_REGISTRYINDEX, runFunction); - luaLcdBuffer = nullptr; - - luaClose(&lsStandalone); - - if (lcdBuffer) delete lcdBuffer; - lcdBuffer = nullptr; - - luaScriptManager = nullptr; - - _instance = nullptr; - -#if defined(USE_HATS_AS_KEYS) - setTransposeHatsForLUA(false); -#endif - - luaState = prevLuaState; - - luaEmptyEventBuffer(); - - MainWindow::instance()->enableWidgetRefresh(true); - - Window::deleteLater(); -} - void StandaloneLuaWindow::checkEvents() { Window::checkEvents(); @@ -241,7 +236,7 @@ void StandaloneLuaWindow::checkEvents() luaNextEvent(&evt); if (evt.event == EVT_KEY_LONG(KEY_EXIT)) { killEvents(evt.event); - deleteLater(); + closeWindow(); } else { if (runFunction != LUA_REFNIL) { lua_rawgeti(lsStandalone, LUA_REGISTRYINDEX, runFunction); @@ -263,7 +258,7 @@ void StandaloneLuaWindow::checkEvents() int scriptResult = lua_tointeger(lsStandalone, -1); lua_pop(lsStandalone, 1); /* pop returned value */ if (scriptResult != 0) { - deleteLater(); + closeWindow(); } else { if (useLvglLayout() && !hasError) { PROTECT_LUA() { @@ -284,7 +279,7 @@ void StandaloneLuaWindow::checkEvents() nextScript[FF_MAX_LFN] = '\0'; _instance = nullptr; lua_settop(lsStandalone, 0); - deleteLater(); + closeWindow(); luaExecStandalone(nextScript); } } diff --git a/radio/src/gui/colorlcd/standalone_lua.h b/radio/src/gui/colorlcd/standalone_lua.h index 6285b069a..989800765 100644 --- a/radio/src/gui/colorlcd/standalone_lua.h +++ b/radio/src/gui/colorlcd/standalone_lua.h @@ -39,8 +39,6 @@ public: static StandaloneLuaWindow* instance(); static void setup(bool useLvgl, int initFn, int runFn); - void deleteLater() override; - #if defined(DEBUG_WINDOWS) std::string getName() const override { return "StandaloneLuaWindow"; } #endif diff --git a/radio/src/gui/colorlcd/startup_shutdown.cpp b/radio/src/gui/colorlcd/startup_shutdown.cpp index 77f8725dd..0bd213d6c 100644 --- a/radio/src/gui/colorlcd/startup_shutdown.cpp +++ b/radio/src/gui/colorlcd/startup_shutdown.cpp @@ -98,7 +98,7 @@ void startSplash() void cancelSplash() { if (splashScreen) { - splashScreen->deleteLater(); + splashScreen->closeWindow(); splashScreen = nullptr; splashStartTime = 0; } @@ -172,7 +172,7 @@ void drawSleepBitmap() void cancelShutdownAnimation() { if (shutdownWindow) { - shutdownWindow->deleteLater(); + shutdownWindow->closeWindow(); shutdownWindow = nullptr; for (int i = 0; i < 4; i += 1) shutdownAnim[i] = nullptr; } diff --git a/radio/src/gui/colorlcd/widgets/gauge.cpp b/radio/src/gui/colorlcd/widgets/gauge.cpp index 1737cd54e..7b3678af9 100644 --- a/radio/src/gui/colorlcd/widgets/gauge.cpp +++ b/radio/src/gui/colorlcd/widgets/gauge.cpp @@ -82,7 +82,7 @@ class GaugeWidget : public Widget void update() override { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; auto widgetData = getPersistentData(); @@ -107,7 +107,7 @@ class GaugeWidget : public Widget void foreground() override { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; auto newValue = getGuageValue(); if (lastValue != newValue) { diff --git a/radio/src/gui/colorlcd/widgets/modelbmp.cpp b/radio/src/gui/colorlcd/widgets/modelbmp.cpp index d85918d2c..bbe5c1660 100644 --- a/radio/src/gui/colorlcd/widgets/modelbmp.cpp +++ b/radio/src/gui/colorlcd/widgets/modelbmp.cpp @@ -55,7 +55,7 @@ class ModelBitmapWidget : public Widget void foreground() override { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; if (getHash() != deps_hash) { update(); @@ -68,7 +68,7 @@ class ModelBitmapWidget : public Widget void update() override { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; auto widgetData = getPersistentData(); diff --git a/radio/src/gui/colorlcd/widgets/outputs.cpp b/radio/src/gui/colorlcd/widgets/outputs.cpp index 58f8f15c5..85f1564e8 100644 --- a/radio/src/gui/colorlcd/widgets/outputs.cpp +++ b/radio/src/gui/colorlcd/widgets/outputs.cpp @@ -95,7 +95,7 @@ class ChannelValue : public Window void refresh() { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; int16_t value = channelOutputs[channel]; diff --git a/radio/src/gui/colorlcd/widgets/radio_info.cpp b/radio/src/gui/colorlcd/widgets/radio_info.cpp index 2426ae5bc..0b38253e8 100644 --- a/radio/src/gui/colorlcd/widgets/radio_info.cpp +++ b/radio/src/gui/colorlcd/widgets/radio_info.cpp @@ -104,7 +104,7 @@ class RadioInfoWidget : public Widget void update() override { - if (_deleted) return; + if (deleted()) return; auto widgetData = getPersistentData(); @@ -116,7 +116,7 @@ class RadioInfoWidget : public Widget void foreground() override { - if (_deleted) return; + if (deleted()) return; usbIcon->show(usbPlugged()); if (getSelectedUsbMode() == USB_UNSELECTED_MODE) @@ -246,14 +246,14 @@ class DateTimeWidget : public Widget void foreground() override { - if (_deleted) return; + if (deleted()) return; Widget::checkEvents(); } void update() override { - if (_deleted) return; + if (deleted()) return; auto widgetData = getPersistentData(); @@ -302,7 +302,7 @@ class InternalGPSWidget : public Widget void foreground() override { - if (_deleted) return; + if (deleted()) return; bool hasGPS = serialGetModePort(UART_MODE_GPS) >= 0; diff --git a/radio/src/gui/colorlcd/widgets/text.cpp b/radio/src/gui/colorlcd/widgets/text.cpp index 4d486f59f..e57301579 100644 --- a/radio/src/gui/colorlcd/widgets/text.cpp +++ b/radio/src/gui/colorlcd/widgets/text.cpp @@ -60,7 +60,7 @@ class TextWidget : public Widget void update() override { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; auto widgetData = getPersistentData(); diff --git a/radio/src/gui/colorlcd/widgets/timer.cpp b/radio/src/gui/colorlcd/widgets/timer.cpp index 55c1bb512..f21d50690 100644 --- a/radio/src/gui/colorlcd/widgets/timer.cpp +++ b/radio/src/gui/colorlcd/widgets/timer.cpp @@ -107,7 +107,7 @@ class TimerWidget : public Widget void foreground() override { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; auto widgetData = getPersistentData(); @@ -236,7 +236,7 @@ class TimerWidget : public Widget void update() override { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; auto widgetData = getPersistentData(); diff --git a/radio/src/gui/colorlcd/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp index a913787f2..46affd581 100644 --- a/radio/src/gui/colorlcd/widgets/value.cpp +++ b/radio/src/gui/colorlcd/widgets/value.cpp @@ -78,7 +78,7 @@ class ValueWidget : public Widget void foreground() override { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; bool changed = false; @@ -187,7 +187,7 @@ class ValueWidget : public Widget void update() override { - if (!loaded || _deleted) return; + if (!loaded || deleted()) return; auto widgetData = getPersistentData(); diff --git a/radio/src/lua/lua_widget.cpp b/radio/src/lua/lua_widget.cpp index 223f24b0a..ae271f68f 100644 --- a/radio/src/lua/lua_widget.cpp +++ b/radio/src/lua/lua_widget.cpp @@ -263,13 +263,12 @@ LuaWidget::LuaWidget(const WidgetFactory* factory, Window* parent, lv_obj_add_event_cb(lvobj, LuaWidget::redraw_cb, LV_EVENT_DRAW_MAIN, nullptr); } -} -LuaWidget::~LuaWidget() -{ - luaL_unref(lsWidgets, LUA_REGISTRYINDEX, zoneRectDataRef); - if (errorMessage) - free(errorMessage); + onClosing([=]() { + luaL_unref(lsWidgets, LUA_REGISTRYINDEX, zoneRectDataRef); + if (errorMessage) + free(errorMessage); + }); } void LuaWidget::onClicked() diff --git a/radio/src/lua/lua_widget.h b/radio/src/lua/lua_widget.h index 44bf26d4d..2ba18e1e0 100644 --- a/radio/src/lua/lua_widget.h +++ b/radio/src/lua/lua_widget.h @@ -108,7 +108,6 @@ class LuaWidget : public Widget, public LuaScriptManager LuaWidget(const WidgetFactory* factory, Window* parent, const rect_t& rect, int screenNum, int zoneNum, int zoneRectDataRef, int optionsDataRef, int createFunctionRef, const std::string& path); - ~LuaWidget() override; #if defined(DEBUG_WINDOWS) std::string getName() const override { return "LuaWidget"; } diff --git a/radio/src/main.cpp b/radio/src/main.cpp index e426bbefa..b30d72280 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -65,7 +65,7 @@ void closeUsbMenu() { if (_usbMenu && !usbPlugged()) { // USB has been unplugged meanwhile - _usbMenu->deleteLater(); + _usbMenu->closeWindow(); } } @@ -75,7 +75,7 @@ void openUsbMenu() _usbMenu = new Menu(); - _usbMenu->setCloseHandler([]() { _usbMenu = nullptr; }); + _usbMenu->onClosing([=]() { _usbMenu = nullptr; }); _usbMenu->setCancelHandler([]() { if (usbPlugged() && (getSelectedUsbMode() == USB_UNSELECTED_MODE)) { @@ -218,7 +218,7 @@ void handleUsbConnection() TRACE("USB stopped"); if (getSelectedUsbMode() == USB_MASS_STORAGE_MODE) { #if defined(COLORLCD) - usbConnectedWindow->deleteLater(); + usbConnectedWindow->closeWindow(); usbConnectedWindow = nullptr; // In case the SD card is removed during the session if (!SD_CARD_PRESENT()) { @@ -227,7 +227,7 @@ void handleUsbConnection() MainWindow::instance()->blockUntilClose(true, []() { return SD_CARD_PRESENT(); }, true); - w->deleteLater(); + w->closeWindow(); } edgeTxResume(); #else diff --git a/radio/src/tests/keyboard.cpp b/radio/src/tests/keyboard.cpp new file mode 100644 index 000000000..3022cdf10 --- /dev/null +++ b/radio/src/tests/keyboard.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) EdgeTX + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "gtests.h" + +#if defined(COLORLCD) + +#include "etx_lv_theme.h" +#include "keyboard_text.h" +#include "mainwindow.h" +#include "page.h" +#include "view_main.h" + +static int changeEndCalls = 0; + +class KeyboardTestField : public FormField +{ + public: + KeyboardTestField(Window* parent) : + FormField(parent, {0, 0, 100, 0}, etx_textarea_create) + { + } + + void changeEnd(bool forceChanged = false) override { changeEndCalls += 1; } +}; + +class KeyboardTestPage : public Page +{ + public: + KeyboardTestPage() : Page(ICON_MODEL) { field = new KeyboardTestField(body); } + + KeyboardTestField* field; +}; + +// While open, the keyboard attaches itself to the field's full screen window, +// so closing that window (e.g. touching the back button) also closes the +// keyboard. The field edit must still be completed, and the keyboard must +// not be left as the active keyboard once destroyed. +TEST(Keyboard, closePageWithKeyboardOpen) +{ + // Dialogs and pages are always shown over the main view + ViewMain::instance(); + MainWindow::instance()->run(); + + auto page = new KeyboardTestPage(); + MainWindow::instance()->run(); + TextKeyboard::open(page->field); + MainWindow::instance()->run(); + ASSERT_NE(Keyboard::keyboardWindow(), nullptr); + + changeEndCalls = 0; + page->closeWindow(); + + EXPECT_EQ(changeEndCalls, 1); + // Must not continue if the (soon to be destroyed) keyboard is still active + ASSERT_EQ(Keyboard::keyboardWindow(), nullptr); + + MainWindow::instance()->run(); + + // Keyboard can be opened again on a new page + auto page2 = new KeyboardTestPage(); + MainWindow::instance()->run(); + TextKeyboard::open(page2->field); + MainWindow::instance()->run(); + ASSERT_NE(Keyboard::keyboardWindow(), nullptr); + EXPECT_EQ(Keyboard::keyboardWindow()->getParent(), page2); + EXPECT_TRUE(page2->field->isEditMode()); + + page2->closeWindow(); + MainWindow::instance()->run(); + EXPECT_EQ(Keyboard::keyboardWindow(), nullptr); +} + +#endif diff --git a/radio/src/tests/mainwindow.cpp b/radio/src/tests/mainwindow.cpp new file mode 100644 index 000000000..eb8a51b51 --- /dev/null +++ b/radio/src/tests/mainwindow.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) EdgeTX + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "gtests.h" + +#if defined(COLORLCD) + +#include "mainwindow.h" +#include "view_main.h" + +// MainWindow::blockUntilClose() runs a nested UI loop, typically from inside +// an event handler or checkEvents() call further up the stack. Windows closed +// during that loop must not be destroyed until control returns to the +// top-level loop, as callers poll e.g. dialog->deleted() in their close +// condition, and outer stack frames may still reference the closed windows. + +// Dialogs are always shown over the main view +static void initMainView() +{ + ViewMain::instance(); + MainWindow::instance()->run(); +} + +static bool selfClosingDestroyed = false; + +class SelfClosingWindow : public Window +{ + public: + SelfClosingWindow() : Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}) + { + setWindowFlag(OPAQUE); + pushLayer(); + } + + ~SelfClosingWindow() override { selfClosingDestroyed = true; } + + void checkEvents() override { closeWindow(); } +}; + +TEST(MainWindow, blockUntilCloseDefersWindowDestruction) +{ + initMainView(); + + selfClosingDestroyed = false; + auto w = new SelfClosingWindow(); + + bool destroyedWhileBlocked = false; + MainWindow::instance()->blockUntilClose(false, [&]() { + // Must not dereference 'w' if it has already been destroyed + if (selfClosingDestroyed) { + destroyedWhileBlocked = true; + return true; + } + return w->deleted(); + }); + + EXPECT_FALSE(destroyedWhileBlocked); + + // Top-level loop is now free to destroy it + MainWindow::instance()->run(); + EXPECT_TRUE(selfClosingDestroyed); +} + +// Moves a switch from within the blocking loop, simulating the user +// correcting the switch position while the switch warning is displayed. +class SwitchMover : public Window +{ + public: + SwitchMover(uint8_t sw, int8_t state) : + Window(MainWindow::instance(), {0, 0, 0, 0}), sw(sw), state(state) + { + } + + bool isBubblePopup() override { return true; } + + void checkEvents() override + { + if (++count == 3) { + simuSetSwitch(sw, state); + closeWindow(); + } + } + + protected: + uint8_t sw; + int8_t state; + int count = 0; +}; + +// Real-world path: the switch warning dialog closes itself once the switch +// is corrected, while checkSwitches() polls dialog->deleted(). Relies on +// ASAN (enabled by default for gtests-radio) to report a use-after-free. +TEST(MainWindow, switchWarningClosesSafely) +{ + RADIO_RESET(); + MODEL_RESET(); + initMainView(); + + // Warn unless switch 0 is up; start with it down + g_model.setSwitchWarning(0, 1); + simuSetSwitch(0, 1); + + uint16_t bad_pots; + ASSERT_TRUE(isSwitchWarningRequired(bad_pots)); + + new SwitchMover(0, -1); + checkSwitches(); + + EXPECT_FALSE(isSwitchWarningRequired(bad_pots)); + + MainWindow::instance()->run(); + g_model.setSwitchWarning(0, 0); +} + +#endif