Thin canvas state, tools menu, and node canvas draw

This commit is contained in:
2026-06-16 10:13:59 +02:00
parent d68c97e609
commit 4a5bb68fe2
7 changed files with 729 additions and 521 deletions

View File

@@ -79,14 +79,14 @@ What is still carrying too much live ownership:
Current hotspot files: Current hotspot files:
- `src/canvas.cpp`: 4128 lines - `src/canvas.cpp`: 3622 lines
- `src/app_layout.cpp`: 2026 lines - `src/app_layout.cpp`: 1498 lines
- `src/canvas_modes.cpp`: 1798 lines - `src/canvas_modes.cpp`: 1798 lines
- `src/node.cpp`: 1551 lines - `src/node.cpp`: 1551 lines
- `src/main.cpp`: 1374 lines - `src/main.cpp`: 1374 lines
- `src/node_panel_brush.cpp`: 1197 lines - `src/node_panel_brush.cpp`: 1197 lines
- `src/node_stroke_preview.cpp`: 1129 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.cpp`: 950 lines
- `src/app_dialogs.cpp`: 908 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 merged-texture draw callback plus non-`draw_merged` per-frame layer draw
callback plus the smoothing-mask face shader/draw pass plus heightmap, callback plus the smoothing-mask face shader/draw pass plus heightmap,
current-mode, and grid-mode callback setup now routed through the same 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 - `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 - `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
global singleton reach, raw observer pointers, retained static worker global singleton reach, raw observer pointers, retained static worker
ownership in several app families, and ad hoc mutex/condition-variable 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 prepared-file worker and the canvas async import/export/save/open worker now
live under `AppRuntime` instead of retained static app-events/canvas live under `AppRuntime` instead of retained static app-events/canvas
workers, `App::rec_loop()` now delegates worker-iteration orchestration into workers, `App::rec_loop()` now delegates worker-iteration orchestration into
the retained recording bridge, and `App::update_rec_frames()` now delegates the retained recording bridge, `App::update_rec_frames()` now delegates
recording label refresh through that same retained recording path even though recording label refresh through that same retained recording path, and the
the bridge still owns worker-side readback flow and encoder-state label canvas state-management cluster for picking, clear/clear-all, layer
reads. 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`, - Modern C++23 usage exists in extracted components, especially `std::span`,
explicit result/status objects, and a few concepts, but the live app still explicit result/status objects, and a few concepts, but the live app still
does not consistently express ownership, thread affinity, or renderer does not consistently express ownership, thread affinity, or renderer

View File

@@ -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 #### ARC-RND-001 - Split `canvas.cpp` Into Document State And Render Execution Shells
Status: Ready Status: In Progress
Why now: Why now:
`src/canvas.cpp` is still the biggest single architectural blocker at about `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: Write scope:
- `src/canvas.cpp` - `src/canvas.cpp`
@@ -171,6 +178,11 @@ Current slice:
now also routes through `execute_legacy_canvas_draw_merge_layer_plane(...)`, now also routes through `execute_legacy_canvas_draw_merge_layer_plane(...)`,
but the node still owns substantial live layer traversal and renderer state but the node still owns substantial live layer traversal and renderer state
orchestration. 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: Write scope:
- `src/node_stroke_preview.cpp` - `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 #### ARC-APP-001 - Split `app_layout.cpp` Into UI Binding Modules
Status: Ready Status: In Progress
Why now: 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. 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: Write scope:
- `src/app_layout.cpp` - `src/app_layout.cpp`
- `src/legacy_app_shell_services.*` - `src/legacy_app_shell_services.*`

View File

@@ -33,6 +33,10 @@
#include <vector> #include <vector>
namespace pp::panopainter {
void bind_legacy_tools_menu(App& app);
}
namespace { namespace {
bool apply_brush_color_plan(App& app, glm::vec4 color, bool update_quick, bool update_color_panel) 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() void App::init_menu_tools()
{ {
auto main = layout[main_id]; pp::panopainter::bind_legacy_tools_menu(*this);
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);
}
};
}
};
}
} }
void App::init_menu_about() void App::init_menu_about()

View 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

View File

@@ -4,6 +4,7 @@
#include <array> #include <array>
#include <functional> #include <functional>
#include <utility>
namespace pp::panopainter { namespace pp::panopainter {
@@ -114,6 +115,21 @@ struct LegacyCanvasDrawMergeLayerPlaneExecution {
std::function<void()> draw_blend; 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 { struct LegacyCanvasDrawMergeTemporaryCompositeExecution {
std::function<void()> setup; std::function<void()> setup;
std::function<void()> bind_samplers; 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( inline void execute_legacy_canvas_draw_merge_temporary_composite(
const LegacyCanvasDrawMergeTemporaryCompositeExecution& execution) const LegacyCanvasDrawMergeTemporaryCompositeExecution& execution)
{ {
@@ -759,6 +817,40 @@ inline void execute_legacy_canvas_draw_merge_post_draw(
execution.draw_current_modes(); 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( inline void execute_legacy_canvas_draw_merge_smask_faces(
const LegacyCanvasDrawMergeTextureMaskUniforms& uniforms, const LegacyCanvasDrawMergeTextureMaskUniforms& uniforms,
const glm::mat4& proj, const glm::mat4& proj,

View File

@@ -3,6 +3,26 @@
#include "canvas.h" #include "canvas.h"
#include "app.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() void Canvas::pick_start()
{ {

View File

@@ -476,7 +476,7 @@ void NodeCanvas::draw()
plane_index, plane_index,
m_canvas->m_layers[layer_index]->m_opacity); 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_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, m_canvas->m_current_stroke && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
use_blend, use_blend,
@@ -515,9 +515,6 @@ void NodeCanvas::draw()
set_active_texture_unit(2); set_active_texture_unit(2);
m_canvas->m_smask.rtt(plane_index).bindTexture(); 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 = [&] { .cleanup_temporary_erase = [&] {
set_active_texture_unit(2); set_active_texture_unit(2);
m_canvas->m_smask.rtt(plane_index).unbindTexture(); m_canvas->m_smask.rtt(plane_index).unbindTexture();
@@ -571,9 +568,6 @@ void NodeCanvas::draw()
b->m_pattern_texture->bind() : b->m_pattern_texture->bind() :
unbind_texture_2d(); unbind_texture_2d();
}, },
.draw_temporary_paint_frame = [&](int frame, float onion_alpha) {
draw_layer_frame(frame, onion_alpha);
},
.cleanup_temporary_paint = [&] { .cleanup_temporary_paint = [&] {
set_active_texture_unit(3); set_active_texture_unit(3);
if (b->m_dual_enabled) if (b->m_dual_enabled)
@@ -593,9 +587,6 @@ void NodeCanvas::draw()
.highlight = m_canvas->m_layers[layer_index]->m_hightlight, .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 = [&] { .cleanup_layer_texture = [&] {
}, },
.draw_blend = [&] { .draw_blend = [&] {
@@ -637,11 +628,9 @@ void NodeCanvas::draw()
set_active_texture_unit(0); set_active_texture_unit(0);
m_blender_rtt.unbindTexture(); m_blender_rtt.unbindTexture();
}, },
}); .draw_debug_outline =
#ifdef _DEBUG
#ifdef _DEBUG [&] {
// draw dirty area
{
auto bb = m_canvas->m_layers[layer_index]->box(plane_index) / (float)m_canvas->m_layers[layer_index]->w; 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 bbmin = xy(bb);
glm::vec2 bbsz = zw(bb) - xy(bb); glm::vec2 bbsz = zw(bb) - xy(bb);
@@ -653,8 +642,14 @@ void NodeCanvas::draw()
* glm::translate(glm::vec3(1, 1, 0)), * glm::translate(glm::vec3(1, 1, 0)),
{ 1, 0, 0, 1 }); { 1, 0, 0, 1 });
m_face_plane.draw_stroke(); m_face_plane.draw_stroke();
} },
#endif #else
[] {
},
#endif
.draw_frame = draw_layer_frame,
});
} }
} }
if (use_blend) if (use_blend)
@@ -705,19 +700,18 @@ void NodeCanvas::draw()
apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); 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_smask_active,
m_canvas->m_current_mode == kCanvasMode::Copy || m_canvas->m_current_mode == kCanvasMode::Cut, m_canvas->m_current_mode == kCanvasMode::Copy || m_canvas->m_current_mode == kCanvasMode::Cut,
m_canvas->m_smask_mode, m_canvas->m_smask_mode,
m_canvas->m_current_mode != kCanvasMode::Grid, m_canvas->m_current_mode != kCanvasMode::Grid,
{ [&] {
.draw_mask_free = [&] {
m_canvas->modes[(int)kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera); 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); 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::execute_legacy_canvas_draw_merge_smask_faces(
pp::panopainter::LegacyCanvasDrawMergeTextureMaskUniforms { pp::panopainter::LegacyCanvasDrawMergeTextureMaskUniforms {
.texture_slot = 0, .texture_slot = 0,
@@ -745,18 +739,17 @@ void NodeCanvas::draw()
}, },
}); });
}, },
.draw_grid_modes = pp::panopainter::make_legacy_canvas_draw_merge_grid_modes_draw( pp::panopainter::make_legacy_canvas_draw_merge_grid_modes_draw(
&Canvas::modes[(int)kCanvasMode::Grid], &Canvas::modes[(int)kCanvasMode::Grid],
ortho_proj, ortho_proj,
proj, proj,
camera), camera),
.draw_heightmap = pp::panopainter::make_legacy_canvas_draw_merge_heightmap_draw(App::I->grid.get(), proj, camera), 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( pp::panopainter::make_legacy_canvas_draw_merge_current_modes_draw(
m_canvas->m_mode, m_canvas->m_mode,
ortho_proj, ortho_proj,
proj, proj,
camera), camera));
});
if (m_density != 1.f) if (m_density != 1.f)
{ {