#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_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 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(plan.width), static_cast(plan.height)); } else { if (plan.width > 0) panel.SetWidth(static_cast(plan.width)); if (plan.height > 0) panel.SetHeight(static_cast(plan.height)); } if (plan.min_width > 0) panel.SetMinWidth(static_cast(plan.min_width)); if (plan.min_height > 0) panel.SetMinHeight(static_cast(plan.min_height)); panel.m_title->set_text(plan.title.data()); panel.m_droppable = plan.droppable; } std::shared_ptr 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 : "", popup.status().message); return nullptr; } return popup.value(); } } // namespace namespace pp::panopainter { void bind_legacy_tools_menu(App& app) { auto main = app.layout[app.main_id]; if (auto menu_exp = main->find("menu-tools")) { menu_exp->on_click = [&app, menu_exp, main](Node*) { glm::vec2 pos = menu_exp->m_pos + glm::vec2(0, menu_exp->m_size.y); auto popup_exp = add_menu_popup(app, "tools-menu", pos, menu_exp->m_size.x); if (!popup_exp) return; if (auto tick = popup_exp->find("tools-panels")) tick->on_click = [&app, popup_exp, main](Node* b) { const auto menu_plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::panels); if (menu_plan.action != pp::app::ToolsMenuAction::show_panels_submenu) return; pp::panopainter::detach_legacy_node_from_parent(*popup_exp); const auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*main, popup_exp); if (!popup_exp_overlay) return; const auto popup_exp_handle = popup_exp_overlay.value(); glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0); auto popup_time = add_menu_popup(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(c)) { if (fp->m_container->is_child(panel)) return true; } } return false; }; popup_time->find("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(); 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(); app.floating_presets->SetHeightP(100); app.floating_presets->on_brush_changed = [&app](Node*, std::shared_ptr& 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("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(); 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(); 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("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(); 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(); 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("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(); 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("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(); 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("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(); 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("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(); 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("tools-options")) options->on_click = [&app, options, main](Node* b) { const auto menu_plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::options); if (menu_plan.action != pp::app::ToolsMenuAction::show_options_submenu) return; glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0); auto popup_time = add_menu_popup(app, "options-menu", pos, b->m_size.x); if (!popup_time) return; if (auto ui_scale = popup_time->find("tools-ui-scale")) { std::vector scale_options; scale_options.reserve(ui_scale->m_data.size()); for (int i = 0; i < ui_scale->m_data.size(); i++) scale_options.push_back(ui_scale->get_float(i)); const auto selection = pp::app::plan_scale_option_selection(App::I->zoom, scale_options); if (selection.has_selection) ui_scale->set_index(static_cast(selection.index)); ui_scale->on_select = [ui_scale](Node*, int index) { const auto status = pp::panopainter::execute_legacy_ui_scale_preference( *App::I, ui_scale->get_float(index)); if (!status.ok()) LOG("UI scale preference failed: %s", status.message); }; } if (auto vp_scale = popup_time->find("tools-vp-scale")) { std::vector scale_options; scale_options.reserve(vp_scale->m_data.size()); for (int i = 0; i < vp_scale->m_data.size(); i++) scale_options.push_back(vp_scale->get_float(i)); const auto selection = pp::app::plan_scale_option_selection(App::I->canvas->m_density, scale_options); if (selection.has_selection) vp_scale->set_index(static_cast(selection.index)); vp_scale->on_select = [vp_scale](Node*, int index) { const auto status = pp::panopainter::execute_legacy_viewport_scale_preference( *App::I, vp_scale->get_float(index)); if (!status.ok()) LOG("Viewport scale preference failed: %s", status.message); }; } if (auto rtl_btn = popup_time->find("tools-rtl")) { NodeCheckBox* cb = rtl_btn->find("tools-rtl-check"); cb->set_value(app.ui_rtl, false); rtl_btn->on_click = [rtl_btn](Node*) { NodeCheckBox* cb = rtl_btn->find("tools-rtl-check"); cb->set_value(!cb->checked, true); }; rtl_btn->find("tools-rtl-check")->on_value_changed = [&app](Node*, bool checked) { const auto status = pp::panopainter::execute_legacy_interface_direction_preference( app, checked); if (!status.ok()) LOG("Interface direction preference failed: %s", status.message); }; } if (auto vr_btn = popup_time->find("tools-vr")) { NodeCheckBox* cb = vr_btn->find("tools-vr-check"); cb->set_value(app.vr_session_snapshot().has_vr); vr_btn->on_click = [vr_btn](Node*) { NodeCheckBox* cb = vr_btn->find("tools-vr-check"); cb->set_value(!cb->checked, true); }; vr_btn->find("tools-vr-check")->on_value_changed = [&app](Node* target, bool checked) { const auto status = pp::panopainter::execute_legacy_vr_mode_preference( app, checked); if (!status.ok()) { auto cb = static_cast(target); cb->set_value(false); app.message_box("VR Failed", "Couldn't start Virtual Reality mode"); } }; } if (auto vr_btn = popup_time->find("tools-vr-controllers")) { NodeCheckBox* cb = vr_btn->find("tools-vr-controllers-check"); cb->set_value(app.vr_controllers_enabled); vr_btn->on_click = [vr_btn](Node*) { NodeCheckBox* cb = vr_btn->find("tools-vr-controllers-check"); cb->set_value(!cb->checked, true); }; vr_btn->find("tools-vr-controllers-check")->on_value_changed = [&app](Node*, bool checked) { const auto status = pp::panopainter::execute_legacy_vr_controllers_preference( app, checked); if (!status.ok()) LOG("VR controllers preference failed: %s", status.message); }; } if (auto btn = popup_time->find("tools-timelapse")) { NodeCheckBox* cb = btn->find("tools-timelapse-check"); cb->set_value( pp::panopainter::read_legacy_startup_preferences(app.vr_controllers_enabled).auto_timelapse, false); btn->on_click = [btn](Node*) { NodeCheckBox* cb = btn->find("tools-timelapse-check"); cb->set_value(!cb->checked, true); }; btn->find("tools-timelapse-check")->on_value_changed = [&app](Node*, bool checked) { const auto status = pp::panopainter::execute_legacy_timelapse_preference( app, checked); if (!status.ok()) LOG("Timelapse preference failed: %s", status.message); }; } if (auto mode = popup_time->find("tools-show-cursor")) { mode->set_index(pp::panopainter::read_legacy_canvas_preferences().cursor_mode); mode->on_select = [](Node*, int index) { const auto status = pp::panopainter::execute_legacy_canvas_cursor_mode_preference( *App::I, index); if (!status.ok()) LOG("Cursor mode preference failed: %s", status.message); }; } }; popup_exp->find("clear-grids")->on_click = [&app, popup_exp](Node*) { auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp); if (!popup_exp_overlay) return; const auto popup_exp_handle = popup_exp_overlay.value(); const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::clear_grids); pp::panopainter::execute_legacy_tools_menu_plan(app, plan); if (plan.closes_root_popup) { close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle); } }; popup_exp->find("camera-reset")->on_click = [&app, popup_exp](Node*) { auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp); if (!popup_exp_overlay) return; const auto popup_exp_handle = popup_exp_overlay.value(); const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::reset_camera); pp::panopainter::execute_legacy_tools_menu_plan(app, plan); if (plan.closes_root_popup) { close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle); } }; popup_exp->find("shortcuts")->on_click = [&app, popup_exp](Node*) { auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp); if (!popup_exp_overlay) return; const auto popup_exp_handle = popup_exp_overlay.value(); const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::shortcuts); pp::panopainter::execute_legacy_tools_menu_plan(app, plan); if (plan.closes_root_popup) { close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle); } }; /* popup_exp->find("mp4test")->on_click = [this, popup_exp](Node*) { dialog_export_mp4(); pp::panopainter::close_legacy_popup_overlay(*popup_exp); }; */ if (app.platform_supports_sonarpen()) { popup_exp->find("sonarpen")->on_click = [&app, popup_exp](Node*) { auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp); if (!popup_exp_overlay) return; const auto popup_exp_handle = popup_exp_overlay.value(); const auto plan = pp::app::plan_tools_menu_command( pp::app::ToolsMenuCommand::sonarpen, app.platform_supports_sonarpen()); pp::panopainter::execute_legacy_tools_menu_plan(app, plan); if (plan.closes_root_popup) { close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle); } }; } }; } } } // namespace pp::panopainter