From 168404433c421d2a289897af4cedbbc37ed33d1e Mon Sep 17 00:00:00 2001 From: omigamedev Date: Mon, 15 Jun 2026 19:31:38 +0200 Subject: [PATCH] Use checked handles for app dialogs --- docs/modernization/debt.md | 7 + src/app_dialogs.cpp | 295 +++++++++++++++++++++++++++---- src/node_about.cpp | 2 - src/node_about.h | 2 +- src/node_changelog.cpp | 2 - src/node_changelog.h | 2 +- src/node_dialog_export_ppbr.cpp | 2 - src/node_dialog_layer_rename.cpp | 2 - src/node_dialog_resize.cpp | 2 - src/node_usermanual.cpp | 2 - src/node_usermanual.h | 2 +- 11 files changed, 271 insertions(+), 49 deletions(-) diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 5051109c..76fbe15f 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,13 @@ agent or engineer to remove them without reconstructing context from chat. ## Recent Reductions +- 2026-06-15: `DEBT-0058`/`DEBT-0063` were narrowed again. App-owned About, + Changelog, User Manual, New Document, Open, Browse, Save, Resize, Layer + Rename, and PPBR Export dialogs now open and close through checked overlay + handles in `src/app_dialogs.cpp`, and the corresponding retained dialog nodes + no longer self-destroy their cancel/OK buttons through + `bind_legacy_click_destroys_node(...)`; menu/layout-owned popup families still + remain on separate retained cleanup paths. - 2026-06-15: `DEBT-0036` was narrowed again. `NodeStrokePreview` now drops the retained pass-sequence, mix-execution, final-composite-request, background capture, and preview-copy wrapper structs/functions in favor of direct diff --git a/src/app_dialogs.cpp b/src/app_dialogs.cpp index 61ca2b59..f19c6c8a 100644 --- a/src/app_dialogs.cpp +++ b/src/app_dialogs.cpp @@ -151,17 +151,80 @@ std::shared_ptr App::input_box(const std::string& title, void App::dialog_usermanual() { - (void)pp::panopainter::add_legacy_overlay_node(*this); + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("User manual dialog open failed: main layout anchor is missing"); + return; + } + + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + LOG("User manual dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); + + const auto close_dialog = [overlay_anchor, overlay_handle]() { + const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + }; + + dialog->btn_ok->on_click = [close_dialog](Node*) { + close_dialog(); + }; } void App::dialog_changelog() { - (void)pp::panopainter::add_legacy_overlay_node(*this); + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("Changelog dialog open failed: main layout anchor is missing"); + return; + } + + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + LOG("Changelog dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); + + const auto close_dialog = [overlay_anchor, overlay_handle]() { + const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + }; + + dialog->btn_ok->on_click = [close_dialog](Node*) { + close_dialog(); + }; } void App::dialog_about() { - (void)pp::panopainter::add_legacy_overlay_node(*this); + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("About dialog open failed: main layout anchor is missing"); + return; + } + + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + LOG("About dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); + + const auto close_dialog = [overlay_anchor, overlay_handle]() { + const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + }; + + dialog->btn_ok->on_click = [close_dialog](Node*) { + close_dialog(); + }; } void App::continue_document_workflow_after_optional_save(std::function action) @@ -180,13 +243,31 @@ void App::continue_document_workflow_after_optional_save(std::function a void App::dialog_newdoc() { auto show_dialog = [this] { + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("New document dialog open failed: main layout anchor is missing"); + return; + } + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); dialog->input->set_text("name"); - (void)pp::panopainter::attach_legacy_overlay_node(*this, dialog); - App::I->showKeyboard(); + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + App::I->hideKeyboard(); + LOG("New document dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); + + const auto close_dialog = [this, overlay_anchor, overlay_handle]() { + const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + App::I->hideKeyboard(); + }; + dialog->btn_ok->on_click = [this, dialog](Node*) { std::string name = dialog->input->m_text; @@ -212,9 +293,9 @@ void App::dialog_newdoc() LOG("New document action failed: %s", status.message); }; - dialog->btn_cancel->on_click = [this, dialog](Node*) + dialog->btn_cancel->on_click = [close_dialog](Node*) { - pp::panopainter::close_legacy_dialog_and_hide_keyboard(*this, *dialog); + close_dialog(); }; }; @@ -226,9 +307,25 @@ void App::dialog_open() { auto show_dialog = [this] { // load thumbnail test + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("Open document dialog open failed: main layout anchor is missing"); + return; + } + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); - (void)pp::panopainter::attach_legacy_overlay_node(*this, dialog); + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + LOG("Open document dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); + + const auto close_dialog = [overlay_anchor, overlay_handle]() { + const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + }; dialog->btn_ok->on_click = [this, dialog](Node*) { @@ -246,6 +343,10 @@ void App::dialog_open() // dialog->destroy(); // ActionManager::clear(); }; + dialog->btn_cancel->on_click = [close_dialog](Node*) + { + close_dialog(); + }; }; continue_document_workflow_after_optional_save(show_dialog); @@ -254,21 +355,40 @@ void App::dialog_open() void App::dialog_browse() { auto show_dialog = [this] { - // load thumbnail test - auto dialog = std::make_shared(); + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("Browse document dialog open failed: main layout anchor is missing"); + return; + } + + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); dialog->search_paths = document_browse_roots(); - pp::panopainter::initialize_legacy_overlay_node(*this, *dialog); - (void)pp::panopainter::attach_legacy_overlay_node(*this, dialog); + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + LOG("Browse document dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); - dialog->btn_ok->on_click = [this, dialog](Node*) + const auto close_dialog = [overlay_anchor, overlay_handle]() { + const auto close_status = + pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + }; + + dialog->btn_ok->on_click = [this, dialog, close_dialog](Node*) { if (dialog->is_selected()) { open_document(dialog->selected_path); - pp::panopainter::close_legacy_dialog_node(*dialog); + close_dialog(); } }; + dialog->btn_cancel->on_click = [close_dialog](Node*) + { + close_dialog(); + }; }; continue_document_workflow_after_optional_save(show_dialog); @@ -319,11 +439,31 @@ void App::dialog_save() if (canvas) { + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("Save document dialog open failed: main layout anchor is missing"); + return; + } + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); dialog->input->set_text(doc_name); App::I->showKeyboard(); + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + App::I->hideKeyboard(); + LOG("Save document dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); + + const auto close_dialog = [this, overlay_anchor, overlay_handle]() { + const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + App::I->hideKeyboard(); + }; + dialog->btn_ok->on_click = [this, dialog](Node*) { std::string name = dialog->input->m_text; @@ -343,12 +483,10 @@ void App::dialog_save() if (!status.ok()) LOG("Document file save action failed: %s", status.message); }; - dialog->btn_cancel->on_click = [this, dialog](Node*) + dialog->btn_cancel->on_click = [close_dialog](Node*) { - pp::panopainter::close_legacy_dialog_and_hide_keyboard(*this, *dialog); + close_dialog(); }; - - (void)pp::panopainter::attach_legacy_overlay_node(*this, dialog); } } @@ -411,23 +549,42 @@ void App::dialog_export_depth() void App::dialog_resize() { + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("Resize dialog open failed: main layout anchor is missing"); + return; + } + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + LOG("Resize dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); - (void)pp::panopainter::attach_legacy_overlay_node(*this, dialog); + const auto close_dialog = [overlay_anchor, overlay_handle]() { + const auto close_status = + pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + }; - dialog->btn_ok->on_click = [this,dialog](Node*) + dialog->btn_ok->on_click = [this, dialog, close_dialog](Node*) { const auto plan = pp::app::plan_document_resize( dialog->combo ? dialog->combo->m_current_index : 0); if (!plan) { - pp::panopainter::close_legacy_dialog_node(*dialog); + close_dialog(); return; } const auto status = pp::panopainter::execute_legacy_document_resize_plan(*this, plan.value()); if (!status.ok()) LOG("Document resize failed: %s", status.message); - pp::panopainter::close_legacy_dialog_node(*dialog); + close_dialog(); + }; + dialog->btn_cancel->on_click = [close_dialog](Node*) { + close_dialog(); }; } @@ -446,12 +603,31 @@ void App::dialog_export_cube_faces() void App::dialog_layer_rename() { + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("Layer rename dialog open failed: main layout anchor is missing"); + return; + } + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); dialog->input->set_text(layers->m_current_layer->m_label_text); App::I->showKeyboard(); - (void)pp::panopainter::attach_legacy_overlay_node(*this, dialog); + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + App::I->hideKeyboard(); + LOG("Layer rename dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); + + const auto close_dialog = [this, overlay_anchor, overlay_handle]() { + const auto close_status = + pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + App::I->hideKeyboard(); + }; dialog->btn_ok->on_click = [this,dialog](Node*) { @@ -463,9 +639,9 @@ void App::dialog_layer_rename() if (!status.ok()) LOG("Layer rename failed: %s", status.message); }; - dialog->btn_cancel->on_click = [this, dialog](Node*) + dialog->btn_cancel->on_click = [close_dialog](Node*) { - pp::panopainter::close_legacy_dialog_and_hide_keyboard(*this, *dialog); + close_dialog(); }; } @@ -476,7 +652,26 @@ void App::dialog_preset_download() void App::dialog_ppbr_export() { - auto dialog = pp::panopainter::add_legacy_overlay_node(*this); + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("PPBR export dialog open failed: main layout anchor is missing"); + return; + } + + auto dialog = pp::panopainter::make_legacy_overlay_node(*this); + + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); + if (!overlay) { + LOG("PPBR export dialog open failed: %s", overlay.status().message); + return; + } + const auto overlay_handle = overlay.value(); + + const auto close_dialog = [overlay_anchor, overlay_handle]() { + const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle); + (void)close_status; + }; + dialog->btn_ok->on_click = [this, dialog] (Node*) { const auto request = pp::panopainter::make_legacy_brush_package_export_request(*dialog); @@ -512,6 +707,10 @@ void App::dialog_ppbr_export() LOG("PPBR export failed: %s", status.message); }); }; + dialog->btn_cancel->on_click = [close_dialog](Node*) + { + close_dialog(); + }; } void App::dialog_timelapse_export() @@ -620,11 +819,39 @@ void App::dialog_export_mp4() void App::dialog_whatsnew(bool force_show) { + auto* overlay_anchor = layout[main_id]; + if (!overlay_anchor) { + LOG("What's new dialog open failed: main layout anchor is missing"); + return; + } + + const auto overlay_handle = std::make_shared(); + const auto open_overlay = [overlay_anchor, overlay_handle](const std::shared_ptr& page) { + if (overlay_handle->valid()) { + return; + } + + const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, page); + if (!overlay) { + return; + } + *overlay_handle = overlay.value(); + }; + const auto close_overlay = [overlay_anchor, overlay_handle]() { + if (!overlay_handle->valid()) { + return; + } + + const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, *overlay_handle); + (void)close_status; + *overlay_handle = {}; + }; + auto whatsnew = std::make_shared(); whatsnew->set_manager(&layout); whatsnew->init(); std::string url = fmt::format("https://panopainter.com/app-content/whatsnew/?version={}", g_version_build); - whatsnew->load_url(url, [this, whatsnew, force_show](bool success) { + whatsnew->load_url(url, [whatsnew, force_show, open_overlay](bool success) { if (success) { int last_id = pp::panopainter::legacy_whatsnew_id_or(0); @@ -632,25 +859,25 @@ void App::dialog_whatsnew(bool force_show) { whatsnew->set_title(fmt::format("What's new in version {}", g_version_number)); if (!force_show) - (void)pp::panopainter::attach_legacy_overlay_node(*this, whatsnew); + open_overlay(whatsnew); } } }); - whatsnew->add_button("Reload", 120, [this, whatsnew](Node*) { + whatsnew->add_button("Reload", 120, [whatsnew](Node*) { whatsnew->reload(); }); - whatsnew->add_button("Read Later", 120, [this, whatsnew](Node*) { + whatsnew->add_button("Read Later", 120, [whatsnew, close_overlay](Node*) { pp::panopainter::clear_legacy_whatsnew_id(); pp::panopainter::save_legacy_preferences(); - pp::panopainter::close_legacy_dialog_node(*whatsnew); + close_overlay(); }); - whatsnew->add_button("Close", 100, [this, whatsnew](Node*) { + whatsnew->add_button("Close", 100, [whatsnew, close_overlay](Node*) { pp::panopainter::set_legacy_whatsnew_id(whatsnew->m_page_id); pp::panopainter::save_legacy_preferences(); - pp::panopainter::close_legacy_dialog_node(*whatsnew); + close_overlay(); }); if (force_show) - (void)pp::panopainter::attach_legacy_overlay_node(*this, whatsnew); + open_overlay(whatsnew); } void App::dialog_shortcuts() diff --git a/src/node_about.cpp b/src/node_about.cpp index e74e3db9..98708b25 100644 --- a/src/node_about.cpp +++ b/src/node_about.cpp @@ -1,6 +1,5 @@ #include "pch.h" #include "log.h" -#include "legacy_ui_overlay_services.h" #include "node_about.h" #include "layout.h" @@ -17,7 +16,6 @@ void NodeAbout::init() SetPositioning(YGPositionTypeAbsolute); add_child_file("data/dialogs/about.xml", "about"); btn_ok = find("btn-ok"); - pp::panopainter::bind_legacy_click_destroys_node(*btn_ok, *this); } kEventResult NodeAbout::handle_event(Event* e) diff --git a/src/node_about.h b/src/node_about.h index ea772020..013cc1a7 100644 --- a/src/node_about.h +++ b/src/node_about.h @@ -4,8 +4,8 @@ class NodeAbout : public Node { - NodeButton* btn_ok; public: + NodeButton* btn_ok; virtual Node* clone_instantiate() const override; virtual void init() override; virtual kEventResult handle_event(Event* e) override; diff --git a/src/node_changelog.cpp b/src/node_changelog.cpp index 99138c3f..877b44d1 100644 --- a/src/node_changelog.cpp +++ b/src/node_changelog.cpp @@ -1,6 +1,5 @@ #include "pch.h" #include "log.h" -#include "legacy_ui_overlay_services.h" #include "node_changelog.h" #include "layout.h" @@ -17,7 +16,6 @@ void NodeChangelog::init() SetPositioning(YGPositionTypeAbsolute); add_child_file("data/dialogs/changelog.xml", "changelog"); btn_ok = find("btn-ok"); - pp::panopainter::bind_legacy_click_destroys_node(*btn_ok, *this); } kEventResult NodeChangelog::handle_event(Event* e) diff --git a/src/node_changelog.h b/src/node_changelog.h index 53774ae4..9502ca6c 100644 --- a/src/node_changelog.h +++ b/src/node_changelog.h @@ -4,8 +4,8 @@ class NodeChangelog : public Node { - NodeButton* btn_ok; public: + NodeButton* btn_ok; virtual Node* clone_instantiate() const override; virtual void init() override; virtual kEventResult handle_event(Event* e) override; diff --git a/src/node_dialog_export_ppbr.cpp b/src/node_dialog_export_ppbr.cpp index 3938d97c..466f9626 100644 --- a/src/node_dialog_export_ppbr.cpp +++ b/src/node_dialog_export_ppbr.cpp @@ -3,7 +3,6 @@ #include "node_dialog_export_ppbr.h" #include "app.h" #include "image.h" -#include "legacy_ui_overlay_services.h" Node* NodeDialogExportPPBR::clone_instantiate() const { @@ -31,7 +30,6 @@ void NodeDialogExportPPBR::init_controls() m_dest_path_txt->SetVisibility(false); btn_ok = find("btn-ok"); btn_cancel = find("btn-cancel"); - pp::panopainter::bind_legacy_click_destroys_node(*btn_cancel, *this); btn_header_open = find("header-open"); btn_header_open->on_click = [this] (Node*) { open_header(); diff --git a/src/node_dialog_layer_rename.cpp b/src/node_dialog_layer_rename.cpp index 8bd4af88..c40380c0 100644 --- a/src/node_dialog_layer_rename.cpp +++ b/src/node_dialog_layer_rename.cpp @@ -2,7 +2,6 @@ #include "log.h" #include "node_dialog_layer_rename.h" #include "canvas.h" -#include "legacy_ui_overlay_services.h" #include "node_image_texture.h" Node* NodeDialogLayerRename::clone_instantiate() const @@ -27,7 +26,6 @@ void NodeDialogLayerRename::init_controls() btn_ok = find("btn-ok"); btn_cancel = find("btn-cancel"); input = find("txt-input"); - pp::panopainter::bind_legacy_click_destroys_node(*btn_cancel, *this); } void NodeDialogLayerRename::loaded() diff --git a/src/node_dialog_resize.cpp b/src/node_dialog_resize.cpp index b4b6a7f5..9939172c 100644 --- a/src/node_dialog_resize.cpp +++ b/src/node_dialog_resize.cpp @@ -3,7 +3,6 @@ #include "node_dialog_resize.h" #include "app_core/document_resize.h" #include "canvas.h" -#include "legacy_ui_overlay_services.h" #include "node_image_texture.h" #include @@ -37,7 +36,6 @@ void NodeDialogResize::init_controls() && state.current_resolution_index < static_cast(combo->m_items.size())) { combo->m_current_index = state.current_resolution_index; } - pp::panopainter::bind_legacy_click_destroys_node(*btn_cancel, *this); } void NodeDialogResize::loaded() diff --git a/src/node_usermanual.cpp b/src/node_usermanual.cpp index 9fff5b0a..18b3938d 100644 --- a/src/node_usermanual.cpp +++ b/src/node_usermanual.cpp @@ -1,6 +1,5 @@ #include "pch.h" #include "log.h" -#include "legacy_ui_overlay_services.h" #include "node_usermanual.h" #include "layout.h" @@ -17,7 +16,6 @@ void NodeUserManual::init() SetPositioning(YGPositionTypeAbsolute); add_child_file("data/dialogs/usermanual.xml", "usermanual"); btn_ok = find("btn-ok"); - pp::panopainter::bind_legacy_click_destroys_node(*btn_ok, *this); } kEventResult NodeUserManual::handle_event(Event* e) diff --git a/src/node_usermanual.h b/src/node_usermanual.h index 53b9e04d..5ad980dd 100644 --- a/src/node_usermanual.h +++ b/src/node_usermanual.h @@ -4,8 +4,8 @@ class NodeUserManual : public Node { - NodeButton* btn_ok; public: + NodeButton* btn_ok; virtual Node* clone_instantiate() const override; virtual void init() override; virtual kEventResult handle_event(Event* e) override;