Thin canvas state, tools menu, and node canvas draw
This commit is contained in:
@@ -79,14 +79,14 @@ What is still carrying too much live ownership:
|
||||
|
||||
Current hotspot files:
|
||||
|
||||
- `src/canvas.cpp`: 4128 lines
|
||||
- `src/app_layout.cpp`: 2026 lines
|
||||
- `src/canvas.cpp`: 3622 lines
|
||||
- `src/app_layout.cpp`: 1498 lines
|
||||
- `src/canvas_modes.cpp`: 1798 lines
|
||||
- `src/node.cpp`: 1551 lines
|
||||
- `src/main.cpp`: 1374 lines
|
||||
- `src/node_panel_brush.cpp`: 1197 lines
|
||||
- `src/node_stroke_preview.cpp`: 1129 lines
|
||||
- `src/node_canvas.cpp`: 962 lines
|
||||
- `src/node_canvas.cpp`: 888 lines
|
||||
- `src/app.cpp`: 950 lines
|
||||
- `src/app_dialogs.cpp`: 908 lines
|
||||
|
||||
@@ -120,9 +120,14 @@ Current architecture mismatches that must be treated as real blockers:
|
||||
merged-texture draw callback plus non-`draw_merged` per-frame layer draw
|
||||
callback plus the smoothing-mask face shader/draw pass plus heightmap,
|
||||
current-mode, and grid-mode callback setup now routed through the same
|
||||
retained helper family.
|
||||
retained helper family, while post-draw callback assembly and the remaining
|
||||
per-layer render-path orchestration now also route through retained
|
||||
draw-merge helpers even though the broader node draw loop is still inline.
|
||||
- `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files
|
||||
rather than thin composition/binding surfaces.
|
||||
rather than thin composition/binding surfaces, even though tools-menu binding
|
||||
plus nested panels/options submenu wiring now live in
|
||||
`src/app_layout_tools_menu.cpp` and `App::init_menu_tools()` is now a thin
|
||||
call-through.
|
||||
- `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
|
||||
global singleton reach, raw observer pointers, retained static worker
|
||||
ownership in several app families, and ad hoc mutex/condition-variable
|
||||
@@ -145,10 +150,13 @@ Current architecture mismatches that must be treated as real blockers:
|
||||
prepared-file worker and the canvas async import/export/save/open worker now
|
||||
live under `AppRuntime` instead of retained static app-events/canvas
|
||||
workers, `App::rec_loop()` now delegates worker-iteration orchestration into
|
||||
the retained recording bridge, and `App::update_rec_frames()` now delegates
|
||||
recording label refresh through that same retained recording path even though
|
||||
the bridge still owns worker-side readback flow and encoder-state label
|
||||
reads.
|
||||
the retained recording bridge, `App::update_rec_frames()` now delegates
|
||||
recording label refresh through that same retained recording path, and the
|
||||
canvas state-management cluster for picking, clear/clear-all, layer
|
||||
add/remove/order/lookups, animation frame control, resize, and snapshot
|
||||
save/restore now lives in `src/legacy_canvas_state_services.cpp` instead of
|
||||
`src/canvas.cpp`, even though the bridge still owns worker-side readback flow
|
||||
and encoder-state label reads.
|
||||
- Modern C++23 usage exists in extracted components, especially `std::span`,
|
||||
explicit result/status objects, and a few concepts, but the live app still
|
||||
does not consistently express ownership, thread affinity, or renderer
|
||||
|
||||
@@ -87,11 +87,18 @@ looks like a wrapper around the old renderer shell.
|
||||
|
||||
#### ARC-RND-001 - Split `canvas.cpp` Into Document State And Render Execution Shells
|
||||
|
||||
Status: Ready
|
||||
Status: In Progress
|
||||
|
||||
Why now:
|
||||
`src/canvas.cpp` is still the biggest single architectural blocker at about
|
||||
4128 lines.
|
||||
3622 lines.
|
||||
|
||||
Current slice:
|
||||
- Canvas state-management helpers for picking, clear/clear-all, layer
|
||||
add/remove/order/lookups, animation frame control, resize, and snapshot
|
||||
save/restore now live in `src/legacy_canvas_state_services.cpp` instead of
|
||||
staying inline in `src/canvas.cpp`, but the file still owns the larger
|
||||
document-plus-render shell.
|
||||
|
||||
Write scope:
|
||||
- `src/canvas.cpp`
|
||||
@@ -171,6 +178,11 @@ Current slice:
|
||||
now also routes through `execute_legacy_canvas_draw_merge_layer_plane(...)`,
|
||||
but the node still owns substantial live layer traversal and renderer state
|
||||
orchestration.
|
||||
- `NodeCanvas` post-draw callback assembly now also routes through
|
||||
`execute_legacy_canvas_draw_merge_post_draw_callbacks(...)`, and the
|
||||
remaining per-layer render-path orchestration now also routes through
|
||||
`execute_legacy_canvas_draw_merge_layer_path(...)`, but the node still owns
|
||||
broader draw-loop and renderer-state shell sequencing.
|
||||
|
||||
Write scope:
|
||||
- `src/node_stroke_preview.cpp`
|
||||
@@ -246,12 +258,18 @@ targets look like helpers under one old monolith.
|
||||
|
||||
#### ARC-APP-001 - Split `app_layout.cpp` Into UI Binding Modules
|
||||
|
||||
Status: Ready
|
||||
Status: In Progress
|
||||
|
||||
Why now:
|
||||
`src/app_layout.cpp` is still a 2026-line mixed file that builds menus,
|
||||
`src/app_layout.cpp` is still a 1498-line mixed file that builds menus,
|
||||
attaches callbacks, computes planner inputs, and mutates UI state directly.
|
||||
|
||||
Current slice:
|
||||
- Tools-menu UI binding, including the nested panels/options submenu wiring,
|
||||
now lives in `src/app_layout_tools_menu.cpp`, and `App::init_menu_tools()`
|
||||
is now a thin call-through, but file/about/layer/sidebar and broader layout
|
||||
composition are still inline in `src/app_layout.cpp`.
|
||||
|
||||
Write scope:
|
||||
- `src/app_layout.cpp`
|
||||
- `src/legacy_app_shell_services.*`
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace pp::panopainter {
|
||||
void bind_legacy_tools_menu(App& app);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool apply_brush_color_plan(App& app, glm::vec4 color, bool update_quick, bool update_color_panel)
|
||||
@@ -897,441 +901,7 @@ void App::init_menu_edit()
|
||||
|
||||
void App::init_menu_tools()
|
||||
{
|
||||
auto main = layout[main_id];
|
||||
|
||||
if (auto menu_exp = main->find<NodeButtonCustom>("menu-tools"))
|
||||
{
|
||||
menu_exp->on_click = [this, 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(*this, "tools-menu", pos, menu_exp->m_size.x);
|
||||
if (!popup_exp)
|
||||
return;
|
||||
|
||||
if (auto tick = popup_exp->find<NodeButtonCustom>("tools-panels")) tick->on_click = [this, popup_exp, main](Node* b)
|
||||
{
|
||||
const auto menu_plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::panels);
|
||||
if (menu_plan.action != pp::app::ToolsMenuAction::show_panels_submenu)
|
||||
return;
|
||||
|
||||
pp::panopainter::detach_legacy_node_from_parent(*popup_exp);
|
||||
const auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*main, popup_exp);
|
||||
if (!popup_exp_overlay)
|
||||
return;
|
||||
const auto popup_exp_handle = popup_exp_overlay.value();
|
||||
|
||||
glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
|
||||
auto popup_time = add_menu_popup(*this, "panels-menu", pos, b->m_size.x);
|
||||
if (!popup_time)
|
||||
{
|
||||
close_legacy_overlay_handle_ignoring_status(*main, popup_exp_handle);
|
||||
return;
|
||||
}
|
||||
pp::panopainter::detach_legacy_node_from_parent(*popup_time);
|
||||
const auto popup_time_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*main, popup_time);
|
||||
if (!popup_time_overlay)
|
||||
{
|
||||
close_legacy_overlay_handle_ignoring_status(*main, popup_exp_handle);
|
||||
return;
|
||||
}
|
||||
const auto popup_time_handle = popup_time_overlay.value();
|
||||
|
||||
const auto close_panel_popups = [main, popup_exp_handle, popup_time_handle]()
|
||||
{
|
||||
close_legacy_overlay_handle_ignoring_status(*main, popup_exp_handle);
|
||||
close_legacy_overlay_handle_ignoring_status(*main, popup_time_handle);
|
||||
};
|
||||
|
||||
auto visible = [this](Node* panel) {
|
||||
if (!panel)
|
||||
return false;
|
||||
for (auto& c : 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 = [this, close_panel_popups, visible](Node*) {
|
||||
const auto plan = pp::app::plan_tools_panel(
|
||||
pp::app::ToolsPanel::presets,
|
||||
visible(floating_presets.get()));
|
||||
if (!should_open_tools_panel(plan))
|
||||
return;
|
||||
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
||||
fpanel->m_class = NodePanelFloating::kClass::Presets;
|
||||
apply_tools_panel_chrome(*fpanel, plan);
|
||||
if (!floating_presets)
|
||||
{
|
||||
floating_presets = fpanel->m_container->add_child_ref<NodePanelBrushPreset>();
|
||||
floating_presets->SetHeightP(100);
|
||||
//floating_presets->SetFlexGrow(1);
|
||||
//floating_presets->find("toolbar")->destroy();
|
||||
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
|
||||
apply_brush_preset_plan(*this, b);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
fpanel->m_container->add_child(floating_presets);
|
||||
}
|
||||
close_panel_popups();
|
||||
};
|
||||
|
||||
popup_time->find<NodeButtonCustom>("panel-color")->on_click = [this, close_panel_popups, visible](Node*) {
|
||||
const auto plan = pp::app::plan_tools_panel(
|
||||
pp::app::ToolsPanel::color,
|
||||
visible(floating_color.get()));
|
||||
if (!should_open_tools_panel(plan))
|
||||
return;
|
||||
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
||||
fpanel->m_class = NodePanelFloating::kClass::Color;
|
||||
apply_tools_panel_chrome(*fpanel, plan);
|
||||
if (!floating_color)
|
||||
{
|
||||
floating_color = fpanel->m_container->add_child_ref<NodePanelColor>();
|
||||
floating_color->SetHeightP(100);
|
||||
//floating_color->SetMinHeight(300);
|
||||
if (plan.hides_embedded_title)
|
||||
floating_color->find("title")->SetVisibility(false);
|
||||
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
|
||||
apply_brush_color_plan(*this, color, false, false);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
fpanel->m_container->add_child(floating_color);
|
||||
}
|
||||
close_panel_popups();
|
||||
};
|
||||
popup_time->find<NodeButtonCustom>("panel-color-adv")->on_click = [this, close_panel_popups, visible](Node*) {
|
||||
const auto plan = pp::app::plan_tools_panel(
|
||||
pp::app::ToolsPanel::color_advanced,
|
||||
visible(floating_picker.get()));
|
||||
if (!should_open_tools_panel(plan))
|
||||
return;
|
||||
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
||||
fpanel->m_class = NodePanelFloating::kClass::ColorAdv;
|
||||
apply_tools_panel_chrome(*fpanel, plan);
|
||||
if (!floating_picker)
|
||||
{
|
||||
floating_picker = fpanel->m_container->add_child_ref<NodeColorPicker>();
|
||||
//floating_picker->find("title")->SetVisibility(false);
|
||||
//floating_picker->SetHeightP(100);
|
||||
//floating_picker->SetWidth(250);
|
||||
floating_picker->m_autohide = false;
|
||||
floating_picker->on_color_change = [this](Node* target, glm::vec3 color) {
|
||||
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, false);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
fpanel->m_container->add_child(floating_picker);
|
||||
}
|
||||
close_panel_popups();
|
||||
};
|
||||
popup_time->find<NodeButtonCustom>("panel-layers")->on_click = [this, close_panel_popups, visible](Node*) {
|
||||
const auto plan = pp::app::plan_tools_panel(
|
||||
pp::app::ToolsPanel::layers,
|
||||
visible(layers.get()));
|
||||
if (!should_open_tools_panel(plan))
|
||||
return;
|
||||
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
||||
fpanel->m_class = NodePanelFloating::kClass::Layers;
|
||||
apply_tools_panel_chrome(*fpanel, plan);
|
||||
fpanel->m_container->add_child(layers);
|
||||
layers->SetPositioning(YGPositionTypeRelative);
|
||||
layers->SetPosition(0, 0);
|
||||
layers->SetWidthP(100);
|
||||
layers->SetHeightP(100);
|
||||
layers->SetFlexShrink(0);
|
||||
if (plan.hides_embedded_title)
|
||||
layers->find("title")->SetVisibility(false);
|
||||
|
||||
close_panel_popups();
|
||||
};
|
||||
popup_time->find<NodeButtonCustom>("panel-brush")->on_click = [this, close_panel_popups, visible](Node*) {
|
||||
const auto plan = pp::app::plan_tools_panel(
|
||||
pp::app::ToolsPanel::brush,
|
||||
visible(stroke.get()));
|
||||
if (!should_open_tools_panel(plan))
|
||||
return;
|
||||
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
||||
fpanel->m_class = NodePanelFloating::kClass::Brush;
|
||||
fpanel->m_container->add_child(stroke);
|
||||
apply_tools_panel_chrome(*fpanel, plan);
|
||||
stroke->SetPositioning(YGPositionTypeRelative);
|
||||
stroke->SetPosition(0, 0);
|
||||
stroke->SetWidthP(100);
|
||||
stroke->SetHeightP(100);
|
||||
if (plan.hides_embedded_title)
|
||||
stroke->find("title")->SetVisibility(false);
|
||||
|
||||
close_panel_popups();
|
||||
};
|
||||
popup_time->find<NodeButtonCustom>("panel-grids")->on_click = [this, close_panel_popups, visible](Node*) {
|
||||
const auto plan = pp::app::plan_tools_panel(
|
||||
pp::app::ToolsPanel::grids,
|
||||
visible(grid.get()));
|
||||
if (!should_open_tools_panel(plan))
|
||||
return;
|
||||
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
||||
fpanel->m_class = NodePanelFloating::kClass::Grids;
|
||||
fpanel->m_container->add_child(grid);
|
||||
apply_tools_panel_chrome(*fpanel, plan);
|
||||
grid->SetPositioning(YGPositionTypeRelative);
|
||||
grid->SetPosition(0, 0);
|
||||
grid->SetWidthP(100);
|
||||
grid->SetHeightP(100);
|
||||
if (plan.hides_embedded_title)
|
||||
grid->find("title")->SetVisibility(false);
|
||||
|
||||
close_panel_popups();
|
||||
};
|
||||
popup_time->find<NodeButtonCustom>("panel-animation")->on_click = [this, close_panel_popups, visible](Node*) {
|
||||
const auto plan = pp::app::plan_tools_panel(
|
||||
pp::app::ToolsPanel::animation,
|
||||
visible(animation.get()));
|
||||
if (!should_open_tools_panel(plan))
|
||||
return;
|
||||
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
||||
fpanel->m_class = NodePanelFloating::kClass::Animation;
|
||||
fpanel->m_container->add_child(animation);
|
||||
apply_tools_panel_chrome(*fpanel, plan);
|
||||
animation->SetPositioning(YGPositionTypeRelative);
|
||||
animation->SetPosition(0, 0);
|
||||
animation->SetWidthP(100);
|
||||
animation->SetHeightP(100);
|
||||
|
||||
close_panel_popups();
|
||||
};
|
||||
};
|
||||
|
||||
if (auto options = popup_exp->find<NodeButtonCustom>("tools-options")) options->on_click = [this, 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(*this, "options-menu", pos, b->m_size.x);
|
||||
if (!popup_time)
|
||||
return;
|
||||
|
||||
if (auto ui_scale = popup_time->find<NodeComboBox>("tools-ui-scale"))
|
||||
{
|
||||
std::vector<float> 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<int>(selection.index));
|
||||
|
||||
ui_scale->on_select = [ui_scale](Node* target, 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<NodeComboBox>("tools-vp-scale"))
|
||||
{
|
||||
std::vector<float> 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<int>(selection.index));
|
||||
|
||||
vp_scale->on_select = [vp_scale](Node* target, 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<NodeButtonCustom>("tools-rtl"))
|
||||
{
|
||||
NodeCheckBox* cb = rtl_btn->find<NodeCheckBox>("tools-rtl-check");
|
||||
cb->set_value(ui_rtl, false);
|
||||
|
||||
rtl_btn->on_click = [this, rtl_btn](Node* b)
|
||||
{
|
||||
NodeCheckBox* cb = rtl_btn->find<NodeCheckBox>("tools-rtl-check");
|
||||
cb->set_value(!cb->checked, true);
|
||||
};
|
||||
|
||||
rtl_btn->find<NodeCheckBox>("tools-rtl-check")->on_value_changed = [this, main](Node*, bool checked)
|
||||
{
|
||||
const auto status = pp::panopainter::execute_legacy_interface_direction_preference(
|
||||
*this,
|
||||
checked);
|
||||
if (!status.ok())
|
||||
LOG("Interface direction preference failed: %s", status.message);
|
||||
};
|
||||
}
|
||||
|
||||
if (auto vr_btn = popup_time->find<NodeButtonCustom>("tools-vr"))
|
||||
{
|
||||
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-check");
|
||||
cb->set_value(has_vr);
|
||||
|
||||
vr_btn->on_click = [this, vr_btn](Node* b)
|
||||
{
|
||||
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-check");
|
||||
cb->set_value(!cb->checked, true);
|
||||
};
|
||||
|
||||
vr_btn->find<NodeCheckBox>("tools-vr-check")->on_value_changed = [this, main](Node* target, bool checked)
|
||||
{
|
||||
const auto status = pp::panopainter::execute_legacy_vr_mode_preference(
|
||||
*this,
|
||||
checked);
|
||||
if (!status.ok()) {
|
||||
auto cb = static_cast<NodeCheckBox*>(target);
|
||||
cb->set_value(false);
|
||||
message_box("VR Failed", "Couldn't start Virtual Reality mode");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (auto vr_btn = popup_time->find<NodeButtonCustom>("tools-vr-controllers"))
|
||||
{
|
||||
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-controllers-check");
|
||||
cb->set_value(vr_controllers_enabled);
|
||||
|
||||
vr_btn->on_click = [this, vr_btn](Node* b)
|
||||
{
|
||||
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-controllers-check");
|
||||
cb->set_value(!cb->checked, true);
|
||||
};
|
||||
|
||||
vr_btn->find<NodeCheckBox>("tools-vr-controllers-check")->on_value_changed = [this, main](Node* target, bool checked)
|
||||
{
|
||||
const auto status = pp::panopainter::execute_legacy_vr_controllers_preference(
|
||||
*this,
|
||||
checked);
|
||||
if (!status.ok())
|
||||
LOG("VR controllers preference failed: %s", status.message);
|
||||
};
|
||||
}
|
||||
|
||||
if (auto btn = popup_time->find<NodeButtonCustom>("tools-timelapse"))
|
||||
{
|
||||
NodeCheckBox* cb = btn->find<NodeCheckBox>("tools-timelapse-check");
|
||||
cb->set_value(
|
||||
pp::panopainter::read_legacy_startup_preferences(vr_controllers_enabled).auto_timelapse,
|
||||
false);
|
||||
|
||||
btn->on_click = [this, btn](Node* b)
|
||||
{
|
||||
NodeCheckBox* cb = btn->find<NodeCheckBox>("tools-timelapse-check");
|
||||
cb->set_value(!cb->checked, true);
|
||||
};
|
||||
|
||||
btn->find<NodeCheckBox>("tools-timelapse-check")->on_value_changed = [this, main](Node*, bool checked)
|
||||
{
|
||||
const auto status = pp::panopainter::execute_legacy_timelapse_preference(
|
||||
*this,
|
||||
checked);
|
||||
if (!status.ok())
|
||||
LOG("Timelapse preference failed: %s", status.message);
|
||||
};
|
||||
}
|
||||
|
||||
if (auto mode = popup_time->find<NodeComboBox>("tools-show-cursor"))
|
||||
{
|
||||
mode->set_index(pp::panopainter::read_legacy_canvas_preferences().cursor_mode);
|
||||
|
||||
mode->on_select = [mode](Node* target, 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<NodeButtonCustom>("clear-grids")->on_click = [this, popup_exp](Node*) {
|
||||
auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp);
|
||||
if (!popup_exp_overlay)
|
||||
return;
|
||||
const auto popup_exp_handle = popup_exp_overlay.value();
|
||||
const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::clear_grids);
|
||||
execute_tools_menu_plan(*this, plan);
|
||||
if (plan.closes_root_popup)
|
||||
{
|
||||
close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle);
|
||||
}
|
||||
};
|
||||
|
||||
popup_exp->find<NodeButtonCustom>("camera-reset")->on_click = [this, popup_exp](Node*) {
|
||||
auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp);
|
||||
if (!popup_exp_overlay)
|
||||
return;
|
||||
const auto popup_exp_handle = popup_exp_overlay.value();
|
||||
const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::reset_camera);
|
||||
execute_tools_menu_plan(*this, plan);
|
||||
if (plan.closes_root_popup)
|
||||
{
|
||||
close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle);
|
||||
}
|
||||
};
|
||||
|
||||
popup_exp->find<NodeButtonCustom>("shortcuts")->on_click = [this, popup_exp](Node*) {
|
||||
auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp);
|
||||
if (!popup_exp_overlay)
|
||||
return;
|
||||
const auto popup_exp_handle = popup_exp_overlay.value();
|
||||
const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::shortcuts);
|
||||
execute_tools_menu_plan(*this, plan);
|
||||
if (plan.closes_root_popup)
|
||||
{
|
||||
close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
popup_exp->find<NodeButtonCustom>("mp4test")->on_click = [this, popup_exp](Node*) {
|
||||
dialog_export_mp4();
|
||||
pp::panopainter::close_legacy_popup_overlay(*popup_exp);
|
||||
};
|
||||
*/
|
||||
|
||||
if (platform_supports_sonarpen())
|
||||
{
|
||||
popup_exp->find<NodeButtonCustom>("sonarpen")->on_click = [this, popup_exp](Node*) {
|
||||
auto popup_exp_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_exp->root(), popup_exp);
|
||||
if (!popup_exp_overlay)
|
||||
return;
|
||||
const auto popup_exp_handle = popup_exp_overlay.value();
|
||||
const auto plan = pp::app::plan_tools_menu_command(
|
||||
pp::app::ToolsMenuCommand::sonarpen,
|
||||
platform_supports_sonarpen());
|
||||
execute_tools_menu_plan(*this, plan);
|
||||
if (plan.closes_root_popup)
|
||||
{
|
||||
close_legacy_overlay_handle_ignoring_status(*popup_exp->root(), popup_exp_handle);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
pp::panopainter::bind_legacy_tools_menu(*this);
|
||||
}
|
||||
|
||||
void App::init_menu_about()
|
||||
|
||||
507
src/app_layout_tools_menu.cpp
Normal file
507
src/app_layout_tools_menu.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
#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 <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,
|
||||
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();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
void bind_legacy_tools_menu(App& app)
|
||||
{
|
||||
auto main = app.layout[app.main_id];
|
||||
|
||||
if (auto menu_exp = main->find<NodeButtonCustom>("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<NodeButtonCustom>("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<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)
|
||||
{
|
||||
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<NodeComboBox>("tools-ui-scale"))
|
||||
{
|
||||
std::vector<float> 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<int>(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<NodeComboBox>("tools-vp-scale"))
|
||||
{
|
||||
std::vector<float> 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<int>(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<NodeButtonCustom>("tools-rtl"))
|
||||
{
|
||||
NodeCheckBox* cb = rtl_btn->find<NodeCheckBox>("tools-rtl-check");
|
||||
cb->set_value(app.ui_rtl, false);
|
||||
|
||||
rtl_btn->on_click = [rtl_btn](Node*)
|
||||
{
|
||||
NodeCheckBox* cb = rtl_btn->find<NodeCheckBox>("tools-rtl-check");
|
||||
cb->set_value(!cb->checked, true);
|
||||
};
|
||||
|
||||
rtl_btn->find<NodeCheckBox>("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<NodeButtonCustom>("tools-vr"))
|
||||
{
|
||||
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-check");
|
||||
cb->set_value(app.has_vr);
|
||||
|
||||
vr_btn->on_click = [vr_btn](Node*)
|
||||
{
|
||||
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-check");
|
||||
cb->set_value(!cb->checked, true);
|
||||
};
|
||||
|
||||
vr_btn->find<NodeCheckBox>("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<NodeCheckBox*>(target);
|
||||
cb->set_value(false);
|
||||
app.message_box("VR Failed", "Couldn't start Virtual Reality mode");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (auto vr_btn = popup_time->find<NodeButtonCustom>("tools-vr-controllers"))
|
||||
{
|
||||
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-controllers-check");
|
||||
cb->set_value(app.vr_controllers_enabled);
|
||||
|
||||
vr_btn->on_click = [vr_btn](Node*)
|
||||
{
|
||||
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-controllers-check");
|
||||
cb->set_value(!cb->checked, true);
|
||||
};
|
||||
|
||||
vr_btn->find<NodeCheckBox>("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<NodeButtonCustom>("tools-timelapse"))
|
||||
{
|
||||
NodeCheckBox* cb = btn->find<NodeCheckBox>("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<NodeCheckBox>("tools-timelapse-check");
|
||||
cb->set_value(!cb->checked, true);
|
||||
};
|
||||
|
||||
btn->find<NodeCheckBox>("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<NodeComboBox>("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<NodeButtonCustom>("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<NodeButtonCustom>("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<NodeButtonCustom>("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<NodeButtonCustom>("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<NodeButtonCustom>("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
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
@@ -114,6 +115,21 @@ struct LegacyCanvasDrawMergeLayerPlaneExecution {
|
||||
std::function<void()> draw_blend;
|
||||
};
|
||||
|
||||
struct LegacyCanvasDrawMergeLayerPathExecution {
|
||||
std::function<void()> bind_blender_framebuffer;
|
||||
std::function<void()> clear_blender_framebuffer;
|
||||
std::function<void()> unbind_blender_framebuffer;
|
||||
std::function<void()> prepare_temporary_erase;
|
||||
std::function<void()> cleanup_temporary_erase;
|
||||
std::function<void()> prepare_temporary_paint;
|
||||
std::function<void()> cleanup_temporary_paint;
|
||||
std::function<void()> prepare_layer_texture;
|
||||
std::function<void()> cleanup_layer_texture;
|
||||
std::function<void()> draw_blend;
|
||||
std::function<void()> draw_debug_outline;
|
||||
std::function<void(int, float)> draw_frame;
|
||||
};
|
||||
|
||||
struct LegacyCanvasDrawMergeTemporaryCompositeExecution {
|
||||
std::function<void()> setup;
|
||||
std::function<void()> bind_samplers;
|
||||
@@ -613,6 +629,48 @@ inline void execute_legacy_canvas_draw_merge_layer_plane(
|
||||
}
|
||||
}
|
||||
|
||||
inline void execute_legacy_canvas_draw_merge_layer_path(
|
||||
bool is_temporary_erase,
|
||||
bool is_temporary_paint,
|
||||
bool use_blend,
|
||||
int first_frame,
|
||||
int last_frame,
|
||||
const std::function<float(int)>& frame_alpha,
|
||||
const LegacyCanvasDrawMergeLayerPathExecution& execution)
|
||||
{
|
||||
if (use_blend) {
|
||||
execution.bind_blender_framebuffer();
|
||||
execution.clear_blender_framebuffer();
|
||||
}
|
||||
|
||||
if (is_temporary_erase) {
|
||||
execution.prepare_temporary_erase();
|
||||
for (int frame = first_frame; frame <= last_frame; frame++) {
|
||||
execution.draw_frame(frame, frame_alpha(frame));
|
||||
}
|
||||
execution.cleanup_temporary_erase();
|
||||
} else if (is_temporary_paint) {
|
||||
execution.prepare_temporary_paint();
|
||||
for (int frame = first_frame; frame <= last_frame; frame++) {
|
||||
execution.draw_frame(frame, frame_alpha(frame));
|
||||
}
|
||||
execution.cleanup_temporary_paint();
|
||||
} else {
|
||||
execution.prepare_layer_texture();
|
||||
for (int frame = first_frame; frame <= last_frame; frame++) {
|
||||
execution.draw_frame(frame, frame_alpha(frame));
|
||||
}
|
||||
execution.cleanup_layer_texture();
|
||||
}
|
||||
|
||||
if (use_blend) {
|
||||
execution.unbind_blender_framebuffer();
|
||||
execution.draw_blend();
|
||||
}
|
||||
|
||||
execution.draw_debug_outline();
|
||||
}
|
||||
|
||||
inline void execute_legacy_canvas_draw_merge_temporary_composite(
|
||||
const LegacyCanvasDrawMergeTemporaryCompositeExecution& execution)
|
||||
{
|
||||
@@ -759,6 +817,40 @@ inline void execute_legacy_canvas_draw_merge_post_draw(
|
||||
execution.draw_current_modes();
|
||||
}
|
||||
|
||||
template <
|
||||
typename DrawMaskFree,
|
||||
typename DrawMaskLine,
|
||||
typename DrawSmaskFaces,
|
||||
typename DrawGridModes,
|
||||
typename DrawHeightmap,
|
||||
typename DrawCurrentModes>
|
||||
inline void execute_legacy_canvas_draw_merge_post_draw_callbacks(
|
||||
bool smask_active,
|
||||
bool draw_mask_overlay,
|
||||
int smask_mode,
|
||||
bool draw_grid_modes_enabled,
|
||||
DrawMaskFree&& draw_mask_free,
|
||||
DrawMaskLine&& draw_mask_line,
|
||||
DrawSmaskFaces&& draw_smask_faces,
|
||||
DrawGridModes&& draw_grid_modes_callback,
|
||||
DrawHeightmap&& draw_heightmap,
|
||||
DrawCurrentModes&& draw_current_modes)
|
||||
{
|
||||
execute_legacy_canvas_draw_merge_post_draw(
|
||||
smask_active,
|
||||
draw_mask_overlay,
|
||||
smask_mode,
|
||||
draw_grid_modes_enabled,
|
||||
LegacyCanvasDrawMergePostDrawExecution {
|
||||
.draw_mask_free = std::forward<DrawMaskFree>(draw_mask_free),
|
||||
.draw_mask_line = std::forward<DrawMaskLine>(draw_mask_line),
|
||||
.draw_smask_faces = std::forward<DrawSmaskFaces>(draw_smask_faces),
|
||||
.draw_grid_modes = std::forward<DrawGridModes>(draw_grid_modes_callback),
|
||||
.draw_heightmap = std::forward<DrawHeightmap>(draw_heightmap),
|
||||
.draw_current_modes = std::forward<DrawCurrentModes>(draw_current_modes),
|
||||
});
|
||||
}
|
||||
|
||||
inline void execute_legacy_canvas_draw_merge_smask_faces(
|
||||
const LegacyCanvasDrawMergeTextureMaskUniforms& uniforms,
|
||||
const glm::mat4& proj,
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
#include "canvas.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "renderer_gl/opengl_capabilities.h"
|
||||
|
||||
namespace {
|
||||
|
||||
GLint current_canvas_stroke_internal_format()
|
||||
{
|
||||
const auto renderer_features = ShaderManager::render_device_features();
|
||||
if (renderer_features.float32_linear_filtering)
|
||||
return static_cast<GLint>(pp::renderer::gl::rgba32f_internal_format());
|
||||
if (renderer_features.float16_render_targets)
|
||||
return static_cast<GLint>(pp::renderer::gl::rgba16f_internal_format());
|
||||
return static_cast<GLint>(pp::renderer::gl::rgba8_internal_format());
|
||||
}
|
||||
|
||||
GLint rgba8_internal_format()
|
||||
{
|
||||
return static_cast<GLint>(pp::renderer::gl::rgba8_internal_format());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Canvas::pick_start()
|
||||
{
|
||||
|
||||
@@ -476,7 +476,7 @@ void NodeCanvas::draw()
|
||||
plane_index,
|
||||
m_canvas->m_layers[layer_index]->m_opacity);
|
||||
|
||||
pp::panopainter::execute_legacy_canvas_draw_merge_layer_plane(
|
||||
pp::panopainter::execute_legacy_canvas_draw_merge_layer_path(
|
||||
m_canvas->m_current_stroke && m_canvas->m_current_mode == kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
||||
m_canvas->m_current_stroke && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
||||
use_blend,
|
||||
@@ -515,9 +515,6 @@ void NodeCanvas::draw()
|
||||
set_active_texture_unit(2);
|
||||
m_canvas->m_smask.rtt(plane_index).bindTexture();
|
||||
},
|
||||
.draw_temporary_erase_frame = [&](int frame, float onion_alpha) {
|
||||
draw_layer_frame(frame, onion_alpha);
|
||||
},
|
||||
.cleanup_temporary_erase = [&] {
|
||||
set_active_texture_unit(2);
|
||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
||||
@@ -571,9 +568,6 @@ void NodeCanvas::draw()
|
||||
b->m_pattern_texture->bind() :
|
||||
unbind_texture_2d();
|
||||
},
|
||||
.draw_temporary_paint_frame = [&](int frame, float onion_alpha) {
|
||||
draw_layer_frame(frame, onion_alpha);
|
||||
},
|
||||
.cleanup_temporary_paint = [&] {
|
||||
set_active_texture_unit(3);
|
||||
if (b->m_dual_enabled)
|
||||
@@ -593,9 +587,6 @@ void NodeCanvas::draw()
|
||||
.highlight = m_canvas->m_layers[layer_index]->m_hightlight,
|
||||
});
|
||||
},
|
||||
.draw_layer_texture_frame = [&](int frame, float onion_alpha) {
|
||||
draw_layer_frame(frame, onion_alpha);
|
||||
},
|
||||
.cleanup_layer_texture = [&] {
|
||||
},
|
||||
.draw_blend = [&] {
|
||||
@@ -637,24 +628,28 @@ void NodeCanvas::draw()
|
||||
set_active_texture_unit(0);
|
||||
m_blender_rtt.unbindTexture();
|
||||
},
|
||||
.draw_debug_outline =
|
||||
#ifdef _DEBUG
|
||||
[&] {
|
||||
auto bb = m_canvas->m_layers[layer_index]->box(plane_index) / (float)m_canvas->m_layers[layer_index]->w;
|
||||
glm::vec2 bbmin = xy(bb);
|
||||
glm::vec2 bbsz = zw(bb) - xy(bb);
|
||||
pp::panopainter::configure_legacy_ui_color_shader(
|
||||
plane_mvp_z
|
||||
* glm::translate(glm::vec3(bbmin * 2.f, 0))
|
||||
* glm::translate(glm::vec3(-1, -1, 0))
|
||||
* glm::scale(glm::vec3(bbsz, 1))
|
||||
* glm::translate(glm::vec3(1, 1, 0)),
|
||||
{ 1, 0, 0, 1 });
|
||||
m_face_plane.draw_stroke();
|
||||
},
|
||||
#else
|
||||
[] {
|
||||
},
|
||||
#endif
|
||||
.draw_frame = draw_layer_frame,
|
||||
});
|
||||
|
||||
#ifdef _DEBUG
|
||||
// draw dirty area
|
||||
{
|
||||
auto bb = m_canvas->m_layers[layer_index]->box(plane_index) / (float)m_canvas->m_layers[layer_index]->w;
|
||||
glm::vec2 bbmin = xy(bb);
|
||||
glm::vec2 bbsz = zw(bb) - xy(bb);
|
||||
pp::panopainter::configure_legacy_ui_color_shader(
|
||||
plane_mvp_z
|
||||
* glm::translate(glm::vec3(bbmin * 2.f, 0))
|
||||
* glm::translate(glm::vec3(-1, -1, 0))
|
||||
* glm::scale(glm::vec3(bbsz, 1))
|
||||
* glm::translate(glm::vec3(1, 1, 0)),
|
||||
{ 1, 0, 0, 1 });
|
||||
m_face_plane.draw_stroke();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (use_blend)
|
||||
@@ -705,58 +700,56 @@ void NodeCanvas::draw()
|
||||
|
||||
apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false);
|
||||
|
||||
pp::panopainter::execute_legacy_canvas_draw_merge_post_draw(
|
||||
pp::panopainter::execute_legacy_canvas_draw_merge_post_draw_callbacks(
|
||||
m_canvas->m_smask_active,
|
||||
m_canvas->m_current_mode == kCanvasMode::Copy || m_canvas->m_current_mode == kCanvasMode::Cut,
|
||||
m_canvas->m_smask_mode,
|
||||
m_canvas->m_current_mode != kCanvasMode::Grid,
|
||||
{
|
||||
.draw_mask_free = [&] {
|
||||
m_canvas->modes[(int)kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera);
|
||||
},
|
||||
.draw_mask_line = [&] {
|
||||
m_canvas->modes[(int)kCanvasMode::MaskLine][0]->on_Draw(ortho_proj, proj, camera);
|
||||
},
|
||||
.draw_smask_faces = [&] {
|
||||
pp::panopainter::execute_legacy_canvas_draw_merge_smask_faces(
|
||||
pp::panopainter::LegacyCanvasDrawMergeTextureMaskUniforms {
|
||||
.texture_slot = 0,
|
||||
.pattern_offset = m_outline_pan,
|
||||
[&] {
|
||||
m_canvas->modes[(int)kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera);
|
||||
},
|
||||
[&] {
|
||||
m_canvas->modes[(int)kCanvasMode::MaskLine][0]->on_Draw(ortho_proj, proj, camera);
|
||||
},
|
||||
[&] {
|
||||
pp::panopainter::execute_legacy_canvas_draw_merge_smask_faces(
|
||||
pp::panopainter::LegacyCanvasDrawMergeTextureMaskUniforms {
|
||||
.texture_slot = 0,
|
||||
.pattern_offset = m_outline_pan,
|
||||
},
|
||||
proj,
|
||||
camera,
|
||||
m_canvas->m_layers.size() + 500.f,
|
||||
std::to_array(m_canvas->m_plane_transform),
|
||||
{
|
||||
.set_active_texture_unit = [&] {
|
||||
set_active_texture_unit(0);
|
||||
},
|
||||
proj,
|
||||
camera,
|
||||
m_canvas->m_layers.size() + 500.f,
|
||||
std::to_array(m_canvas->m_plane_transform),
|
||||
{
|
||||
.set_active_texture_unit = [&] {
|
||||
set_active_texture_unit(0);
|
||||
},
|
||||
.enable_blend = [&] {
|
||||
apply_node_canvas_capability(pp::renderer::gl::blend_state(), true);
|
||||
},
|
||||
.bind_face_texture = [&](int plane_index) {
|
||||
m_canvas->m_smask.rtt(plane_index).bindTexture();
|
||||
},
|
||||
.draw_face = [&] {
|
||||
m_face_plane.draw_fill();
|
||||
},
|
||||
.unbind_face_texture = [&](int plane_index) {
|
||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
||||
},
|
||||
});
|
||||
},
|
||||
.draw_grid_modes = pp::panopainter::make_legacy_canvas_draw_merge_grid_modes_draw(
|
||||
&Canvas::modes[(int)kCanvasMode::Grid],
|
||||
ortho_proj,
|
||||
proj,
|
||||
camera),
|
||||
.draw_heightmap = pp::panopainter::make_legacy_canvas_draw_merge_heightmap_draw(App::I->grid.get(), proj, camera),
|
||||
.draw_current_modes = pp::panopainter::make_legacy_canvas_draw_merge_current_modes_draw(
|
||||
m_canvas->m_mode,
|
||||
ortho_proj,
|
||||
proj,
|
||||
camera),
|
||||
});
|
||||
.enable_blend = [&] {
|
||||
apply_node_canvas_capability(pp::renderer::gl::blend_state(), true);
|
||||
},
|
||||
.bind_face_texture = [&](int plane_index) {
|
||||
m_canvas->m_smask.rtt(plane_index).bindTexture();
|
||||
},
|
||||
.draw_face = [&] {
|
||||
m_face_plane.draw_fill();
|
||||
},
|
||||
.unbind_face_texture = [&](int plane_index) {
|
||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
||||
},
|
||||
});
|
||||
},
|
||||
pp::panopainter::make_legacy_canvas_draw_merge_grid_modes_draw(
|
||||
&Canvas::modes[(int)kCanvasMode::Grid],
|
||||
ortho_proj,
|
||||
proj,
|
||||
camera),
|
||||
pp::panopainter::make_legacy_canvas_draw_merge_heightmap_draw(App::I->grid.get(), proj, camera),
|
||||
pp::panopainter::make_legacy_canvas_draw_merge_current_modes_draw(
|
||||
m_canvas->m_mode,
|
||||
ortho_proj,
|
||||
proj,
|
||||
camera));
|
||||
|
||||
if (m_density != 1.f)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user