From ce169a3fd6511f6922923e595aafb3d89cb63845 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Mon, 15 Jun 2026 19:33:06 +0200 Subject: [PATCH] Move retained popups to checked handles --- docs/modernization/debt.md | 9 + src/app_layout.cpp | 393 ++++++++++++++++++++++--------- src/legacy_quick_ui_services.cpp | 101 ++++++-- src/node_combobox.cpp | 21 +- src/node_dialog_picker.cpp | 10 +- src/node_panel_stroke.h | 5 +- 6 files changed, 397 insertions(+), 142 deletions(-) diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 76fbe15f..8433cf10 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,15 @@ agent or engineer to remove them without reconstructing context from chat. ## Recent Reductions +- 2026-06-15: `DEBT-0063` was narrowed again. Sidebar stroke/color/layer/grid + popups, file/about/layer menu popups in `src/app_layout.cpp`, quick + brush/color popup+ticker overlays in `src/legacy_quick_ui_services.cpp`, + combo-box popup open/close in `src/node_combobox.cpp`, and picker + outside-click teardown in `src/node_dialog_picker.cpp` now route through + checked overlay handles or explicit handle-safe close callbacks instead of + raw attach-and-destroy popup ownership; remaining popup families still stay + open under the same debt until the last retained dialog/menu surfaces are + converted. - 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 diff --git a/src/app_layout.cpp b/src/app_layout.cpp index 3cf42272..131cbbc9 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -79,6 +79,27 @@ std::shared_ptr add_menu_popup( return popup.value(); } +void close_legacy_overlay_handle_ignoring_status( + Node& anchor, + pp::ui::NodeHandle overlay) noexcept +{ + (void)pp::panopainter::close_legacy_overlay_node(anchor, overlay); +} + +template +void close_legacy_overlay_handles_if_open( + Node& anchor, + const PopupOverlay& popup_overlay, + const TickOverlay& tick_overlay) noexcept +{ + if (popup_overlay) { + close_legacy_overlay_handle_ignoring_status(anchor, popup_overlay.value()); + } + if (tick_overlay) { + close_legacy_overlay_handle_ignoring_status(anchor, tick_overlay.value()); + } +} + pp::app::DocumentLayerMenuPlan make_layer_menu_plan( pp::app::DocumentLayerMenuCommand command, App& app) @@ -463,7 +484,11 @@ void App::init_sidebar() if (auto* button = layout[main_id]->find("btn-stroke")) { button->on_click = [this, button](Node*) { - auto screen = layout[main_id]->m_size; + auto* popup_root = layout[main_id]; + if (!popup_root) { + return; + } + auto screen = popup_root->m_size; glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y); if (stroke->m_parent) { @@ -473,24 +498,35 @@ void App::init_sidebar() pp::panopainter::close_legacy_dialog_node(*fp); } } - (void)pp::panopainter::attach_legacy_overlay_node(*this, stroke); + const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, stroke); + if (!popup_overlay) { + return; + } stroke->SetSize(350, glm::max(100.f, screen.y - pos.y - 50.f)); stroke->find("title")->SetVisibility(true); - auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor(*layout[main_id]); + auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor(*popup_root); tick->SetPositioning(YGPositionTypeAbsolute); tick->SetSize(32, 16); tick->SetPosition(pos.x - 16, pos.y); tick->set_image("data/ui/popup-tick-up.png"); - (void)pp::panopainter::attach_legacy_overlay_node(*this, tick); - layout[main_id]->update(); + auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick); + if (!popup_overlay || !tick_overlay) + { + close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay); + return; + } + const auto popup_handle = popup_overlay.value(); + const auto tick_handle = tick_overlay.value(); + popup_root->update(); stroke->SetPosition(pos.x - stroke->m_size.x / 2.f, pos.y + 16); stroke->SetPositioning(YGPositionTypeAbsolute); pp::panopainter::activate_legacy_popup_overlay(*stroke); auto scroll = stroke->find("scroller"); //scroll->SetHeight(glm::max(100.f, screen.y - pos.y - 200.f)); + stroke->m_popup_overlay_handle = popup_handle; + stroke->m_tick_overlay_handle = tick_handle; - pp::panopainter::bind_legacy_popup_close_destroys_overlay(*stroke, tick); }; } //if (auto* button = layout[main_id]->find("btn-brush")) @@ -512,9 +548,17 @@ void App::init_sidebar() if (auto* button = layout[main_id]->find("btn-color")) { button->on_click = [this, button](Node*) { - auto screen = layout[main_id]->m_size; + auto* popup_root = layout[main_id]; + if (!popup_root) { + return; + } glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y); - (void)pp::panopainter::attach_legacy_overlay_node(*this, color); + const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, color); + if (!popup_overlay) + { + LOG("Color popup overlay failed: %s", popup_overlay.status().message); + return; + } color->find("title")->SetVisibility(true); color->SetSize(350, 350); auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor(*layout[main_id]); @@ -522,20 +566,33 @@ void App::init_sidebar() tick->SetSize(32, 16); tick->SetPosition(pos.x - 16, pos.y); tick->set_image("data/ui/popup-tick-up.png"); - (void)pp::panopainter::attach_legacy_overlay_node(*this, tick); - layout[main_id]->update(); + auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick); + if (!popup_overlay || !tick_overlay) + { + close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay); + return; + } + const auto popup_handle = popup_overlay.value(); + const auto tick_handle = tick_overlay.value(); + popup_root->update(); color->SetPosition(pos.x - color->m_size.x / 2.f, pos.y + 16); color->SetPositioning(YGPositionTypeAbsolute); pp::panopainter::activate_legacy_popup_overlay(*color); - - pp::panopainter::bind_legacy_popup_close_destroys_overlay(*color, tick); + color->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) { + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle); + }; }; } if (auto* button = layout[main_id]->find("btn-layer")) { button->on_click = [this, button](Node*) { - auto screen = layout[main_id]->m_size; + auto* popup_root = layout[main_id]; + if (!popup_root) { + return; + } + auto screen = popup_root->m_size; glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y); layers->find("title")->SetVisibility(true); layers->SetSize(350, YGUndefined); @@ -547,28 +604,45 @@ void App::init_sidebar() pp::panopainter::close_legacy_dialog_node(*fp); } } - (void)pp::panopainter::attach_legacy_overlay_node(*this, layers); - auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor(*layout[main_id]); + const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, layers); + if (!popup_overlay) { + return; + } + auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor(*popup_root); tick->SetPositioning(YGPositionTypeAbsolute); tick->SetSize(32, 16); tick->SetPosition(pos.x - 16, pos.y); tick->set_image("data/ui/popup-tick-up.png"); - (void)pp::panopainter::attach_legacy_overlay_node(*this, tick); - layout[main_id]->update(); + auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick); + if (!popup_overlay || !tick_overlay) + { + close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay); + return; + } + const auto popup_handle = popup_overlay.value(); + const auto tick_handle = tick_overlay.value(); + popup_root->update(); layers->SetPosition(pos.x - layers->m_size.x / 2.f, pos.y + 16); layers->SetPositioning(YGPositionTypeAbsolute); pp::panopainter::activate_legacy_popup_overlay(*layers); auto scroll = layers->find("layers-container"); scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 200.f)); + layers->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) { + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle); + }; - pp::panopainter::bind_legacy_popup_close_destroys_overlay(*layers, tick); }; } if (auto* button = layout[main_id]->find("btn-grids-panel")) { button->on_click = [this, button](Node*) { - auto screen = layout[main_id]->m_size; + auto* popup_root = layout[main_id]; + if (!popup_root) { + return; + } + auto screen = popup_root->m_size; glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y); grid->find("title")->SetVisibility(true); grid->SetSize(350, YGUndefined); @@ -580,14 +654,26 @@ void App::init_sidebar() pp::panopainter::close_legacy_dialog_node(*fp); } } - (void)pp::panopainter::attach_legacy_overlay_node(*this, grid); - auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor(*layout[main_id]); + const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, grid); + if (!popup_overlay) + { + LOG("Grid popup overlay failed: %s", popup_overlay.status().message); + return; + } + auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor(*popup_root); tick->SetPositioning(YGPositionTypeAbsolute); tick->SetSize(32, 16); tick->SetPosition(pos.x - 16, pos.y); tick->set_image("data/ui/popup-tick-up.png"); - (void)pp::panopainter::attach_legacy_overlay_node(*this, tick); - layout[main_id]->update(); + auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick); + if (!popup_overlay || !tick_overlay) + { + close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay); + return; + } + const auto popup_handle = popup_overlay.value(); + const auto tick_handle = tick_overlay.value(); + popup_root->update(); grid->SetPosition(pos.x - grid->m_size.x / 2.f, pos.y + 16); grid->SetPositioning(YGPositionTypeAbsolute); @@ -595,7 +681,10 @@ void App::init_sidebar() auto scroll = grid->find("scroller"); scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 250.f)); - pp::panopainter::bind_legacy_popup_close_destroys_overlay(*grid, tick); + grid->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) { + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle); + }; }; } } @@ -661,113 +750,133 @@ void App::init_menu_file() { if (auto* menu_file = layout[main_id]->find("menu-file")) { - menu_file->on_click = [=](Node*) { + menu_file->on_click = [this, menu_file](Node*) { + auto* popup_root = layout[main_id]; + if (!popup_root) { + return; + } + const auto open_checked_menu_popup = [this, popup_root](const char* id, glm::vec2 pos, float width) + -> std::pair, pp::ui::NodeHandle> + { + auto popup = add_menu_popup(*this, id, pos, width); + if (!popup) { + return {}; + } + pp::panopainter::detach_legacy_node_from_parent(*popup); + auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, popup); + if (!popup_overlay) { + pp::panopainter::destroy_legacy_node(*popup); + return {}; + } + return { popup, popup_overlay.value() }; + }; + glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); - auto popup = add_menu_popup(*this, "file-menu", pos, menu_file->m_size.x); + const auto [popup, popup_handle] = open_checked_menu_popup("file-menu", pos, menu_file->m_size.x); if (!popup) return; if (auto b = popup->find("file-newdoc")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::new_document); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-import")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::import_image); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-open")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::open_project); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-browse")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::browse_cloud); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-save")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::save); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-save-as")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::save_as); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-save-ver")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::save_version); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-export")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::export_jpeg); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-export-tick")) - b->on_click = [this, b, popup](Node*) { + b->on_click = [this, b, popup_root, popup_handle, open_checked_menu_popup](Node*) { glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0); - auto subpopup = add_menu_popup(*this, "file-submenu-export", pos, b->m_size.x); + const auto [subpopup, subpopup_handle] = open_checked_menu_popup("file-submenu-export", pos, b->m_size.x); if (!subpopup) return; - subpopup->find("file-submenu-export-png")->on_click = [this, subpopup, popup](Node*) { + subpopup->find("file-submenu-export-png")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::png); - pp::panopainter::close_legacy_popup_overlay(*popup); - pp::panopainter::close_legacy_popup_overlay(*subpopup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); }; - subpopup->find("file-submenu-export-layers")->on_click = [this, subpopup, popup](Node*) { + subpopup->find("file-submenu-export-layers")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::layers); - pp::panopainter::close_legacy_popup_overlay(*popup); - pp::panopainter::close_legacy_popup_overlay(*subpopup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); }; - subpopup->find("file-submenu-export-cube")->on_click = [this, subpopup, popup](Node*) { + subpopup->find("file-submenu-export-cube")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::cube_faces); - pp::panopainter::close_legacy_popup_overlay(*popup); - pp::panopainter::close_legacy_popup_overlay(*subpopup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); }; - subpopup->find("file-submenu-export-depth")->on_click = [this, subpopup, popup](Node*) { + subpopup->find("file-submenu-export-depth")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::depth); - pp::panopainter::close_legacy_popup_overlay(*popup); - pp::panopainter::close_legacy_popup_overlay(*subpopup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); }; - subpopup->find("file-submenu-export-anim")->on_click = [this, subpopup, popup](Node*) { + subpopup->find("file-submenu-export-anim")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::animation_frames); - pp::panopainter::close_legacy_popup_overlay(*popup); - pp::panopainter::close_legacy_popup_overlay(*subpopup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); }; - subpopup->find("file-submenu-export-anim-mp4")->on_click = [this, subpopup, popup](Node*) { + subpopup->find("file-submenu-export-anim-mp4")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::animation_mp4); - pp::panopainter::close_legacy_popup_overlay(*popup); - pp::panopainter::close_legacy_popup_overlay(*subpopup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); }; - subpopup->find("file-submenu-export-timelapse")->on_click = [this, subpopup, popup](Node*) { + subpopup->find("file-submenu-export-timelapse")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::timelapse); - pp::panopainter::close_legacy_popup_overlay(*popup); - pp::panopainter::close_legacy_popup_overlay(*subpopup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); }; }; if (auto b = popup->find("file-share")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::share); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-resize")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::resize); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-cloud-upload")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::cloud_upload); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; if (auto b = popup->find("file-cloud-browse")) - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { apply_file_menu_plan(*this, pp::app::FileMenuCommand::cloud_browse); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; }; } @@ -798,16 +907,39 @@ void App::init_menu_tools() if (!popup_exp) return; - if (auto tick = popup_exp->find("tools-panels")) tick->on_click = [this, popup_exp](Node* b) + if (auto tick = popup_exp->find("tools-panels")) tick->on_click = [this, popup_exp, main](Node* b) { const auto menu_plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::panels); if (menu_plan.action != pp::app::ToolsMenuAction::show_panels_submenu) return; + pp::panopainter::detach_legacy_node_from_parent(*popup_exp); + const auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*main, popup_exp); + if (!popup_exp_overlay) + return; + const auto popup_exp_handle = popup_exp_overlay.value(); + glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0); auto popup_time = add_menu_popup(*this, "panels-menu", pos, b->m_size.x); if (!popup_time) + { + close_legacy_overlay_handle_ignoring_status(*main, popup_exp_handle); return; + } + pp::panopainter::detach_legacy_node_from_parent(*popup_time); + const auto popup_time_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*main, popup_time); + if (!popup_time_overlay) + { + close_legacy_overlay_handle_ignoring_status(*main, popup_exp_handle); + return; + } + const auto popup_time_handle = popup_time_overlay.value(); + + const auto close_panel_popups = [main, popup_exp_handle, popup_time_handle]() + { + close_legacy_overlay_handle_ignoring_status(*main, popup_exp_handle); + close_legacy_overlay_handle_ignoring_status(*main, popup_time_handle); + }; auto visible = [this](Node* panel) { if (!panel) @@ -823,7 +955,7 @@ void App::init_menu_tools() return false; }; - popup_time->find("panel-presets")->on_click = [this, popup_time, popup_exp, visible](Node*) { + popup_time->find("panel-presets")->on_click = [this, close_panel_popups, visible](Node*) { const auto plan = pp::app::plan_tools_panel( pp::app::ToolsPanel::presets, visible(floating_presets.get())); @@ -846,11 +978,10 @@ void App::init_menu_tools() { fpanel->m_container->add_child(floating_presets); } - pp::panopainter::close_legacy_popup_overlay(*popup_exp); - pp::panopainter::close_legacy_popup_overlay(*popup_time); + close_panel_popups(); }; - popup_time->find("panel-color")->on_click = [this, popup_time, popup_exp, visible](Node*) { + popup_time->find("panel-color")->on_click = [this, close_panel_popups, visible](Node*) { const auto plan = pp::app::plan_tools_panel( pp::app::ToolsPanel::color, visible(floating_color.get())); @@ -874,10 +1005,9 @@ void App::init_menu_tools() { fpanel->m_container->add_child(floating_color); } - pp::panopainter::close_legacy_popup_overlay(*popup_exp); - pp::panopainter::close_legacy_popup_overlay(*popup_time); + close_panel_popups(); }; - popup_time->find("panel-color-adv")->on_click = [this, popup_time, popup_exp, visible](Node*) { + popup_time->find("panel-color-adv")->on_click = [this, close_panel_popups, visible](Node*) { const auto plan = pp::app::plan_tools_panel( pp::app::ToolsPanel::color_advanced, visible(floating_picker.get())); @@ -901,10 +1031,9 @@ void App::init_menu_tools() { fpanel->m_container->add_child(floating_picker); } - pp::panopainter::close_legacy_popup_overlay(*popup_exp); - pp::panopainter::close_legacy_popup_overlay(*popup_time); + close_panel_popups(); }; - popup_time->find("panel-layers")->on_click = [this, popup_time, popup_exp, visible](Node*) { + popup_time->find("panel-layers")->on_click = [this, close_panel_popups, visible](Node*) { const auto plan = pp::app::plan_tools_panel( pp::app::ToolsPanel::layers, visible(layers.get())); @@ -922,10 +1051,9 @@ void App::init_menu_tools() if (plan.hides_embedded_title) layers->find("title")->SetVisibility(false); - pp::panopainter::close_legacy_popup_overlay(*popup_exp); - pp::panopainter::close_legacy_popup_overlay(*popup_time); + close_panel_popups(); }; - popup_time->find("panel-brush")->on_click = [this, popup_time, popup_exp, visible](Node*) { + popup_time->find("panel-brush")->on_click = [this, close_panel_popups, visible](Node*) { const auto plan = pp::app::plan_tools_panel( pp::app::ToolsPanel::brush, visible(stroke.get())); @@ -942,10 +1070,9 @@ void App::init_menu_tools() if (plan.hides_embedded_title) stroke->find("title")->SetVisibility(false); - pp::panopainter::close_legacy_popup_overlay(*popup_exp); - pp::panopainter::close_legacy_popup_overlay(*popup_time); + close_panel_popups(); }; - popup_time->find("panel-grids")->on_click = [this, popup_time, popup_exp, visible](Node*) { + popup_time->find("panel-grids")->on_click = [this, close_panel_popups, visible](Node*) { const auto plan = pp::app::plan_tools_panel( pp::app::ToolsPanel::grids, visible(grid.get())); @@ -962,10 +1089,9 @@ void App::init_menu_tools() if (plan.hides_embedded_title) grid->find("title")->SetVisibility(false); - pp::panopainter::close_legacy_popup_overlay(*popup_exp); - pp::panopainter::close_legacy_popup_overlay(*popup_time); + close_panel_popups(); }; - popup_time->find("panel-animation")->on_click = [this, popup_time, popup_exp, visible](Node*) { + popup_time->find("panel-animation")->on_click = [this, close_panel_popups, visible](Node*) { const auto plan = pp::app::plan_tools_panel( pp::app::ToolsPanel::animation, visible(animation.get())); @@ -980,8 +1106,7 @@ void App::init_menu_tools() animation->SetWidthP(100); animation->SetHeightP(100); - pp::panopainter::close_legacy_popup_overlay(*popup_exp); - pp::panopainter::close_legacy_popup_overlay(*popup_time); + close_panel_popups(); }; }; @@ -1143,29 +1268,41 @@ void App::init_menu_tools() }; popup_exp->find("clear-grids")->on_click = [this, popup_exp](Node*) { + auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp); + if (!popup_exp_overlay) + return; + const auto popup_exp_handle = popup_exp_overlay.value(); const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::clear_grids); execute_tools_menu_plan(*this, plan); if (plan.closes_root_popup) { - pp::panopainter::close_legacy_popup_overlay(*popup_exp); + close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle); } }; popup_exp->find("camera-reset")->on_click = [this, popup_exp](Node*) { + auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp); + if (!popup_exp_overlay) + return; + const auto popup_exp_handle = popup_exp_overlay.value(); const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::reset_camera); execute_tools_menu_plan(*this, plan); if (plan.closes_root_popup) { - pp::panopainter::close_legacy_popup_overlay(*popup_exp); + close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle); } }; popup_exp->find("shortcuts")->on_click = [this, popup_exp](Node*) { + auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp); + if (!popup_exp_overlay) + return; + const auto popup_exp_handle = popup_exp_overlay.value(); const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::shortcuts); execute_tools_menu_plan(*this, plan); if (plan.closes_root_popup) { - pp::panopainter::close_legacy_popup_overlay(*popup_exp); + close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle); } }; @@ -1179,13 +1316,17 @@ void App::init_menu_tools() if (platform_supports_sonarpen()) { popup_exp->find("sonarpen")->on_click = [this, popup_exp](Node*) { + auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp); + if (!popup_exp_overlay) + return; + const auto popup_exp_handle = popup_exp_overlay.value(); const auto plan = pp::app::plan_tools_menu_command( pp::app::ToolsMenuCommand::sonarpen, platform_supports_sonarpen()); execute_tools_menu_plan(*this, plan); if (plan.closes_root_popup) { - pp::panopainter::close_legacy_popup_overlay(*popup_exp); + close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle); } }; } @@ -1198,12 +1339,23 @@ void App::init_menu_about() if (auto* menu_file = layout[main_id]->find("menu-about")) { menu_file->on_click = [=](Node*) { + auto* popup_root = layout[main_id]; + if (!popup_root) { + return; + } glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); auto popup = add_menu_popup(*this, "about-menu", pos, menu_file->m_size.x); if (!popup) return; + pp::panopainter::detach_legacy_node_from_parent(*popup); + auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, popup); + if (!popup_overlay) { + pp::panopainter::destroy_legacy_node(*popup); + return; + } + auto popup_handle = popup_overlay.value(); - popup->find("about-app")->on_click = [this, popup](Node*) { + popup->find("about-app")->on_click = [this, popup_root, popup_handle](Node*) { const auto plan = pp::app::plan_about_menu_command( pp::app::AboutMenuCommand::about_app, g_version_major, @@ -1212,11 +1364,11 @@ void App::init_menu_about() execute_about_menu_plan(*this, plan); if (plan.closes_root_popup) { - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); } }; - popup->find("about-doc")->on_click = [this, popup](Node*) { + popup->find("about-doc")->on_click = [this, popup_root, popup_handle](Node*) { // auto path = Asset::absolute("data/doc/test.pdf"); // display_file(path); const auto plan = pp::app::plan_about_menu_command( @@ -1227,7 +1379,7 @@ void App::init_menu_about() execute_about_menu_plan(*this, plan); if (plan.closes_root_popup) { - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); } }; @@ -1242,7 +1394,7 @@ void App::init_menu_about() g_version_fix); text->set_text(plan.label.c_str()); } - item->on_click = [this, popup](Node*) { + item->on_click = [this, popup_root, popup_handle](Node*) { const auto plan = pp::app::plan_about_menu_command( pp::app::AboutMenuCommand::whats_new, g_version_major, @@ -1251,14 +1403,14 @@ void App::init_menu_about() execute_about_menu_plan(*this, plan); if (plan.closes_root_popup) { - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); } }; } if (auto b = popup->find("about-crash")) { - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { const auto plan = pp::app::plan_about_menu_command( pp::app::AboutMenuCommand::induce_crash, g_version_major, @@ -1267,14 +1419,14 @@ void App::init_menu_about() execute_about_menu_plan(*this, plan); if (plan.closes_root_popup) { - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); } }; } if (auto b = popup->find("about-perf")) { - b->on_click = [this, popup](Node*) { + b->on_click = [this, popup_root, popup_handle](Node*) { const auto plan = pp::app::plan_about_menu_command( pp::app::AboutMenuCommand::performance_test, g_version_major, @@ -1285,7 +1437,7 @@ void App::init_menu_about() execute_about_menu_plan(*this, plan); if (plan.closes_root_popup) { - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); } }; } @@ -1356,15 +1508,26 @@ void App::init_menu_layer() if (auto* menu_file = layout[main_id]->find("menu-layers")) { menu_file->on_click = [=](Node*) { + auto* popup_root = layout[main_id]; + if (!popup_root) { + return; + } glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); auto popup = add_menu_popup(*this, "layers-menu", pos, menu_file->m_size.x); if (!popup) return; + pp::panopainter::detach_legacy_node_from_parent(*popup); + auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, popup); + if (!popup_overlay) { + pp::panopainter::destroy_legacy_node(*popup); + return; + } + auto popup_handle = popup_overlay.value(); - popup->find("layer-clear")->on_click = [this, popup](Node*) { + popup->find("layer-clear")->on_click = [this, popup_root, popup_handle](Node*) { const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::clear, *this); execute_document_layer_menu_plan(*this, plan); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; { const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::clear, *this); @@ -1373,10 +1536,10 @@ void App::init_menu_layer() set_text(plan.label.c_str()); } - popup->find("layer-rename")->on_click = [this, popup](Node*) { + popup->find("layer-rename")->on_click = [this, popup_root, popup_handle](Node*) { const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::rename, *this); execute_document_layer_menu_plan(*this, plan); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; { const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::rename, *this); @@ -1385,10 +1548,10 @@ void App::init_menu_layer() set_text(plan.label.c_str()); } - popup->find("layer-merge")->on_click = [this, popup](Node*) { + popup->find("layer-merge")->on_click = [this, popup_root, popup_handle](Node*) { const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::merge_down, *this); execute_document_layer_menu_plan(*this, plan); - pp::panopainter::close_legacy_popup_overlay(*popup); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); }; { const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::merge_down, *this); diff --git a/src/legacy_quick_ui_services.cpp b/src/legacy_quick_ui_services.cpp index c95c9972..ed8da7b8 100644 --- a/src/legacy_quick_ui_services.cpp +++ b/src/legacy_quick_ui_services.cpp @@ -10,6 +10,75 @@ namespace pp::panopainter { namespace { +void close_legacy_overlay_handle_ignoring_status( + Node& anchor, + const pp::foundation::Result& overlay) noexcept +{ + if (!overlay) { + return; + } + (void)close_legacy_overlay_node(anchor, overlay.value()); +} + +void close_legacy_overlay_handles_if_open( + Node& anchor, + const pp::foundation::Result& popup, + const pp::foundation::Result& tick) noexcept +{ + if (popup) { + close_legacy_overlay_handle_ignoring_status(anchor, popup); + } + if (tick) { + close_legacy_overlay_handle_ignoring_status(anchor, tick); + } +} + +struct CheckedPopupAndTickOverlays { + bool opened; + pp::foundation::Result popup; + pp::foundation::Result tick; +}; + +template +void set_popup_ticker_overlay_close_handler( + Node& anchor, + PopupT& popup, + const CheckedPopupAndTickOverlays& popup_and_tick) noexcept +{ + popup.on_popup_close = [&anchor, popup_and_tick](Node*) { + close_legacy_overlay_handles_if_open(anchor, popup_and_tick.popup, popup_and_tick.tick); + }; +} + +CheckedPopupAndTickOverlays open_checked_popup_and_tick_overlays( + Node& anchor, + const std::shared_ptr& popup, + const std::shared_ptr& tick) noexcept +{ + auto popup_overlay = open_legacy_overlay_node_with_handle(anchor, popup); + auto tick_overlay = open_legacy_overlay_node_with_handle(anchor, tick); + if (!popup_overlay || !tick_overlay) { + close_legacy_overlay_handles_if_open(anchor, popup_overlay, tick_overlay); + return { false, popup_overlay, tick_overlay }; + } + return { true, popup_overlay, tick_overlay }; +} + +std::shared_ptr create_checked_popup_tick( + Node& anchor, + const glm::vec2& tick_pos, + float anchor_height, + const glm::vec2& tick_sz) noexcept +{ + auto tick = make_legacy_overlay_node_for_anchor(anchor); + tick->SetPositioning(YGPositionTypeAbsolute); + tick->SetPosition(tick_pos.x, tick_pos.y + (anchor_height - tick_sz.y) * 0.5f); + tick->SetSize(tick_sz); + tick->set_image("data/ui/popup-tick.png"); + tick->m_scale = { 1, 1 }; + return tick; +} + class LegacyQuickUiServices final : public pp::app::QuickUiServices { public: LegacyQuickUiServices(NodePanelQuick& panel, const NodePanelQuick::MiniState* restore_state = nullptr) noexcept @@ -84,20 +153,18 @@ private: glm::vec2 tick_pos = button->m_pos + glm::vec2(button->m_size.x, 0); glm::vec2 popup_pos = { tick_pos.x + tick_sz.x, tick_pos.y }; - auto tick = make_legacy_overlay_node_for_anchor(panel_); - tick->SetPositioning(YGPositionTypeAbsolute); - tick->SetPosition(tick_pos.x, tick_pos.y + (button->m_size.y - tick_sz.y) * 0.5f); - tick->SetSize(tick_sz); - tick->set_image("data/ui/popup-tick.png"); - tick->m_scale = { 1, 1 }; - (void)attach_legacy_overlay_node_to_root(panel_, tick); + auto tick = create_checked_popup_tick(panel_, tick_pos, button->m_size.y, tick_sz); float hh = popup->m_container->m_children.size() > 10 ? (screen.y - 90.f) : 400.f; popup->SetWidth(350); popup->SetHeight(glm::max(hh, 400.f)); popup->SetPositioning(YGPositionTypeAbsolute); popup->SetPosition(popup_pos); - (void)attach_legacy_overlay_node_to_root(panel_, popup); + + const auto popup_overlays = open_checked_popup_and_tick_overlays(panel_, popup, tick); + if (!popup_overlays.opened) { + return; + } panel_.root()->update(); popup->tick(0); @@ -115,8 +182,7 @@ private: popup->update(); activate_legacy_popup_overlay(*popup); - - pp::panopainter::bind_legacy_popup_close_destroys_overlay(*popup, tick); + set_popup_ticker_overlay_close_handler(panel_, *popup, popup_overlays); auto* panel = &panel_; popup->on_brush_changed = [panel, button](Node*, std::shared_ptr& b) { @@ -141,17 +207,14 @@ private: glm::vec2 tick_pos = target->m_pos + glm::vec2(target->m_size.x, 0); glm::vec2 popup_pos = { tick_pos.x + tick_sz.x, tick_pos.y - 140.f }; - auto tick = make_legacy_overlay_node_for_anchor(panel_); - tick->SetPositioning(YGPositionTypeAbsolute); - tick->SetPosition(tick_pos.x, tick_pos.y + (target->m_size.y - tick_sz.y) * 0.5f); - tick->SetSize(tick_sz); - tick->set_image("data/ui/popup-tick.png"); - tick->m_scale = { 1, 1 }; - (void)attach_legacy_overlay_node_to_root(panel_, tick); + auto tick = create_checked_popup_tick(panel_, tick_pos, target->m_size.y, tick_sz); popup->SetPositioning(YGPositionTypeAbsolute); popup->SetPosition(popup_pos); - (void)attach_legacy_overlay_node_to_root(panel_, popup); + const auto popup_overlays = open_checked_popup_and_tick_overlays(panel_, popup, tick); + if (!popup_overlays.opened) { + return; + } panel_.root()->update(); popup->tick(0); @@ -172,7 +235,7 @@ private: auto c = static_cast(target->m_children[0].get()); panel_.m_picker->set_color(c->m_color); - pp::panopainter::bind_legacy_popup_close_destroys_overlay(*panel_.m_picker, tick); + set_popup_ticker_overlay_close_handler(panel_, *popup, popup_overlays); auto* panel = &panel_; panel_.m_picker->on_color_change = [panel, c](Node*, glm::vec3 rgb) { diff --git a/src/node_combobox.cpp b/src/node_combobox.cpp index a59f426a..274c58bc 100644 --- a/src/node_combobox.cpp +++ b/src/node_combobox.cpp @@ -30,8 +30,13 @@ void NodeComboBox::loaded() popup->init(); popup->create(); popup->loaded(); - (void)pp::panopainter::attach_legacy_overlay_node_to_root(*this, popup); + const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*this, popup); + if (!popup_overlay) { + return; + } + const auto popup_handle = popup_overlay.value(); m_items.clear(); + int popup_child_index = -1; for (int i = 0; i < m_data.size(); i++) { if (m_data[i] == "-") @@ -47,17 +52,21 @@ void NodeComboBox::loaded() btn->m_text->set_text(m_data[i].c_str()); btn->m_border->SetWidthP(100.f); btn->m_border->SetHeight(GetHeight()); + popup_child_index++; int index = (int)m_items.size(); if (index == m_current_index) - m_selected_child_index = popup->get_child_index(btn); + m_selected_child_index = popup_child_index; m_items.push_back(m_data[i]); - btn->on_click = [this,popup,btn,index](Node* target) { + const int clicked_child_index = popup_child_index; + btn->on_click = [this, popup_handle, index, clicked_child_index](Node* target) { m_current_index = index; - m_selected_child_index = popup->get_child_index(target); + m_selected_child_index = clicked_child_index; m_text->set_text(m_items[index].c_str()); if (on_select) - on_select(btn, index); - pp::panopainter::close_legacy_popup_overlay(*popup); + on_select(target, index); + const auto close_status = + pp::panopainter::close_legacy_overlay_node(*this, popup_handle); + (void)close_status; }; } } diff --git a/src/node_dialog_picker.cpp b/src/node_dialog_picker.cpp index 746835de..d1afe11c 100644 --- a/src/node_dialog_picker.cpp +++ b/src/node_dialog_picker.cpp @@ -54,13 +54,21 @@ kEventResult NodeColorPicker::handle_event(Event* e) case kEventType::MouseUpL: if (!m_mouse_inside) { + pp::panopainter::release_legacy_mouse_capture(*this); + if (m_parent) + { + pp::panopainter::detach_legacy_node_from_parent(*this); + } if (m_color_cur->m_color != m_color_old->m_color) { m_color_old2->m_color = m_color_old1->m_color; m_color_old1->m_color = m_color_old->m_color; m_color_old->m_color = m_color_cur->m_color; } - pp::panopainter::close_legacy_popup_panel(*this, on_popup_close); + if (on_popup_close) + { + on_popup_close(this); + } } break; default: diff --git a/src/node_panel_stroke.h b/src/node_panel_stroke.h index 13af997f..d177a85c 100644 --- a/src/node_panel_stroke.h +++ b/src/node_panel_stroke.h @@ -8,6 +8,7 @@ #include "node_button_custom.h" #include "node_image.h" #include "node_panel_brush.h" +#include "ui_core/node_lifetime.h" namespace pp::app { struct BrushStrokeControlPlan; @@ -100,7 +101,8 @@ public: std::function on_brush_changed; std::function on_dual_changed; //std::function on_texture_changed; - std::function on_popup_close; + pp::ui::NodeHandle m_popup_overlay_handle {}; + pp::ui::NodeHandle m_tick_overlay_handle {}; struct SliderCurve { @@ -130,4 +132,5 @@ public: void init_checkbox(NodeCheckBox*& slider, const char* id, pp::app::BrushStrokeBoolSetting setting, bool Brush::* prop); void handle_checkbox(pp::app::BrushStrokeBoolSetting setting, bool value); void execute_stroke_control_plan(const pp::app::BrushStrokeControlPlan& plan); + void close_popup_overlay_handles() noexcept; };