Extract dialog workflow, bootstrap, and canvas tail helpers

This commit is contained in:
2026-06-16 12:09:53 +02:00
parent 01854f9b10
commit 3407daff08
8 changed files with 586 additions and 534 deletions

View File

@@ -92,6 +92,7 @@ set(PP_PANOPAINTER_APP_SOURCES
src/app_cloud.cpp src/app_cloud.cpp
src/app_commands.cpp src/app_commands.cpp
src/app_dialogs.cpp src/app_dialogs.cpp
src/app_dialogs_workflow.cpp
src/app_dialogs_export.cpp src/app_dialogs_export.cpp
src/app_dialogs_info_openers.cpp src/app_dialogs_info_openers.cpp
src/app_events.cpp src/app_events.cpp
@@ -164,6 +165,7 @@ set(PP_PANOPAINTER_UI_SOURCES
set(PP_WINDOWS_PLATFORM_SOURCES set(PP_WINDOWS_PLATFORM_SOURCES
src/main.cpp src/main.cpp
src/platform_windows/windows_bootstrap_helpers.cpp
src/platform_windows/windows_platform_services.cpp src/platform_windows/windows_platform_services.cpp
src/platform_windows/windows_platform_services.h src/platform_windows/windows_platform_services.h
src/platform_windows/windows_splash.cpp src/platform_windows/windows_splash.cpp

View File

@@ -83,12 +83,12 @@ Current hotspot files:
- `src/app_layout.cpp`: 1249 lines - `src/app_layout.cpp`: 1249 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`: 1117 lines - `src/main.cpp`: 909 lines
- `src/node_panel_brush.cpp`: 1197 lines - `src/node_panel_brush.cpp`: 1197 lines
- `src/node_stroke_preview.cpp`: 933 lines - `src/node_stroke_preview.cpp`: 933 lines
- `src/node_canvas.cpp`: 897 lines - `src/node_canvas.cpp`: 905 lines
- `src/app.cpp`: 502 lines - `src/app.cpp`: 502 lines
- `src/app_dialogs.cpp`: 441 lines - `src/app_dialogs.cpp`: 142 lines
Current architecture mismatches that must be treated as real blockers: Current architecture mismatches that must be treated as real blockers:
@@ -128,7 +128,8 @@ Current architecture mismatches that must be treated as real blockers:
per-layer GL setup now also routes through per-layer GL setup now also routes through
`make_legacy_canvas_draw_merge_layer_path_gl_execution(...)` even though the `make_legacy_canvas_draw_merge_layer_path_gl_execution(...)` even though the
remaining draw lambdas and broader node draw loop still live in remaining draw lambdas and broader node draw loop still live in
`src/node_canvas.cpp`. `src/node_canvas.cpp`, where the post-draw/display-resolve tail now also
routes through `execute_node_canvas_draw_merge_tail(...)`.
- `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, even though tools-menu binding rather than thin composition/binding surfaces, even though tools-menu binding
plus nested panels/options submenu wiring now live in plus nested panels/options submenu wiring now live in
@@ -142,7 +143,9 @@ Current architecture mismatches that must be treated as real blockers:
and the corresponding `App::dialog_*` entrypoints are thinner, while the and the corresponding `App::dialog_*` entrypoints are thinner, while the
export/video/PPBR dialog family now also lives in export/video/PPBR dialog family now also lives in
`src/app_dialogs_export.cpp` and those `App::dialog_*` entrypoints are `src/app_dialogs_export.cpp` and those `App::dialog_*` entrypoints are
thinner too. thinner too, while new/open/save/browse/resize workflow entrypoints now also
live in `src/app_dialogs_workflow.cpp` and `src/app_dialogs.cpp` is now
mostly a thin dialog dispatch surface.
- `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
@@ -171,6 +174,9 @@ Current architecture mismatches that must be treated as real blockers:
in `src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`, in `src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`,
while the retained Win32 VR/HMD shell now also routes through while the retained Win32 VR/HMD shell now also routes through
`src/platform_windows/windows_vr_shell.h` instead of staying inline in `src/platform_windows/windows_vr_shell.h` instead of staying inline in
`src/main.cpp`, while RenderDoc startup/frame capture, SHCore DPI bootstrap,
Win32 error-string conversion, and the GL debug pre/post callbacks now also
live in `src/platform_windows/windows_bootstrap_helpers.cpp` instead of
`src/main.cpp`, `src/main.cpp`,
while `App::rec_loop()` now delegates worker-iteration orchestration into while `App::rec_loop()` now delegates worker-iteration orchestration into
the retained recording bridge, `App::update_rec_frames()` now delegates the retained recording bridge, `App::update_rec_frames()` now delegates

View File

@@ -205,6 +205,11 @@ Current slice:
`make_node_canvas_layer_path_execution(...)` helper, which materially thins `make_node_canvas_layer_path_execution(...)` helper, which materially thins
`NodeCanvas::draw()` even though the broader draw loop still lives in `NodeCanvas::draw()` even though the broader draw loop still lives in
`src/node_canvas.cpp`. `src/node_canvas.cpp`.
- `NodeCanvas` post-draw callback assembly, smoothing-mask face execution, and
optional display resolve now also route through
`execute_node_canvas_draw_merge_tail(...)`, which trims another live tail
block from `NodeCanvas::draw()` even though the broader outer draw shell is
still inline.
Write scope: Write scope:
- `src/node_stroke_preview.cpp` - `src/node_stroke_preview.cpp`
@@ -343,6 +348,9 @@ Current slice:
`src/app_dialogs_export.cpp`, and the corresponding `App::dialog_*` `src/app_dialogs_export.cpp`, and the corresponding `App::dialog_*`
entrypoints are now thin call-throughs, but new/open/save/browse/resize and entrypoints are now thin call-throughs, but new/open/save/browse/resize and
retained dialog execution are still inline in `src/app_dialogs.cpp`. retained dialog execution are still inline in `src/app_dialogs.cpp`.
- New/open/save/browse/resize workflow entrypoints now also live in
`src/app_dialogs_workflow.cpp`, and `src/app_dialogs.cpp` is down to the
remaining thin entrypoints plus layer-rename retained dialog glue.
Write scope: Write scope:
- `src/app_dialogs.cpp` - `src/app_dialogs.cpp`
@@ -448,6 +456,10 @@ Current slice:
state, now routes through `src/platform_windows/windows_vr_shell.h` instead state, now routes through `src/platform_windows/windows_vr_shell.h` instead
of staying inline in `src/main.cpp`, but broader retained Win32 shell state of staying inline in `src/main.cpp`, but broader retained Win32 shell state
is still open is still open
- RenderDoc startup/frame capture, SHCore DPI bootstrap, Win32 error-string
conversion, `UnadjustWindowRectEx`, and GL debug pre/post callbacks now also
live in `src/platform_windows/windows_bootstrap_helpers.cpp` instead of
`src/main.cpp`
- prepared-file background work now runs through an `AppRuntime`-owned worker - prepared-file background work now runs through an `AppRuntime`-owned worker
queue instead of a retained static worker in `src/app_events.cpp` queue instead of a retained static worker in `src/app_events.cpp`
- canvas async import/export/save/open background work now also runs through an - canvas async import/export/save/open background work now also runs through an

View File

@@ -2,100 +2,29 @@
#include "app.h" #include "app.h"
#include "app_core/app_dialog.h" #include "app_core/app_dialog.h"
#include "app_core/document_layer.h" #include "app_core/document_layer.h"
#include "app_core/document_resize.h"
#include "app_core/document_export.h"
#include "app_core/document_session.h"
#include "legacy_document_canvas_services.h"
#include "legacy_app_dialog_services.h" #include "legacy_app_dialog_services.h"
#include "legacy_brush_package_export_services.h"
#include "legacy_document_export_services.h"
#include "legacy_document_layer_services.h" #include "legacy_document_layer_services.h"
#include "legacy_document_session_services.h"
#include "legacy_ui_overlay_services.h" #include "legacy_ui_overlay_services.h"
#include "node_dialog_open.h" #include "node_dialog_layer_rename.h"
#include "node_dialog_browse.h"
#include "node_dialog_resize.h"
#include "node_dialog_cloud.h"
#ifdef __QUEST__ #ifdef __QUEST__
#include "oculus_vr.h" #include "oculus_vr.h"
#endif #endif
namespace pp::panopainter { namespace pp::panopainter {
void open_usermanual_dialog(App& app);
void open_changelog_dialog(App& app);
void open_about_dialog(App& app);
void open_whatsnew_dialog(App& app, bool force_show);
void open_shortcuts_dialog(App& app);
void open_document_export_dialog(App& app, std::string ext); void open_document_export_dialog(App& app, std::string ext);
void open_document_export_layers_dialog(App& app); void open_document_export_layers_dialog(App& app);
void open_document_export_anim_frames_dialog(App& app); void open_document_export_anim_frames_dialog(App& app);
void open_document_export_depth_dialog(App& app); void open_document_export_depth_dialog(App& app);
void open_document_export_cube_faces_dialog(App& app); void open_document_export_cube_faces_dialog(App& app);
void open_ppbr_export_dialog(App& app);
void open_document_timelapse_export_dialog(App& app); void open_document_timelapse_export_dialog(App& app);
void open_document_export_mp4_dialog(App& app); void open_document_export_mp4_dialog(App& app);
void open_ppbr_export_dialog(App& app); void open_usermanual_dialog(App& app);
} void open_changelog_dialog(App& app);
void open_about_dialog(App& app);
namespace { void open_whatsnew_dialog(App& app, bool force_show);
void open_shortcuts_dialog(App& app);
void wire_document_browse_dialog_actions(
App& app,
const std::shared_ptr<NodeDialogBrowse>& dialog,
Node& overlay_anchor,
pp::ui::NodeHandle overlay_handle)
{
const auto close_dialog = [&overlay_anchor, overlay_handle]() {
const auto close_status =
pp::panopainter::close_legacy_overlay_node(overlay_anchor, overlay_handle);
(void)close_status;
};
dialog->btn_ok->on_click = [&app, dialog, close_dialog](Node*)
{
if (dialog->is_selected())
{
app.open_document(dialog->selected_path);
close_dialog();
}
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
}
void wire_document_save_dialog_buttons(
App& app,
const std::shared_ptr<NodeDialogSave>& dialog,
std::function<void()> close_dialog)
{
dialog->btn_ok->on_click = [&app, dialog](Node*)
{
std::string name = dialog->input->m_text;
const auto plan = pp::app::plan_document_file_save(
app.work_path,
name,
[](const std::string& path) {
return Asset::exist(path);
});
if (!plan)
{
app.message_box("Warning", "You need to specify a name to file.");
return;
}
const auto status =
pp::panopainter::execute_legacy_document_file_save_plan(app, plan.value(), dialog);
if (!status.ok())
LOG("Document file save action failed: %s", status.message);
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
}
} }
std::shared_ptr<NodeProgressBar> App::show_progress(const std::string& title, int total /*= 0*/) std::shared_ptr<NodeProgressBar> App::show_progress(const std::string& title, int total /*= 0*/)
@@ -139,234 +68,6 @@ void App::dialog_about()
pp::panopainter::open_about_dialog(*this); pp::panopainter::open_about_dialog(*this);
} }
void App::continue_document_workflow_after_optional_save(std::function<void()> action)
{
const bool has_canvas = canvas != nullptr;
const bool has_unsaved_changes = has_canvas && Canvas::I->m_unsaved;
const auto decision = pp::app::plan_document_workflow(has_canvas, has_unsaved_changes);
const auto status = pp::panopainter::execute_legacy_document_workflow_decision(
*this,
decision,
std::move(action));
if (!status.ok())
LOG("Document workflow action failed: %s", status.message);
}
void App::dialog_newdoc()
{
auto show_dialog = [this] {
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("New document dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogNewDoc>(*this);
dialog->input->set_text("name");
App::I->showKeyboard();
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
App::I->hideKeyboard();
LOG("New document dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
const auto close_dialog = [this, overlay_anchor, overlay_handle]() {
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
(void)close_status;
App::I->hideKeyboard();
};
dialog->btn_ok->on_click = [this, dialog](Node*)
{
std::string name = dialog->input->m_text;
const auto plan = pp::app::plan_new_document(
work_path,
name,
dialog->m_resolution->m_current_index,
[](const std::string& path) {
return Asset::exist(path);
});
if (!plan)
{
const bool missing_name =
plan.status().code == pp::foundation::StatusCode::invalid_argument;
message_box(
"Warning",
missing_name ? "You need to specify a name to file." : plan.status().message);
return;
}
const auto status = pp::panopainter::execute_legacy_new_document_plan(*this, plan.value(), dialog);
if (!status.ok())
LOG("New document action failed: %s", status.message);
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
};
continue_document_workflow_after_optional_save(show_dialog);
}
// DEPRECATED
void App::dialog_open()
{
auto show_dialog = [this] {
// load thumbnail test
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Open document dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogOpen>(*this);
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("Open document dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
const auto close_dialog = [overlay_anchor, overlay_handle]() {
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
(void)close_status;
};
dialog->btn_ok->on_click = [this, dialog](Node*)
{
// canvas->reset_camera();
// layers->clear();
// doc_name = dialog->selected_name;
// canvas->m_canvas->project_open(dialog->selected_path, [this](bool success) {
// // on complete
// async_start();
// title_update();
// for (auto& i : canvas->m_canvas->m_order)
// layers->add_layer(canvas->m_canvas->m_layers[i]->m_name.c_str());
// async_end();
// });
// dialog->destroy();
// ActionManager::clear();
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
};
continue_document_workflow_after_optional_save(show_dialog);
}
void App::dialog_browse()
{
auto show_dialog = [this] {
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Browse document dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogBrowse>(*this);
dialog->search_paths = document_browse_roots();
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("Browse document dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
wire_document_browse_dialog_actions(*this, dialog, *overlay_anchor, overlay_handle);
};
continue_document_workflow_after_optional_save(show_dialog);
}
void save_document_version(App& app)
{
const auto target = pp::app::find_next_document_version_target(
app.doc_dir,
app.doc_name,
[](const std::string& path) {
return Asset::exist(path);
});
if (!target) {
app.message_box("Saving Error", target.status().message);
return;
}
const auto status = pp::panopainter::execute_legacy_document_version_save(app, target.value());
if (!status.ok())
LOG("Document version save action failed: %s", status.message);
}
void App::dialog_save_ver()
{
if (!check_license())
{
message_box("License", "This function is disabled in demo mode.");
return;
}
save_document_version(*this);
}
void App::save_document(pp::app::DocumentSaveIntent intent)
{
const auto decision = pp::app::plan_document_save(
Canvas::I->m_newdoc,
Canvas::I->m_unsaved,
intent);
const auto status = pp::panopainter::execute_legacy_document_save_decision(*this, decision);
if (!status.ok())
LOG("Document save action failed: %s", status.message);
}
void App::dialog_save()
{
if (!check_license())
{
message_box("License", "This function is disabled in demo mode.");
return;
}
if (canvas)
{
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Save document dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogSave>(*this);
dialog->input->set_text(doc_name);
App::I->showKeyboard();
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
App::I->hideKeyboard();
LOG("Save document dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
const auto close_dialog = [this, overlay_anchor, overlay_handle]() {
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
(void)close_status;
App::I->hideKeyboard();
};
wire_document_save_dialog_buttons(*this, dialog, close_dialog);
}
}
void App::dialog_export(std::string ext) void App::dialog_export(std::string ext)
{ {
pp::panopainter::open_document_export_dialog(*this, ext); pp::panopainter::open_document_export_dialog(*this, ext);
@@ -387,47 +88,6 @@ void App::dialog_export_depth()
pp::panopainter::open_document_export_depth_dialog(*this); pp::panopainter::open_document_export_depth_dialog(*this);
} }
void App::dialog_resize()
{
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Resize dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogResize>(*this);
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("Resize dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
const auto close_dialog = [overlay_anchor, overlay_handle]() {
const auto close_status =
pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
(void)close_status;
};
dialog->btn_ok->on_click = [this, dialog, close_dialog](Node*)
{
const auto plan = pp::app::plan_document_resize(
dialog->combo ? dialog->combo->m_current_index : 0);
if (!plan)
{
close_dialog();
return;
}
const auto status = pp::panopainter::execute_legacy_document_resize_plan(*this, plan.value());
if (!status.ok())
LOG("Document resize failed: %s", status.message);
close_dialog();
};
dialog->btn_cancel->on_click = [close_dialog](Node*) {
close_dialog();
};
}
void App::dialog_export_cube_faces() void App::dialog_export_cube_faces()
{ {
pp::panopainter::open_document_export_cube_faces_dialog(*this); pp::panopainter::open_document_export_cube_faces_dialog(*this);

View File

@@ -0,0 +1,343 @@
#include "pch.h"
#include "app.h"
#include "app_core/document_resize.h"
#include "legacy_document_canvas_services.h"
#include "legacy_document_session_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_dialog_browse.h"
#include "node_dialog_open.h"
#include "node_dialog_resize.h"
namespace {
void wire_document_browse_dialog_actions(
App& app,
const std::shared_ptr<NodeDialogBrowse>& dialog,
Node& overlay_anchor,
pp::ui::NodeHandle overlay_handle)
{
const auto close_dialog = [&overlay_anchor, overlay_handle]() {
const auto close_status =
pp::panopainter::close_legacy_overlay_node(overlay_anchor, overlay_handle);
(void)close_status;
};
dialog->btn_ok->on_click = [&app, dialog, close_dialog](Node*)
{
if (dialog->is_selected())
{
app.open_document(dialog->selected_path);
close_dialog();
}
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
}
void wire_document_save_dialog_buttons(
App& app,
const std::shared_ptr<NodeDialogSave>& dialog,
std::function<void()> close_dialog)
{
dialog->btn_ok->on_click = [&app, dialog](Node*)
{
std::string name = dialog->input->m_text;
const auto plan = pp::app::plan_document_file_save(
app.work_path,
name,
[](const std::string& path) {
return Asset::exist(path);
});
if (!plan)
{
app.message_box("Warning", "You need to specify a name to file.");
return;
}
const auto status =
pp::panopainter::execute_legacy_document_file_save_plan(app, plan.value(), dialog);
if (!status.ok())
LOG("Document file save action failed: %s", status.message);
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
}
void save_document_version(App& app)
{
const auto target = pp::app::find_next_document_version_target(
app.doc_dir,
app.doc_name,
[](const std::string& path) {
return Asset::exist(path);
});
if (!target) {
app.message_box("Saving Error", target.status().message);
return;
}
const auto status = pp::panopainter::execute_legacy_document_version_save(app, target.value());
if (!status.ok())
LOG("Document version save action failed: %s", status.message);
}
} // namespace
void App::continue_document_workflow_after_optional_save(std::function<void()> action)
{
const bool has_canvas = canvas != nullptr;
const bool has_unsaved_changes = has_canvas && Canvas::I->m_unsaved;
const auto decision = pp::app::plan_document_workflow(has_canvas, has_unsaved_changes);
const auto status = pp::panopainter::execute_legacy_document_workflow_decision(
*this,
decision,
std::move(action));
if (!status.ok())
LOG("Document workflow action failed: %s", status.message);
}
void App::dialog_newdoc()
{
auto show_dialog = [this] {
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("New document dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogNewDoc>(*this);
dialog->input->set_text("name");
App::I->showKeyboard();
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
App::I->hideKeyboard();
LOG("New document dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
const auto close_dialog = [this, overlay_anchor, overlay_handle]() {
const auto close_status =
pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
(void)close_status;
App::I->hideKeyboard();
};
dialog->btn_ok->on_click = [this, dialog](Node*)
{
std::string name = dialog->input->m_text;
const auto plan = pp::app::plan_new_document(
work_path,
name,
dialog->m_resolution->m_current_index,
[](const std::string& path) {
return Asset::exist(path);
});
if (!plan)
{
const bool missing_name =
plan.status().code == pp::foundation::StatusCode::invalid_argument;
message_box(
"Warning",
missing_name ? "You need to specify a name to file." : plan.status().message);
return;
}
const auto status =
pp::panopainter::execute_legacy_new_document_plan(*this, plan.value(), dialog);
if (!status.ok())
LOG("New document action failed: %s", status.message);
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
};
continue_document_workflow_after_optional_save(show_dialog);
}
// DEPRECATED
void App::dialog_open()
{
auto show_dialog = [this] {
// load thumbnail test
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Open document dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogOpen>(*this);
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("Open document dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
const auto close_dialog = [overlay_anchor, overlay_handle]() {
const auto close_status =
pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
(void)close_status;
};
dialog->btn_ok->on_click = [this, dialog](Node*)
{
// canvas->reset_camera();
// layers->clear();
// doc_name = dialog->selected_name;
// canvas->m_canvas->project_open(dialog->selected_path, [this](bool success) {
// // on complete
// async_start();
// title_update();
// for (auto& i : canvas->m_canvas->m_order)
// layers->add_layer(canvas->m_canvas->m_layers[i]->m_name.c_str());
// async_end();
// });
// dialog->destroy();
// ActionManager::clear();
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
};
continue_document_workflow_after_optional_save(show_dialog);
}
void App::dialog_browse()
{
auto show_dialog = [this] {
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Browse document dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogBrowse>(*this);
dialog->search_paths = document_browse_roots();
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("Browse document dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
wire_document_browse_dialog_actions(*this, dialog, *overlay_anchor, overlay_handle);
};
continue_document_workflow_after_optional_save(show_dialog);
}
void App::dialog_save_ver()
{
if (!check_license())
{
message_box("License", "This function is disabled in demo mode.");
return;
}
save_document_version(*this);
}
void App::save_document(pp::app::DocumentSaveIntent intent)
{
const auto decision = pp::app::plan_document_save(
Canvas::I->m_newdoc,
Canvas::I->m_unsaved,
intent);
const auto status = pp::panopainter::execute_legacy_document_save_decision(*this, decision);
if (!status.ok())
LOG("Document save action failed: %s", status.message);
}
void App::dialog_save()
{
if (!check_license())
{
message_box("License", "This function is disabled in demo mode.");
return;
}
if (canvas)
{
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Save document dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogSave>(*this);
dialog->input->set_text(doc_name);
App::I->showKeyboard();
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
App::I->hideKeyboard();
LOG("Save document dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
const auto close_dialog = [this, overlay_anchor, overlay_handle]() {
const auto close_status =
pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
(void)close_status;
App::I->hideKeyboard();
};
wire_document_save_dialog_buttons(*this, dialog, close_dialog);
}
}
void App::dialog_resize()
{
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Resize dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogResize>(*this);
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("Resize dialog open failed: %s", overlay.status().message);
return;
}
const auto overlay_handle = overlay.value();
const auto close_dialog = [overlay_anchor, overlay_handle]() {
const auto close_status =
pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
(void)close_status;
};
dialog->btn_ok->on_click = [this, dialog, close_dialog](Node*)
{
const auto plan = pp::app::plan_document_resize(
dialog->combo ? dialog->combo->m_current_index : 0);
if (!plan)
{
close_dialog();
return;
}
const auto status = pp::panopainter::execute_legacy_document_resize_plan(*this, plan.value());
if (!status.ok())
LOG("Document resize failed: %s", status.message);
close_dialog();
};
dialog->btn_cancel->on_click = [close_dialog](Node*) {
close_dialog();
};
}

View File

@@ -43,6 +43,15 @@ namespace pp::platform::windows {
void unlock_async_render_context(); void unlock_async_render_context();
void swap_async_render_context(); void swap_async_render_context();
} }
extern HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
extern HRESULT(*SetProcessDpiAwareness_fn)(PROCESS_DPI_AWARENESS value);
bool win32_renderdoc_init();
void win32_renderdoc_frame_start();
void win32_renderdoc_frame_end();
void init_shcore_API();
std::string GetLastErrorAsString();
void _pre_call_callback(const char* name, void* funcptr, int len_args, ...);
void _post_call_callback(const char* name, void* funcptr, int len_args, ...);
struct RetainedState struct RetainedState
{ {
HINSTANCE hInst{}; HINSTANCE hInst{};
@@ -120,71 +129,6 @@ void pp_windows_enqueue_main_task(std::packaged_task<void()> task)
std::atomic<int> running{-1}; std::atomic<int> running{-1};
#ifdef USE_RENDERDOC
RENDERDOC_API_1_4_0* rdoc_api = NULL;
bool win32_renderdoc_init()
{
// At init, on windows
if (HMODULE mod = GetModuleHandleA("renderdoc.dll"))
{
pRENDERDOC_GetAPI RENDERDOC_GetAPI =
(pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
return RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void**)&rdoc_api);
}
return false;
}
void win32_renderdoc_frame_start()
{
if (rdoc_api)
rdoc_api->StartFrameCapture(NULL, NULL);
}
void win32_renderdoc_frame_end()
{
if (rdoc_api)
rdoc_api->EndFrameCapture(NULL, NULL);
}
#else
void win32_renderdoc_frame_start() { }
void win32_renderdoc_frame_end() { }
#endif
HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
HRESULT(*SetProcessDpiAwareness_fn)(PROCESS_DPI_AWARENESS value);
void init_shcore_API()
{
HMODULE dll = LoadLibrary(L"Shcore.dll");
if (!dll)
{
LOG("cannot load Shcore.dll");
return;
}
LOG("loaded Shcore.dll");
GetDpiForMonitor_fn = (decltype(GetDpiForMonitor_fn))GetProcAddress(dll, "GetDpiForMonitor");
SetProcessDpiAwareness_fn = (decltype(SetProcessDpiAwareness_fn))GetProcAddress(dll, "SetProcessDpiAwareness");
}
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::string GetLastErrorAsString()
{
//Get the error message, if any.
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0)
return std::string(); //No error message has been recorded
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
std::string message(messageBuffer, size);
//Free the buffer.
LocalFree(messageBuffer);
return message;
}
void destroy_window() void destroy_window()
{ {
auto& state = retained_state(); auto& state = retained_state();
@@ -531,36 +475,6 @@ void win32_save_window_state()
p.rcNormalPosition.right, p.rcNormalPosition.right,
p.rcNormalPosition.bottom }); p.rcNormalPosition.bottom });
} }
BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle)
{
RECT rc;
SetRectEmpty(&rc);
BOOL fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle);
if (fRc) {
prc->left -= rc.left;
prc->top -= rc.top;
prc->right -= rc.right;
prc->bottom -= rc.bottom;
}
return fRc;
}
void _pre_call_callback(const char* name, void* funcptr, int len_args, ...)
{
assert(App::I->is_render_thread());
}
void _post_call_callback(const char* name, void* funcptr, int len_args, ...)
{
GLenum error_code;
error_code = glad_glGetError();
if (error_code != pp::renderer::gl::no_error_code())
{
LOG("ERROR %d in %s\n", error_code, name);
}
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
auto& state = retained_state(); auto& state = retained_state();

View File

@@ -395,6 +395,101 @@ pp::panopainter::LegacyCanvasDrawMergeLayerPathExecution make_node_canvas_layer_
draw_layer_frame); draw_layer_frame);
} }
void execute_node_canvas_draw_merge_tail(
NodeCanvas& node_canvas,
const glm::mat4& ortho_proj,
const glm::mat4& proj,
const glm::mat4& camera,
const glm::ivec4& c)
{
apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false);
pp::panopainter::execute_legacy_canvas_draw_merge_post_draw_callbacks(
node_canvas.m_canvas->m_smask_active,
node_canvas.m_canvas->m_current_mode == kCanvasMode::Copy || node_canvas.m_canvas->m_current_mode == kCanvasMode::Cut,
node_canvas.m_canvas->m_smask_mode,
node_canvas.m_canvas->m_current_mode != kCanvasMode::Grid,
[&] {
node_canvas.m_canvas->modes[(int)kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera);
},
[&] {
node_canvas.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 = node_canvas.m_outline_pan,
},
proj,
camera,
node_canvas.m_canvas->m_layers.size() + 500.f,
std::to_array(node_canvas.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) {
node_canvas.m_canvas->m_smask.rtt(plane_index).bindTexture();
},
.draw_face = [&] {
node_canvas.m_face_plane.draw_fill();
},
.unbind_face_texture = [&](int plane_index) {
node_canvas.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(
node_canvas.m_canvas->m_mode,
ortho_proj,
proj,
camera));
if (node_canvas.m_density != 1.f) {
pp::panopainter::execute_legacy_canvas_draw_merge_display_resolve(
pp::panopainter::LegacyCanvasDrawMergeDisplayResolveUniforms {
.texture = {
.mvp = glm::ortho<float>(-1, 1, -1, 1),
.texture_slot = 0,
},
},
{
.unbind_resolve_framebuffer = [&] {
node_canvas.m_rtt.unbindFramebuffer();
},
.clear_color_buffer = [&] {
clear_node_canvas_color_buffer({ 1.f, 1.f, 1.f, 0.f });
},
.apply_viewport = [&] {
apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w);
},
.bind_sampler = [&] {
node_canvas.m_sampler_nearest.bind(0);
},
.bind_resolve_texture = [&] {
set_active_texture_unit(0);
node_canvas.m_rtt.bindTexture();
},
.draw = [&] {
node_canvas.m_face_plane.draw_fill();
},
.unbind_resolve_texture = [&] {
node_canvas.m_rtt.unbindTexture();
},
});
}
}
} }
Node* NodeCanvas::clone_instantiate() const Node* NodeCanvas::clone_instantiate() const
@@ -700,93 +795,7 @@ void NodeCanvas::draw()
} }
} }
apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); execute_node_canvas_draw_merge_tail(*this, ortho_proj, proj, camera, c);
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,
[&] {
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);
},
.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)
{
pp::panopainter::execute_legacy_canvas_draw_merge_display_resolve(
pp::panopainter::LegacyCanvasDrawMergeDisplayResolveUniforms {
.texture = {
.mvp = glm::ortho<float>(-1, 1, -1, 1),
.texture_slot = 0,
},
},
{
.unbind_resolve_framebuffer = [&] {
m_rtt.unbindFramebuffer();
},
.clear_color_buffer = [&] {
clear_node_canvas_color_buffer({ 1.f, 1.f, 1.f, 0.f });
},
.apply_viewport = [&] {
apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w);
},
.bind_sampler = [&] {
m_sampler_nearest.bind(0);
},
.bind_resolve_texture = [&] {
set_active_texture_unit(0);
m_rtt.bindTexture();
},
.draw = [&] {
m_face_plane.draw_fill();
},
.unbind_resolve_texture = [&] {
m_rtt.unbindTexture();
},
});
}
scissor ? apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), true) : apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), false); scissor ? apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), true) : apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), false);
blend ? apply_node_canvas_capability(pp::renderer::gl::blend_state(), true) : apply_node_canvas_capability(pp::renderer::gl::blend_state(), false); blend ? apply_node_canvas_capability(pp::renderer::gl::blend_state(), true) : apply_node_canvas_capability(pp::renderer::gl::blend_state(), false);

View File

@@ -0,0 +1,106 @@
#include "pch.h"
#include "app.h"
#include "log.h"
#include "renderer_gl/opengl_capabilities.h"
#include <shellscalingapi.h>
#include <string>
#if __has_include(<renderdoc_app.h>)
#include <renderdoc_app.h>
#define USE_RENDERDOC
#endif
#ifdef USE_RENDERDOC
RENDERDOC_API_1_4_0* rdoc_api = NULL;
bool win32_renderdoc_init()
{
// At init, on windows
if (HMODULE mod = GetModuleHandleA("renderdoc.dll"))
{
pRENDERDOC_GetAPI RENDERDOC_GetAPI =
(pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
return RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void**)&rdoc_api);
}
return false;
}
void win32_renderdoc_frame_start()
{
if (rdoc_api)
rdoc_api->StartFrameCapture(NULL, NULL);
}
void win32_renderdoc_frame_end()
{
if (rdoc_api)
rdoc_api->EndFrameCapture(NULL, NULL);
}
#else
void win32_renderdoc_frame_start() { }
void win32_renderdoc_frame_end() { }
#endif
HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
HRESULT(*SetProcessDpiAwareness_fn)(PROCESS_DPI_AWARENESS value);
void init_shcore_API()
{
HMODULE dll = LoadLibrary(L"Shcore.dll");
if (!dll)
{
LOG("cannot load Shcore.dll");
return;
}
LOG("loaded Shcore.dll");
GetDpiForMonitor_fn = (decltype(GetDpiForMonitor_fn))GetProcAddress(dll, "GetDpiForMonitor");
SetProcessDpiAwareness_fn = (decltype(SetProcessDpiAwareness_fn))GetProcAddress(dll, "SetProcessDpiAwareness");
}
// Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::string GetLastErrorAsString()
{
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0)
return std::string();
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
std::string message(messageBuffer, size);
LocalFree(messageBuffer);
return message;
}
BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle)
{
RECT rc;
SetRectEmpty(&rc);
BOOL fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle);
if (fRc) {
prc->left -= rc.left;
prc->top -= rc.top;
prc->right -= rc.right;
prc->bottom -= rc.bottom;
}
return fRc;
}
void _pre_call_callback(const char* name, void* funcptr, int len_args, ...)
{
assert(App::I->is_render_thread());
}
void _post_call_callback(const char* name, void* funcptr, int len_args, ...)
{
GLenum error_code;
error_code = glad_glGetError();
if (error_code != pp::renderer::gl::no_error_code())
{
LOG("ERROR %d in %s\n", error_code, name);
}
}