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_app_shell_services.cpp
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.h
src/legacy_canvas_tool_services.cpp
@@ -157,9 +159,15 @@ set(PP_PANOPAINTER_APP_SOURCES
src/app_layout_edit_menu.cpp
src/app_layout_about_layer_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/legacy_tools_menu_binding_services.cpp
src/legacy_tools_menu_binding_services.h
src/app_shaders.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_frame_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,
unsaved-project prompting, project-open execution, and title/layer refresh
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
download background execution in `src/legacy_cloud_services.cpp` now routes
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,
brush-package import, and timelapse-export async paths now use
`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
longer compiles Web platform sources directly, but broader CMake and
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
retained document-open bridge now owns that handoff in
`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

View File

@@ -1,8 +1,7 @@
#include "pch.h"
#include "app.h"
#include "app_core/about_menu.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_ui_overlay_services.h"
#include "node_button_custom.h"
@@ -70,11 +69,6 @@ pp::app::DocumentLayerMenuPlan make_layer_menu_plan(
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)
{
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"))
{
menu_file->on_click = [&app, menu_file](Node*) {
auto* popup_root = app.layout[app.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(app, "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<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);
}
};
}
};
auto* popup_root = app.layout[app.main_id];
if (!popup_root) {
return;
}
pp::panopainter::bind_legacy_about_menu_popup(
app,
*popup_root,
*menu_file,
g_version_major,
g_version_minor,
g_version_fix,
app.canvas && app.canvas->m_canvas);
}
}

View File

@@ -1,51 +1,7 @@
#include "pch.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 "legacy_file_menu_binding_services.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 {
@@ -55,135 +11,7 @@ void bind_legacy_file_menu(App& app)
if (auto* menu_file = main->find<NodeButtonCustom>("menu-file"))
{
menu_file->on_click = [&app, menu_file](Node*) {
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);
};
};
bind_legacy_file_menu_popup(app, *menu_file, *main);
}
}

View File

@@ -1,118 +1,12 @@
#include "pch.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 {
void execute_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan)
{
pp::panopainter::execute_legacy_main_toolbar_plan(app, plan);
}
} // namespace
#include "legacy_main_toolbar_binding_services.h"
namespace pp::panopainter {
void bind_legacy_main_toolbar(App& app)
{
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-anim"))
{
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());
};
}
pp::panopainter::bind_legacy_main_toolbar_buttons(app, *app.layout[app.main_id]);
}
} // namespace pp::panopainter

View File

@@ -1,53 +1,21 @@
#include "pch.h"
#include "app.h"
#include "app_core/app_preferences.h"
#include "app_core/brush_ui.h"
#include "app_core/tools_menu.h"
#include "legacy_brush_ui_services.h"
#include "legacy_app_preference_services.h"
#include "legacy_app_shell_services.h"
#include "legacy_preference_storage.h"
#include "legacy_tools_menu_binding_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_button_custom.h"
#include "node_checkbox.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 <vector>
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(
App& app,
const char* template_id,
@@ -83,201 +51,13 @@ void bind_legacy_tools_menu(App& app)
if (!popup_exp)
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);
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(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();
};
pp::panopainter::bind_legacy_tools_panels_submenu(app, *main, popup_exp, *tick);
};
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