#include "pch.h" #include "legacy_ui_overlay_services.h" #include "app.h" #include "node.h" #include "node_input_box.h" #include "node_message_box.h" #include "node_dialog_cloud.h" #include "node_popup_menu.h" #include "node_progress_bar.h" #include "node_settings.h" #include #include namespace pp::panopainter { namespace { template void attach_legacy_app_overlay_with_handle_or_fallback( App& app, const std::shared_ptr& node) noexcept { if (!node) { return; } if (auto* anchor = app.layout[app.main_id]) { const auto overlay = open_legacy_overlay_node_with_handle(*anchor, node); if (overlay) { return; } } (void)attach_legacy_overlay_node(app, node); } struct LegacyOverlayContext { pp::ui::NodeHandle root_handle {}; pp::ui::NodeLifetimeTree tree {}; std::unique_ptr overlays {}; std::unordered_map overlay_handles {}; }; using LegacyOverlayContextMap = std::unordered_map; LegacyOverlayContextMap& overlay_contexts() { static LegacyOverlayContextMap contexts {}; return contexts; } pp::foundation::Result get_overlay_context(Node& anchor, bool create_if_missing) noexcept { auto* root = anchor.root(); if (!root) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("legacy overlay root is missing")); } auto& contexts = overlay_contexts(); auto it = contexts.find(root); if (it == contexts.end()) { if (!create_if_missing) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("legacy overlay registry is missing")); } it = contexts.try_emplace(root, LegacyOverlayContext {}).first; } auto& context = it->second; if (!context.overlays) { const auto root_handle = context.tree.create_root(); if (!root_handle) { return pp::foundation::Result::failure(root_handle.status()); } context.root_handle = root_handle.value(); context.overlays = std::make_unique(context.tree, context.root_handle); } return pp::foundation::Result::success(&context); } Node* overlay_node_for_handle( const LegacyOverlayContext& context, pp::ui::NodeHandle overlay) noexcept { for (auto& entry : context.overlay_handles) { if (entry.second == overlay) { return entry.first; } } return nullptr; } void forget_overlay_by_handle(LegacyOverlayContext& context, pp::ui::NodeHandle overlay) noexcept { for (auto it = context.overlay_handles.begin(); it != context.overlay_handles.end();) { if (it->second == overlay) { it = context.overlay_handles.erase(it); break; } else { ++it; } } } } // namespace void initialize_legacy_overlay_node(App& app, Node& node) { node.set_manager(&app.layout); node.init(); node.create(); node.loaded(); } void destroy_legacy_node(Node& node) { node.destroy(); } void detach_legacy_node_from_parent(Node& node) { node.remove_from_parent(); } void close_legacy_dialog_node(Node& node) { const auto context = get_overlay_context(node, false); if (context) { auto it = context.value()->overlay_handles.find(&node); if (it != context.value()->overlay_handles.end()) { const auto status = close_legacy_overlay_node(node, it->second); if (status.ok()) { return; } context.value()->overlay_handles.erase(it); } } destroy_legacy_node(node); } void release_legacy_mouse_capture(Node& node) noexcept { node.mouse_release(); } void configure_legacy_popup_overlay(Node& node) noexcept { node.m_mouse_ignore = false; node.m_flood_events = true; node.m_capture_children = false; } void activate_legacy_popup_overlay(Node& node) noexcept { configure_legacy_popup_overlay(node); node.mouse_capture(); } void close_legacy_popup_overlay(Node& node) noexcept { release_legacy_mouse_capture(node); destroy_legacy_node(node); } void close_legacy_overlay_handle_ignoring_status( Node& anchor, pp::ui::NodeHandle overlay) noexcept { (void)close_legacy_overlay_node(anchor, overlay); } void close_legacy_overlay_handles_if_open( Node& anchor, const pp::foundation::Result& popup_overlay, const pp::foundation::Result& 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()); } } void close_legacy_dialog_and_hide_keyboard(App& app, Node& node) { close_legacy_dialog_node(node); app.hideKeyboard(); } void close_legacy_popup_panel( Node& node, const std::function& on_close) { release_legacy_mouse_capture(node); if (node.m_parent) { detach_legacy_node_from_parent(node); } if (on_close) { on_close(&node); } } pp::foundation::Status attach_legacy_overlay_node( App& app, const std::shared_ptr& node) noexcept { if (!node) { return pp::foundation::Status::invalid_argument("legacy overlay node is null"); } auto* root = app.layout[app.main_id]; if (!root) { return pp::foundation::Status::invalid_argument("legacy overlay root is missing"); } root->add_child(node); return pp::foundation::Status::success(); } pp::foundation::Status attach_legacy_overlay_node_to_root( Node& anchor, const std::shared_ptr& node) noexcept { if (!node) { return pp::foundation::Status::invalid_argument("legacy overlay node is null"); } auto* root = anchor.root(); if (!root) { return pp::foundation::Status::invalid_argument("legacy overlay root is missing"); } root->add_child(node); return pp::foundation::Status::success(); } pp::foundation::Result open_legacy_overlay_node_with_handle( Node& anchor, const std::shared_ptr& node, bool modal) noexcept { if (!node) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("legacy overlay node is null")); } const auto context = get_overlay_context(anchor, true); if (!context) { return pp::foundation::Result::failure( context.status()); } auto overlay = context.value()->overlays->open_dialog(modal); if (!overlay) { return pp::foundation::Result::failure(overlay.status()); } const auto status = attach_legacy_overlay_node_to_root(anchor, node); if (!status.ok()) { (void)context.value()->overlays->close(overlay.value()); return pp::foundation::Result::failure(status); } context.value()->overlay_handles[node.get()] = overlay.value(); return pp::foundation::Result::success(overlay.value()); } pp::foundation::Status close_legacy_overlay_node(Node& anchor, pp::ui::NodeHandle overlay) noexcept { const auto context = get_overlay_context(anchor, false); if (!context) { return pp::foundation::Status::invalid_argument("legacy overlay registry is missing"); } const auto status = context.value()->overlays->close(overlay); if (!status.ok()) { forget_overlay_by_handle(*context.value(), overlay); return status; } if (auto* raw_overlay = overlay_node_for_handle(*context.value(), overlay)) { destroy_legacy_node(*raw_overlay); } forget_overlay_by_handle(*context.value(), overlay); return status; } pp::foundation::Result> add_legacy_popup_menu( App& app, const char* template_id, float x, float y, float rtl_anchor_width) noexcept { if (!template_id) { return pp::foundation::Result>::failure( pp::foundation::Status::invalid_argument("legacy popup template id is null")); } auto* template_root = app.layout[const_hash(template_id)]; if (!template_root || template_root->m_children.empty()) { return pp::foundation::Result>::failure( pp::foundation::Status::invalid_argument("legacy popup template is missing")); } auto popup = template_root->m_children[0]->clone(); if (!popup) { return pp::foundation::Result>::failure( pp::foundation::Status::invalid_argument("legacy popup clone failed")); } popup->update(); if (auto* root = app.layout[app.main_id]) { if (YGNodeStyleGetDirection(root->y_node) == YGDirectionRTL) { x = x - popup->m_size.x + rtl_anchor_width; } } popup->SetPositioning(YGPositionTypeAbsolute); popup->SetPosition(x, y); const auto status = attach_legacy_overlay_node(app, popup); if (!status.ok()) { return pp::foundation::Result>::failure(status); } return pp::foundation::Result>::success(popup); } std::shared_ptr create_legacy_progress_dialog_overlay( App& app, const pp::app::AppProgressDialogPlan& plan) { auto progress = make_legacy_overlay_node(app); progress->m_progress->SetWidthP(plan.progress_fraction); progress->m_title->set_text(plan.title.c_str()); progress->m_total = plan.total; progress->m_count = plan.count; attach_legacy_app_overlay_with_handle_or_fallback(app, progress); return progress; } std::shared_ptr create_legacy_message_dialog_overlay( App& app, const pp::app::AppMessageDialogPlan& plan) { auto message = make_legacy_overlay_node(app); message->m_title->set_text(plan.title.c_str()); message->m_message->set_text(plan.message.c_str()); message->btn_ok->m_text->set_text(plan.ok_caption.c_str()); if (plan.show_cancel) message->btn_cancel->m_text->set_text(plan.cancel_caption.c_str()); else close_legacy_dialog_node(*message->btn_cancel); attach_legacy_app_overlay_with_handle_or_fallback(app, message); return message; } std::shared_ptr create_legacy_input_dialog_overlay( App& app, const pp::app::AppInputDialogPlan& plan) { auto input = make_legacy_overlay_node(app); input->m_title->set_text(plan.title.c_str()); input->m_field_name->set_text(plan.field_name.c_str()); input->btn_ok->m_text->set_text(plan.ok_caption.c_str()); attach_legacy_app_overlay_with_handle_or_fallback(app, input); return input; } std::shared_ptr create_legacy_settings_dialog_overlay( App& app) { auto settings = make_legacy_overlay_node(app); attach_legacy_app_overlay_with_handle_or_fallback(app, settings); return settings; } std::shared_ptr create_legacy_cloud_browser_dialog_overlay( App& app) { auto dialog = make_legacy_overlay_node(app); attach_legacy_app_overlay_with_handle_or_fallback(app, dialog); return dialog; } } // namespace pp::panopainter