Extract app menu binding services

This commit is contained in:
2026-06-17 19:12:46 +02:00
parent d632efb10f
commit 0c609b9d15
16 changed files with 996 additions and 616 deletions

View File

@@ -94,6 +94,8 @@ set(PP_LEGACY_APP_SOURCES
src/legacy_canvas_mode_transform.cpp src/legacy_canvas_mode_transform.cpp
src/legacy_app_shell_services.cpp src/legacy_app_shell_services.cpp
src/legacy_app_shell_services.h src/legacy_app_shell_services.h
src/legacy_about_menu_binding_services.cpp
src/legacy_about_menu_binding_services.h
src/legacy_app_shell_performance_test_services.cpp src/legacy_app_shell_performance_test_services.cpp
src/legacy_app_shell_performance_test_services.h src/legacy_app_shell_performance_test_services.h
src/legacy_canvas_tool_services.cpp src/legacy_canvas_tool_services.cpp
@@ -157,9 +159,15 @@ set(PP_PANOPAINTER_APP_SOURCES
src/app_layout_edit_menu.cpp src/app_layout_edit_menu.cpp
src/app_layout_about_layer_menu.cpp src/app_layout_about_layer_menu.cpp
src/app_layout_file_menu.cpp src/app_layout_file_menu.cpp
src/legacy_file_menu_binding_services.cpp
src/legacy_file_menu_binding_services.h
src/app_layout_tools_menu.cpp src/app_layout_tools_menu.cpp
src/legacy_tools_menu_binding_services.cpp
src/legacy_tools_menu_binding_services.h
src/app_shaders.cpp src/app_shaders.cpp
src/app_vr.cpp src/app_vr.cpp
src/legacy_main_toolbar_binding_services.cpp
src/legacy_main_toolbar_binding_services.h
src/legacy_app_runtime_shell_services.cpp src/legacy_app_runtime_shell_services.cpp
src/legacy_app_frame_services.cpp src/legacy_app_frame_services.cpp
src/legacy_app_dialog_services.cpp src/legacy_app_dialog_services.cpp

View File

@@ -25,6 +25,29 @@ agent or engineer to remove them without reconstructing context from chat.
owns that document-open handoff inline while retained dialog creation, owns that document-open handoff inline while retained dialog creation,
unsaved-project prompting, project-open execution, and title/layer refresh unsaved-project prompting, project-open execution, and title/layer refresh
still remain. still remain.
- 2026-06-17: `DEBT-0035` was narrowed again.
`src/app_layout_main_toolbar.cpp` no longer owns the retained main-toolbar
button wiring inline; `src/legacy_main_toolbar_binding_services.*` now owns
the per-button bindings behind explicit `App&` plus toolbar-root
dependencies, while the app-layout file remains a thin layout-root adapter
and retained toolbar execution still lives in
`src/legacy_app_shell_services.*`.
- 2026-06-17: `DEBT-0031`/`DEBT-0030` were narrowed again.
`src/app_layout_file_menu.cpp` no longer owns the retained File-menu popup,
export-submenu popup, or button wiring inline; that binding now lives in
`src/legacy_file_menu_binding_services.*`, while retained file/export command
execution still lives in `src/legacy_app_shell_services.*`.
- 2026-06-17: `DEBT-0034` was narrowed again.
`src/app_layout_about_layer_menu.cpp` no longer owns the retained About-menu
popup creation, version-label setup, or button wiring inline; that binding
now lives in `src/legacy_about_menu_binding_services.*`, while retained About
command execution and diagnostics still live in
`src/legacy_app_shell_services.*`.
- 2026-06-17: `DEBT-0033` was narrowed again.
`src/app_layout_tools_menu.cpp` no longer owns the retained Tools > Panels
submenu popup flow or panel-item wiring inline; that binding now lives in
`src/legacy_tools_menu_binding_services.*`, while retained Tools command
execution and options-menu preference wiring still remain in the app shell.
- 2026-06-17: `DEBT-0038` was narrowed again. The retained cloud upload and - 2026-06-17: `DEBT-0038` was narrowed again. The retained cloud upload and
download background execution in `src/legacy_cloud_services.cpp` now routes download background execution in `src/legacy_cloud_services.cpp` now routes
through `AppRuntime::canvas_async_task` instead of a file-static worker through `AppRuntime::canvas_async_task` instead of a file-static worker

View File

@@ -83,6 +83,10 @@ Current conclusion:
`src/legacy_document_open_services.*`, and retained cloud upload/download, `src/legacy_document_open_services.*`, and retained cloud upload/download,
brush-package import, and timelapse-export async paths now use brush-package import, and timelapse-export async paths now use
`AppRuntime::canvas_async_task` instead of file-static worker singletons. `AppRuntime::canvas_async_task` instead of file-static worker singletons.
- Main-toolbar, File-menu, About-menu, and Tools > Panels binding ownership now
lives in dedicated `legacy_*_binding_services.*` helpers, so the
corresponding `app_layout_*` files are thinner adapters even though retained
execution still lives in the app shell.
- Platform extraction improved substantially and the root app source group no - Platform extraction improved substantially and the root app source group no
longer compiles Web platform sources directly, but broader CMake and longer compiles Web platform sources directly, but broader CMake and
entrypoint cleanup are not complete. entrypoint cleanup are not complete.

View File

@@ -65,6 +65,25 @@ Key facts:
- `App::dialog_browse()` no longer owns browse-dialog button wiring inline; the - `App::dialog_browse()` no longer owns browse-dialog button wiring inline; the
retained document-open bridge now owns that handoff in retained document-open bridge now owns that handoff in
`src/legacy_document_open_services.*`. `src/legacy_document_open_services.*`.
- `App::init_toolbar_main()` now delegates retained main-toolbar button wiring
through `src/legacy_main_toolbar_binding_services.*`, so
`src/app_layout_main_toolbar.cpp` is down to a thin root lookup and adapter
call while retained toolbar execution still lives in
`src/legacy_app_shell_services.*`.
- `App::init_menu_file()` now delegates retained File-menu popup and export
submenu wiring through `src/legacy_file_menu_binding_services.*`, so
`src/app_layout_file_menu.cpp` is down to a thin trigger lookup and adapter
call while retained file/export execution still lives in
`src/legacy_app_shell_services.*`.
- `App::init_menu_about()` now delegates retained About-menu popup wiring
through `src/legacy_about_menu_binding_services.*`, so
`src/app_layout_about_layer_menu.cpp` no longer owns the About callback body
inline while retained About execution still lives in
`src/legacy_app_shell_services.*`.
- `App::init_menu_tools()` now delegates the retained Tools > Panels submenu
wiring through `src/legacy_tools_menu_binding_services.*`, so
`src/app_layout_tools_menu.cpp` no longer owns that floating-panel submenu
body inline while retained Tools execution and options wiring remain.
## Parallel Assignment Rules ## Parallel Assignment Rules

View File

@@ -1,8 +1,7 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "app_core/about_menu.h"
#include "app_core/document_layer.h" #include "app_core/document_layer.h"
#include "legacy_app_shell_services.h" #include "legacy_about_menu_binding_services.h"
#include "legacy_document_layer_services.h" #include "legacy_document_layer_services.h"
#include "legacy_ui_overlay_services.h" #include "legacy_ui_overlay_services.h"
#include "node_button_custom.h" #include "node_button_custom.h"
@@ -70,11 +69,6 @@ pp::app::DocumentLayerMenuPlan make_layer_menu_plan(
return {}; return {};
} }
void execute_about_menu_plan(App& app, const pp::app::AboutMenuPlan& plan)
{
pp::panopainter::execute_legacy_about_menu_plan(app, plan);
}
void execute_document_layer_menu_plan(App& app, const pp::app::DocumentLayerMenuPlan& plan) void execute_document_layer_menu_plan(App& app, const pp::app::DocumentLayerMenuPlan& plan)
{ {
const auto status = pp::panopainter::execute_legacy_document_layer_menu_plan(app, plan); const auto status = pp::panopainter::execute_legacy_document_layer_menu_plan(app, plan);
@@ -92,110 +86,18 @@ void bind_legacy_about_menu(App& app)
if (auto* menu_file = main->find<NodeButtonCustom>("menu-about")) if (auto* menu_file = main->find<NodeButtonCustom>("menu-about"))
{ {
menu_file->on_click = [&app, menu_file](Node*) { auto* popup_root = app.layout[app.main_id];
auto* popup_root = app.layout[app.main_id]; if (!popup_root) {
if (!popup_root) { return;
return; }
} pp::panopainter::bind_legacy_about_menu_popup(
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); app,
auto popup = add_menu_popup(app, "about-menu", pos, menu_file->m_size.x); *popup_root,
if (!popup) *menu_file,
return; g_version_major,
pp::panopainter::detach_legacy_node_from_parent(*popup); g_version_minor,
auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, popup); g_version_fix,
if (!popup_overlay) { app.canvas && app.canvas->m_canvas);
pp::panopainter::destroy_legacy_node(*popup);
return;
}
auto popup_handle = popup_overlay.value();
popup->find<NodeButtonCustom>("about-app")->on_click = [&app, popup_root, popup_handle](Node*) {
const auto plan = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::about_app,
g_version_major,
g_version_minor,
g_version_fix);
execute_about_menu_plan(app, plan);
if (plan.closes_root_popup)
{
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
}
};
popup->find<NodeButtonCustom>("about-doc")->on_click = [&app, 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(
pp::app::AboutMenuCommand::help_guide,
g_version_major,
g_version_minor,
g_version_fix);
execute_about_menu_plan(app, plan);
if (plan.closes_root_popup)
{
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
}
};
if (auto item = popup->find<NodeButtonCustom>("about-news"))
{
if (auto text = item->find<NodeText>("menu-label"))
{
const auto plan = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::whats_new,
g_version_major,
g_version_minor,
g_version_fix);
text->set_text(plan.label.c_str());
}
item->on_click = [&app, popup_root, popup_handle](Node*) {
const auto plan = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::whats_new,
g_version_major,
g_version_minor,
g_version_fix);
execute_about_menu_plan(app, plan);
if (plan.closes_root_popup)
{
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
}
};
}
if (auto b = popup->find<NodeButtonCustom>("about-crash"))
{
b->on_click = [&app, popup_root, popup_handle](Node*) {
const auto plan = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::induce_crash,
g_version_major,
g_version_minor,
g_version_fix);
execute_about_menu_plan(app, plan);
if (plan.closes_root_popup)
{
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
}
};
}
if (auto b = popup->find<NodeButtonCustom>("about-perf"))
{
b->on_click = [&app, popup_root, popup_handle](Node*) {
const auto plan = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::performance_test,
g_version_major,
g_version_minor,
g_version_fix,
true,
Canvas::I != nullptr);
execute_about_menu_plan(app, plan);
if (plan.closes_root_popup)
{
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
}
};
}
};
} }
} }

View File

@@ -1,51 +1,7 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "app_core/document_export.h" #include "legacy_file_menu_binding_services.h"
#include "app_core/file_menu.h"
#include "legacy_app_shell_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_button_custom.h" #include "node_button_custom.h"
#include "node_popup_menu.h"
namespace {
void apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind)
{
(void)pp::panopainter::apply_legacy_document_export_menu_plan(app, kind);
}
void apply_file_menu_plan(App& app, pp::app::FileMenuCommand command)
{
pp::panopainter::apply_legacy_file_menu_command(app, command);
}
std::shared_ptr<NodePopupMenu> add_menu_popup(
App& app,
const char* template_id,
glm::vec2 position,
float rtl_anchor_width)
{
const auto popup = pp::panopainter::add_legacy_popup_menu(
app,
template_id,
position.x,
position.y,
rtl_anchor_width);
if (!popup) {
LOG("Popup menu '%s' failed: %s", template_id ? template_id : "<null>", popup.status().message);
return nullptr;
}
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);
}
} // namespace
namespace pp::panopainter { namespace pp::panopainter {
@@ -55,135 +11,7 @@ void bind_legacy_file_menu(App& app)
if (auto* menu_file = main->find<NodeButtonCustom>("menu-file")) if (auto* menu_file = main->find<NodeButtonCustom>("menu-file"))
{ {
menu_file->on_click = [&app, menu_file](Node*) { bind_legacy_file_menu_popup(app, *menu_file, *main);
auto* popup_root = app.layout[app.main_id];
if (!popup_root) {
return;
}
const auto open_checked_menu_popup = [popup_root](App& app_ref, const char* id, glm::vec2 pos, float width)
-> std::pair<std::shared_ptr<NodePopupMenu>, pp::ui::NodeHandle>
{
auto popup = add_menu_popup(app_ref, 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);
const auto [popup, popup_handle] = open_checked_menu_popup(app, "file-menu", pos, menu_file->m_size.x);
if (!popup)
return;
if (auto b = popup->find<NodeButtonCustom>("file-newdoc"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::new_document);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-import"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::import_image);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-open"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::open_project);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-browse"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::browse_cloud);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-save"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::save);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-save-as"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::save_as);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-save-ver"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::save_version);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-export"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::export_jpeg);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-export-tick"))
b->on_click = [&app, b, popup_root, popup_handle, open_checked_menu_popup](Node*) {
glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
const auto [subpopup, subpopup_handle] = open_checked_menu_popup(app, "file-submenu-export", pos, b->m_size.x);
if (!subpopup)
return;
subpopup->find<NodeButtonCustom>("file-submenu-export-png")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::png);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-layers")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::layers);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-cube")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::cube_faces);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-depth")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::depth);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-anim")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::animation_frames);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-anim-mp4")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::animation_mp4);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-timelapse")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::timelapse);
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<NodeButtonCustom>("file-share"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::share);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-resize"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::resize);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-cloud-upload"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::cloud_upload);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-cloud-browse"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::cloud_browse);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
};
} }
} }

View File

@@ -1,118 +1,12 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "app_core/main_toolbar.h" #include "legacy_main_toolbar_binding_services.h"
#include "legacy_app_shell_services.h"
#include "legacy_history_services.h"
#include "node_button.h"
#include "node_button_custom.h"
namespace {
void execute_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan)
{
pp::panopainter::execute_legacy_main_toolbar_plan(app, plan);
}
} // namespace
namespace pp::panopainter { namespace pp::panopainter {
void bind_legacy_main_toolbar(App& app) void bind_legacy_main_toolbar(App& app)
{ {
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-anim")) pp::panopainter::bind_legacy_main_toolbar_buttons(app, *app.layout[app.main_id]);
{
button->on_click = [&app, button](Node*) {
if (app.canvas)
{
//app.canvas->m_canvas->export_anim();
}
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-open"))
{
button->on_click = [&app, button](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::open_document);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-save"))
{
button->on_click = [&app, button](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::save_document);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButtonCustom>("btn-undo"))
{
button->on_click = [&app, button](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::undo,
history.undo_count);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButtonCustom>("btn-redo"))
{
button->on_click = [&app, button](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::redo,
0,
history.redo_count);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButtonCustom>("btn-clean-memory"))
{
button->on_click = [&app](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::clear_history,
history.undo_count,
history.redo_count,
history.memory_bytes);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-clear"))
{
button->on_click = [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::clear_canvas,
0,
0,
0,
static_cast<bool>(app.canvas));
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-popup"))
{
button->on_click = [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::show_message_box);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButtonCustom>("btn-settings"))
{
button->on_click = [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::show_settings);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
} }
} // namespace pp::panopainter } // namespace pp::panopainter

View File

@@ -1,53 +1,21 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "app_core/app_preferences.h" #include "app_core/app_preferences.h"
#include "app_core/brush_ui.h"
#include "app_core/tools_menu.h" #include "app_core/tools_menu.h"
#include "legacy_brush_ui_services.h"
#include "legacy_app_preference_services.h" #include "legacy_app_preference_services.h"
#include "legacy_app_shell_services.h" #include "legacy_app_shell_services.h"
#include "legacy_preference_storage.h" #include "legacy_preference_storage.h"
#include "legacy_tools_menu_binding_services.h"
#include "legacy_ui_overlay_services.h" #include "legacy_ui_overlay_services.h"
#include "node_button_custom.h" #include "node_button_custom.h"
#include "node_checkbox.h" #include "node_checkbox.h"
#include "node_combobox.h" #include "node_combobox.h"
#include "node_dialog_picker.h"
#include "node_panel_animation.h"
#include "node_panel_brush.h"
#include "node_panel_color.h"
#include "node_panel_floating.h"
#include "node_panel_grid.h"
#include "node_panel_layer.h"
#include "node_panel_stroke.h"
#include "node_popup_menu.h" #include "node_popup_menu.h"
#include <vector> #include <vector>
namespace { namespace {
[[nodiscard]] bool should_open_tools_panel(const pp::app::ToolsPanelPlan& plan) noexcept
{
return plan.action == pp::app::ToolsPanelAction::open_floating_panel;
}
void apply_tools_panel_chrome(NodePanelFloating& panel, const pp::app::ToolsPanelPlan& plan)
{
if (plan.width > 0 && plan.height > 0) {
panel.SetSize(static_cast<float>(plan.width), static_cast<float>(plan.height));
} else {
if (plan.width > 0)
panel.SetWidth(static_cast<float>(plan.width));
if (plan.height > 0)
panel.SetHeight(static_cast<float>(plan.height));
}
if (plan.min_width > 0)
panel.SetMinWidth(static_cast<float>(plan.min_width));
if (plan.min_height > 0)
panel.SetMinHeight(static_cast<float>(plan.min_height));
panel.m_title->set_text(plan.title.data());
panel.m_droppable = plan.droppable;
}
std::shared_ptr<NodePopupMenu> add_menu_popup( std::shared_ptr<NodePopupMenu> add_menu_popup(
App& app, App& app,
const char* template_id, const char* template_id,
@@ -83,201 +51,13 @@ void bind_legacy_tools_menu(App& app)
if (!popup_exp) if (!popup_exp)
return; return;
if (auto tick = popup_exp->find<NodeButtonCustom>("tools-panels")) tick->on_click = [&app, popup_exp, main](Node* b) if (auto tick = popup_exp->find<NodeButtonCustom>("tools-panels")) tick->on_click = [&app, popup_exp, main, tick](Node*)
{ {
const auto menu_plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::panels); const auto menu_plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::panels);
if (menu_plan.action != pp::app::ToolsMenuAction::show_panels_submenu) if (menu_plan.action != pp::app::ToolsMenuAction::show_panels_submenu)
return; return;
pp::panopainter::detach_legacy_node_from_parent(*popup_exp); pp::panopainter::bind_legacy_tools_panels_submenu(app, *main, popup_exp, *tick);
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(app, "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 = [&app](Node* panel) {
if (!panel)
return false;
for (auto& c : app.floatings_container->m_children)
{
if (auto fp = std::static_pointer_cast<NodePanelFloating>(c))
{
if (fp->m_container->is_child(panel))
return true;
}
}
return false;
};
popup_time->find<NodeButtonCustom>("panel-presets")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::presets,
visible(app.floating_presets.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Presets;
apply_tools_panel_chrome(*fpanel, plan);
if (!app.floating_presets)
{
app.floating_presets = fpanel->m_container->add_child_ref<NodePanelBrushPreset>();
app.floating_presets->SetHeightP(100);
app.floating_presets->on_brush_changed = [&app](Node*, std::shared_ptr<Brush>& b) {
(void)pp::panopainter::apply_legacy_brush_preset_plan(app, b);
};
}
else
{
fpanel->m_container->add_child(app.floating_presets);
}
close_panel_popups();
};
popup_time->find<NodeButtonCustom>("panel-color")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::color,
visible(app.floating_color.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Color;
apply_tools_panel_chrome(*fpanel, plan);
if (!app.floating_color)
{
app.floating_color = fpanel->m_container->add_child_ref<NodePanelColor>();
app.floating_color->SetHeightP(100);
if (plan.hides_embedded_title)
app.floating_color->find("title")->SetVisibility(false);
app.floating_color->on_color_changed = [&app](Node*, glm::vec4 color) {
(void)pp::panopainter::apply_legacy_brush_color_plan(app, color, false, false);
};
}
else
{
fpanel->m_container->add_child(app.floating_color);
}
close_panel_popups();
};
popup_time->find<NodeButtonCustom>("panel-color-adv")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::color_advanced,
visible(app.floating_picker.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::ColorAdv;
apply_tools_panel_chrome(*fpanel, plan);
if (!app.floating_picker)
{
app.floating_picker = fpanel->m_container->add_child_ref<NodeColorPicker>();
app.floating_picker->m_autohide = false;
app.floating_picker->on_color_change = [&app](Node*, glm::vec3 color) {
(void)pp::panopainter::apply_legacy_brush_color_plan(app, glm::vec4(color, 1.f), false, false);
};
}
else
{
fpanel->m_container->add_child(app.floating_picker);
}
close_panel_popups();
};
popup_time->find<NodeButtonCustom>("panel-layers")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::layers,
visible(app.layers.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Layers;
apply_tools_panel_chrome(*fpanel, plan);
fpanel->m_container->add_child(app.layers);
app.layers->SetPositioning(YGPositionTypeRelative);
app.layers->SetPosition(0, 0);
app.layers->SetWidthP(100);
app.layers->SetHeightP(100);
app.layers->SetFlexShrink(0);
if (plan.hides_embedded_title)
app.layers->find("title")->SetVisibility(false);
close_panel_popups();
};
popup_time->find<NodeButtonCustom>("panel-brush")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::brush,
visible(app.stroke.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Brush;
fpanel->m_container->add_child(app.stroke);
apply_tools_panel_chrome(*fpanel, plan);
app.stroke->SetPositioning(YGPositionTypeRelative);
app.stroke->SetPosition(0, 0);
app.stroke->SetWidthP(100);
app.stroke->SetHeightP(100);
if (plan.hides_embedded_title)
app.stroke->find("title")->SetVisibility(false);
close_panel_popups();
};
popup_time->find<NodeButtonCustom>("panel-grids")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::grids,
visible(app.grid.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Grids;
fpanel->m_container->add_child(app.grid);
apply_tools_panel_chrome(*fpanel, plan);
app.grid->SetPositioning(YGPositionTypeRelative);
app.grid->SetPosition(0, 0);
app.grid->SetWidthP(100);
app.grid->SetHeightP(100);
if (plan.hides_embedded_title)
app.grid->find("title")->SetVisibility(false);
close_panel_popups();
};
popup_time->find<NodeButtonCustom>("panel-animation")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::animation,
visible(app.animation.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Animation;
fpanel->m_container->add_child(app.animation);
apply_tools_panel_chrome(*fpanel, plan);
app.animation->SetPositioning(YGPositionTypeRelative);
app.animation->SetPosition(0, 0);
app.animation->SetWidthP(100);
app.animation->SetHeightP(100);
close_panel_popups();
};
}; };
if (auto options = popup_exp->find<NodeButtonCustom>("tools-options")) options->on_click = [&app, options, main](Node* b) if (auto options = popup_exp->find<NodeButtonCustom>("tools-options")) options->on_click = [&app, options, main](Node* b)

View File

@@ -0,0 +1,166 @@
#include "pch.h"
#include "legacy_about_menu_binding_services.h"
#include "app.h"
#include "legacy_app_shell_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_button_custom.h"
#include "node_popup_menu.h"
#include "node_text.h"
namespace pp::panopainter {
namespace {
std::shared_ptr<NodePopupMenu> add_menu_popup(
App& app,
const char* template_id,
glm::vec2 position,
float rtl_anchor_width)
{
const auto popup = pp::panopainter::add_legacy_popup_menu(
app,
template_id,
position.x,
position.y,
rtl_anchor_width);
if (!popup) {
LOG("Popup menu '%s' failed: %s", template_id ? template_id : "<null>", popup.status().message);
return nullptr;
}
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);
}
void bind_about_menu_action(
App& app,
Node& popup_root,
pp::ui::NodeHandle popup_handle,
NodeButtonCustom& button,
pp::app::AboutMenuCommand command,
int version_major,
int version_minor,
int version_fix,
bool has_canvas) noexcept
{
button.on_click = [&app, &popup_root, popup_handle, command, version_major, version_minor, version_fix, has_canvas](Node*) {
const auto plan = pp::app::plan_about_menu_command(
command,
version_major,
version_minor,
version_fix,
true,
has_canvas);
execute_legacy_about_menu_plan(app, plan);
if (plan.closes_root_popup) {
close_legacy_overlay_handle_ignoring_status(popup_root, popup_handle);
}
};
}
} // namespace
void bind_legacy_about_menu_popup(
App& app,
Node& popup_root,
NodeButtonCustom& trigger_button,
int version_major,
int version_minor,
int version_fix,
bool has_canvas) noexcept
{
trigger_button.on_click = [&app, &popup_root, &trigger_button, version_major, version_minor, version_fix, has_canvas](Node*) {
const glm::vec2 pos = trigger_button.m_pos + glm::vec2(0, trigger_button.m_size.y);
auto popup = add_menu_popup(app, "about-menu", pos, trigger_button.m_size.x);
if (!popup) {
return;
}
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;
}
const auto popup_handle = popup_overlay.value();
bind_about_menu_action(
app,
popup_root,
popup_handle,
*popup->find<NodeButtonCustom>("about-app"),
pp::app::AboutMenuCommand::about_app,
version_major,
version_minor,
version_fix,
has_canvas);
bind_about_menu_action(
app,
popup_root,
popup_handle,
*popup->find<NodeButtonCustom>("about-doc"),
pp::app::AboutMenuCommand::help_guide,
version_major,
version_minor,
version_fix,
has_canvas);
if (auto item = popup->find<NodeButtonCustom>("about-news")) {
if (auto text = item->find<NodeText>("menu-label")) {
const auto plan = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::whats_new,
version_major,
version_minor,
version_fix);
text->set_text(plan.label.c_str());
}
bind_about_menu_action(
app,
popup_root,
popup_handle,
*item,
pp::app::AboutMenuCommand::whats_new,
version_major,
version_minor,
version_fix,
has_canvas);
}
if (auto b = popup->find<NodeButtonCustom>("about-crash")) {
bind_about_menu_action(
app,
popup_root,
popup_handle,
*b,
pp::app::AboutMenuCommand::induce_crash,
version_major,
version_minor,
version_fix,
has_canvas);
}
if (auto b = popup->find<NodeButtonCustom>("about-perf")) {
bind_about_menu_action(
app,
popup_root,
popup_handle,
*b,
pp::app::AboutMenuCommand::performance_test,
version_major,
version_minor,
version_fix,
has_canvas);
}
};
}
} // namespace pp::panopainter

View File

@@ -0,0 +1,20 @@
#pragma once
#include "app_core/about_menu.h"
class App;
class Node;
class NodeButtonCustom;
namespace pp::panopainter {
void bind_legacy_about_menu_popup(
App& app,
Node& popup_root,
NodeButtonCustom& trigger_button,
int version_major,
int version_minor,
int version_fix,
bool has_canvas) noexcept;
} // namespace pp::panopainter

View File

@@ -0,0 +1,286 @@
#include "pch.h"
#include "legacy_file_menu_binding_services.h"
#include "app.h"
#include "app_core/document_export.h"
#include "app_core/file_menu.h"
#include "legacy_app_shell_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_button_custom.h"
#include "node_popup_menu.h"
namespace {
std::shared_ptr<NodePopupMenu> add_menu_popup(
App& app,
const char* template_id,
glm::vec2 position,
float rtl_anchor_width)
{
const auto popup = pp::panopainter::add_legacy_popup_menu(
app,
template_id,
position.x,
position.y,
rtl_anchor_width);
if (!popup) {
LOG("Popup menu '%s' failed: %s", template_id ? template_id : "<null>", popup.status().message);
return nullptr;
}
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);
}
} // namespace
namespace pp::panopainter {
namespace {
class LegacyFileMenuBindingServices final {
public:
LegacyFileMenuBindingServices(App& app, Node& popup_root) noexcept
: app_(app)
, popup_root_(popup_root)
{
}
void bind_menu_button(NodeButtonCustom& menu_file)
{
menu_file.on_click = [this, &menu_file](Node*) {
open_file_menu_popup(menu_file);
};
}
private:
void open_file_menu_popup(NodeButtonCustom& menu_file)
{
const glm::vec2 pos = menu_file.m_pos + glm::vec2(0, menu_file.m_size.y);
const auto popup = add_menu_popup(app_, "file-menu", pos, menu_file.m_size.x);
if (!popup) {
return;
}
pp::panopainter::detach_legacy_node_from_parent(*popup);
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(popup_root_, popup);
if (!popup_overlay) {
pp::panopainter::destroy_legacy_node(*popup);
return;
}
bind_popup_wiring(*popup, popup_overlay.value());
}
void bind_popup_wiring(
NodePopupMenu& popup,
pp::ui::NodeHandle popup_handle)
{
if (auto* b = popup.find<NodeButtonCustom>("file-newdoc")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::new_document);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-import")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::import_image);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-open")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::open_project);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-browse")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::browse_cloud);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-save")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::save);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-save-as")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::save_as);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-save-ver")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::save_version);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-export")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::export_jpeg);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-export-tick")) {
b->on_click = [this, b, popup_handle](Node*) {
open_export_submenu(*b, popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-share")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::share);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-resize")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::resize);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-cloud-upload")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::cloud_upload);
close_popup(popup_handle);
};
}
if (auto* b = popup.find<NodeButtonCustom>("file-cloud-browse")) {
b->on_click = [this, popup_handle](Node*) {
apply_file_menu_command(pp::app::FileMenuCommand::cloud_browse);
close_popup(popup_handle);
};
}
}
void open_export_submenu(
NodeButtonCustom& export_button,
pp::ui::NodeHandle popup_handle)
{
const glm::vec2 pos = export_button.m_pos + glm::vec2(export_button.m_size.x, 0);
const auto subpopup = add_menu_popup(
app_,
"file-submenu-export",
pos,
export_button.m_size.x);
if (!subpopup) {
return;
}
pp::panopainter::detach_legacy_node_from_parent(*subpopup);
const auto subpopup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(popup_root_, subpopup);
if (!subpopup_overlay) {
pp::panopainter::destroy_legacy_node(*subpopup);
return;
}
bind_export_submenu_wiring(
*subpopup,
popup_handle,
subpopup_overlay.value());
}
void bind_export_submenu_wiring(
NodePopupMenu& subpopup,
pp::ui::NodeHandle popup_handle,
pp::ui::NodeHandle subpopup_handle)
{
bind_export_submenu_button(
subpopup,
"file-submenu-export-png",
pp::app::DocumentExportMenuKind::png,
popup_handle,
subpopup_handle);
bind_export_submenu_button(
subpopup,
"file-submenu-export-layers",
pp::app::DocumentExportMenuKind::layers,
popup_handle,
subpopup_handle);
bind_export_submenu_button(
subpopup,
"file-submenu-export-cube",
pp::app::DocumentExportMenuKind::cube_faces,
popup_handle,
subpopup_handle);
bind_export_submenu_button(
subpopup,
"file-submenu-export-depth",
pp::app::DocumentExportMenuKind::depth,
popup_handle,
subpopup_handle);
bind_export_submenu_button(
subpopup,
"file-submenu-export-anim",
pp::app::DocumentExportMenuKind::animation_frames,
popup_handle,
subpopup_handle);
bind_export_submenu_button(
subpopup,
"file-submenu-export-anim-mp4",
pp::app::DocumentExportMenuKind::animation_mp4,
popup_handle,
subpopup_handle);
bind_export_submenu_button(
subpopup,
"file-submenu-export-timelapse",
pp::app::DocumentExportMenuKind::timelapse,
popup_handle,
subpopup_handle);
}
void bind_export_submenu_button(
NodePopupMenu& subpopup,
const char* button_id,
pp::app::DocumentExportMenuKind kind,
pp::ui::NodeHandle popup_handle,
pp::ui::NodeHandle subpopup_handle)
{
if (auto* b = subpopup.find<NodeButtonCustom>(button_id)) {
b->on_click = [this, kind, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu(kind);
close_popup(popup_handle);
close_popup(subpopup_handle);
};
}
}
void apply_file_menu_command(pp::app::FileMenuCommand command)
{
pp::panopainter::apply_legacy_file_menu_command(app_, command);
}
void apply_document_export_menu(pp::app::DocumentExportMenuKind kind)
{
(void)pp::panopainter::apply_legacy_document_export_menu_plan(app_, kind);
}
void close_popup(pp::ui::NodeHandle overlay) noexcept
{
close_legacy_overlay_handle_ignoring_status(popup_root_, overlay);
}
App& app_;
Node& popup_root_;
};
} // namespace
void bind_legacy_file_menu_popup(
App& app,
NodeButtonCustom& menu_file,
Node& popup_root)
{
LegacyFileMenuBindingServices services(app, popup_root);
services.bind_menu_button(menu_file);
}
} // namespace pp::panopainter

View File

@@ -0,0 +1,14 @@
#pragma once
class App;
class Node;
class NodeButtonCustom;
namespace pp::panopainter {
void bind_legacy_file_menu_popup(
App& app,
NodeButtonCustom& menu_file,
Node& popup_root);
} // namespace pp::panopainter

View File

@@ -0,0 +1,112 @@
#include "pch.h"
#include "legacy_main_toolbar_binding_services.h"
#include "app.h"
#include "app_core/main_toolbar.h"
#include "legacy_app_shell_services.h"
#include "legacy_history_services.h"
#include "node_button.h"
#include "node_button_custom.h"
namespace pp::panopainter {
namespace {
template <class ButtonT, class Handler>
void bind_legacy_main_toolbar_click(Node& toolbar_root, const char* button_id, Handler&& handler)
{
if (auto* button = toolbar_root.find<ButtonT>(button_id)) {
button->on_click = std::forward<Handler>(handler);
}
}
void execute_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan)
{
pp::panopainter::execute_legacy_main_toolbar_plan(app, plan);
}
} // namespace
void bind_legacy_main_toolbar_buttons(App& app, Node& toolbar_root)
{
bind_legacy_main_toolbar_click<NodeButton>(toolbar_root, "btn-anim", [&app](Node*) {
if (app.canvas) {
//app.canvas->m_canvas->export_anim();
}
});
bind_legacy_main_toolbar_click<NodeButton>(toolbar_root, "btn-open", [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::open_document);
if (plan)
execute_main_toolbar_plan(app, plan.value());
});
bind_legacy_main_toolbar_click<NodeButton>(toolbar_root, "btn-save", [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::save_document);
if (plan)
execute_main_toolbar_plan(app, plan.value());
});
bind_legacy_main_toolbar_click<NodeButtonCustom>(toolbar_root, "btn-undo", [&app](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::undo,
history.undo_count);
if (plan)
execute_main_toolbar_plan(app, plan.value());
});
bind_legacy_main_toolbar_click<NodeButtonCustom>(toolbar_root, "btn-redo", [&app](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::redo,
0,
history.redo_count);
if (plan)
execute_main_toolbar_plan(app, plan.value());
});
bind_legacy_main_toolbar_click<NodeButtonCustom>(
toolbar_root,
"btn-clean-memory",
[&app](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::clear_history,
history.undo_count,
history.redo_count,
history.memory_bytes);
if (plan)
execute_main_toolbar_plan(app, plan.value());
});
bind_legacy_main_toolbar_click<NodeButton>(toolbar_root, "btn-clear", [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::clear_canvas,
0,
0,
0,
static_cast<bool>(app.canvas));
if (plan)
execute_main_toolbar_plan(app, plan.value());
});
bind_legacy_main_toolbar_click<NodeButton>(toolbar_root, "btn-popup", [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::show_message_box);
if (plan)
execute_main_toolbar_plan(app, plan.value());
});
bind_legacy_main_toolbar_click<NodeButtonCustom>(toolbar_root, "btn-settings", [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::show_settings);
if (plan)
execute_main_toolbar_plan(app, plan.value());
});
}
} // namespace pp::panopainter

View File

@@ -0,0 +1,10 @@
#pragma once
class App;
class Node;
namespace pp::panopainter {
void bind_legacy_main_toolbar_buttons(App& app, Node& toolbar_root);
} // namespace pp::panopainter

View File

@@ -0,0 +1,296 @@
#include "pch.h"
#include "legacy_tools_menu_binding_services.h"
#include "app.h"
#include "app_core/brush_ui.h"
#include "app_core/tools_menu.h"
#include "legacy_brush_ui_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_button_custom.h"
#include "node_dialog_picker.h"
#include "node_panel_animation.h"
#include "node_panel_brush.h"
#include "node_panel_color.h"
#include "node_panel_floating.h"
#include "node_panel_grid.h"
#include "node_panel_layer.h"
#include "node_panel_stroke.h"
#include "node_popup_menu.h"
#include <functional>
#include <utility>
namespace {
[[nodiscard]] bool should_open_tools_panel(const pp::app::ToolsPanelPlan& plan) noexcept
{
return plan.action == pp::app::ToolsPanelAction::open_floating_panel;
}
void apply_tools_panel_chrome(NodePanelFloating& panel, const pp::app::ToolsPanelPlan& plan)
{
if (plan.width > 0 && plan.height > 0) {
panel.SetSize(static_cast<float>(plan.width), static_cast<float>(plan.height));
} else {
if (plan.width > 0)
panel.SetWidth(static_cast<float>(plan.width));
if (plan.height > 0)
panel.SetHeight(static_cast<float>(plan.height));
}
if (plan.min_width > 0)
panel.SetMinWidth(static_cast<float>(plan.min_width));
if (plan.min_height > 0)
panel.SetMinHeight(static_cast<float>(plan.min_height));
panel.m_title->set_text(plan.title.data());
panel.m_droppable = plan.droppable;
}
[[nodiscard]] std::shared_ptr<NodePopupMenu> add_menu_popup(
App& app,
const char* template_id,
glm::vec2 position,
float rtl_anchor_width)
{
const auto popup = pp::panopainter::add_legacy_popup_menu(
app,
template_id,
position.x,
position.y,
rtl_anchor_width);
if (!popup) {
LOG("Popup menu '%s' failed: %s", template_id ? template_id : "<null>", popup.status().message);
return nullptr;
}
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);
}
auto make_panel_visibility_checker(App& app)
{
return [&app](Node* panel) {
if (!panel)
return false;
for (auto& c : app.floatings_container->m_children)
{
if (auto fp = std::static_pointer_cast<NodePanelFloating>(c))
{
if (fp->m_container->is_child(panel))
return true;
}
}
return false;
};
}
void bind_panel_handlers(
App& app,
NodePopupMenu& panels_popup,
const std::function<void()>& close_panel_popups,
const std::function<bool(Node*)>& visible)
{
panels_popup.find<NodeButtonCustom>("panel-presets")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::presets,
visible(app.floating_presets.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Presets;
apply_tools_panel_chrome(*fpanel, plan);
if (!app.floating_presets)
{
app.floating_presets = fpanel->m_container->add_child_ref<NodePanelBrushPreset>();
app.floating_presets->SetHeightP(100);
app.floating_presets->on_brush_changed = [&app](Node*, std::shared_ptr<Brush>& b) {
(void)pp::panopainter::apply_legacy_brush_preset_plan(app, b);
};
}
else
{
fpanel->m_container->add_child(app.floating_presets);
}
close_panel_popups();
};
panels_popup.find<NodeButtonCustom>("panel-color")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::color,
visible(app.floating_color.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Color;
apply_tools_panel_chrome(*fpanel, plan);
if (!app.floating_color)
{
app.floating_color = fpanel->m_container->add_child_ref<NodePanelColor>();
app.floating_color->SetHeightP(100);
if (plan.hides_embedded_title)
app.floating_color->find("title")->SetVisibility(false);
app.floating_color->on_color_changed = [&app](Node*, glm::vec4 color) {
(void)pp::panopainter::apply_legacy_brush_color_plan(app, color, false, false);
};
}
else
{
fpanel->m_container->add_child(app.floating_color);
}
close_panel_popups();
};
panels_popup.find<NodeButtonCustom>("panel-color-adv")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::color_advanced,
visible(app.floating_picker.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::ColorAdv;
apply_tools_panel_chrome(*fpanel, plan);
if (!app.floating_picker)
{
app.floating_picker = fpanel->m_container->add_child_ref<NodeColorPicker>();
app.floating_picker->m_autohide = false;
app.floating_picker->on_color_change = [&app](Node*, glm::vec3 color) {
(void)pp::panopainter::apply_legacy_brush_color_plan(app, glm::vec4(color, 1.f), false, false);
};
}
else
{
fpanel->m_container->add_child(app.floating_picker);
}
close_panel_popups();
};
panels_popup.find<NodeButtonCustom>("panel-layers")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::layers,
visible(app.layers.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Layers;
apply_tools_panel_chrome(*fpanel, plan);
fpanel->m_container->add_child(app.layers);
app.layers->SetPositioning(YGPositionTypeRelative);
app.layers->SetPosition(0, 0);
app.layers->SetWidthP(100);
app.layers->SetHeightP(100);
app.layers->SetFlexShrink(0);
if (plan.hides_embedded_title)
app.layers->find("title")->SetVisibility(false);
close_panel_popups();
};
panels_popup.find<NodeButtonCustom>("panel-brush")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::brush,
visible(app.stroke.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Brush;
fpanel->m_container->add_child(app.stroke);
apply_tools_panel_chrome(*fpanel, plan);
app.stroke->SetPositioning(YGPositionTypeRelative);
app.stroke->SetPosition(0, 0);
app.stroke->SetWidthP(100);
app.stroke->SetHeightP(100);
if (plan.hides_embedded_title)
app.stroke->find("title")->SetVisibility(false);
close_panel_popups();
};
panels_popup.find<NodeButtonCustom>("panel-grids")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::grids,
visible(app.grid.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Grids;
fpanel->m_container->add_child(app.grid);
apply_tools_panel_chrome(*fpanel, plan);
app.grid->SetPositioning(YGPositionTypeRelative);
app.grid->SetPosition(0, 0);
app.grid->SetWidthP(100);
app.grid->SetHeightP(100);
if (plan.hides_embedded_title)
app.grid->find("title")->SetVisibility(false);
close_panel_popups();
};
panels_popup.find<NodeButtonCustom>("panel-animation")->on_click = [&app, close_panel_popups, visible](Node*) {
const auto plan = pp::app::plan_tools_panel(
pp::app::ToolsPanel::animation,
visible(app.animation.get()));
if (!should_open_tools_panel(plan))
return;
auto fpanel = app.floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Animation;
fpanel->m_container->add_child(app.animation);
apply_tools_panel_chrome(*fpanel, plan);
app.animation->SetPositioning(YGPositionTypeRelative);
app.animation->SetPosition(0, 0);
app.animation->SetWidthP(100);
app.animation->SetHeightP(100);
close_panel_popups();
};
}
} // namespace
namespace pp::panopainter {
void bind_legacy_tools_panels_submenu(
App& app,
Node& main,
std::shared_ptr<NodePopupMenu> root_popup,
NodeButtonCustom& panels_button)
{
panels_button.on_click = [&app, &main, root_popup = std::move(root_popup), &panels_button](Node*) mutable {
pp::panopainter::detach_legacy_node_from_parent(*root_popup);
const auto root_popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(main, root_popup);
if (!root_popup_overlay)
return;
const auto root_popup_handle = root_popup_overlay.value();
glm::vec2 pos = panels_button.m_pos + glm::vec2(panels_button.m_size.x, 0);
auto panels_popup = add_menu_popup(app, "panels-menu", pos, panels_button.m_size.x);
if (!panels_popup)
{
close_legacy_overlay_handle_ignoring_status(main, root_popup_handle);
return;
}
pp::panopainter::detach_legacy_node_from_parent(*panels_popup);
const auto panels_popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(main, panels_popup);
if (!panels_popup_overlay)
{
close_legacy_overlay_handle_ignoring_status(main, root_popup_handle);
return;
}
const auto panels_popup_handle = panels_popup_overlay.value();
const auto close_panel_popups = [root_popup_handle, panels_popup_handle, &main]()
{
close_legacy_overlay_handle_ignoring_status(main, root_popup_handle);
close_legacy_overlay_handle_ignoring_status(main, panels_popup_handle);
};
const auto visible = make_panel_visibility_checker(app);
bind_panel_handlers(app, *panels_popup, close_panel_popups, visible);
};
}
} // namespace pp::panopainter

View File

@@ -0,0 +1,18 @@
#pragma once
#include <memory>
class App;
class Node;
class NodeButtonCustom;
class NodePopupMenu;
namespace pp::panopainter {
void bind_legacy_tools_panels_submenu(
App& app,
Node& main,
std::shared_ptr<NodePopupMenu> root_popup,
NodeButtonCustom& panels_button);
} // namespace pp::panopainter