Thin app dialog export and popup seams

This commit is contained in:
2026-06-17 19:49:35 +02:00
parent b505d9f727
commit c63a96cc87
20 changed files with 670 additions and 522 deletions

View File

@@ -163,6 +163,8 @@ set(PP_PANOPAINTER_APP_SOURCES
src/legacy_draw_toolbar_binding_services.h src/legacy_draw_toolbar_binding_services.h
src/app_layout_ui_state.cpp src/app_layout_ui_state.cpp
src/app_layout_sidebar.cpp src/app_layout_sidebar.cpp
src/legacy_sidebar_grid_popup_services.cpp
src/legacy_sidebar_grid_popup_services.h
src/legacy_sidebar_stroke_popup_services.cpp src/legacy_sidebar_stroke_popup_services.cpp
src/legacy_sidebar_stroke_popup_services.h src/legacy_sidebar_stroke_popup_services.h
src/legacy_sidebar_color_popup_services.cpp src/legacy_sidebar_color_popup_services.cpp

View File

@@ -792,26 +792,40 @@ agent or engineer to remove them without reconstructing context from chat.
`bind_legacy_click_destroys_node(...)`; menu/layout-owned popup families still `bind_legacy_click_destroys_node(...)`; menu/layout-owned popup families still
remain on separate retained cleanup paths. remain on separate retained cleanup paths.
- 2026-06-17: `DEBT-0058`/`DEBT-0063` were narrowed again. The retained - 2026-06-17: `DEBT-0058`/`DEBT-0063` were narrowed again. The retained
usermanual, changelog, and about openers in `src/app_dialogs_info_openers.cpp` usermanual, changelog, about, What's New, and shortcuts openers in
now delegate retained dialog construction and overlay close wiring through `src/app_dialogs_info_openers.cpp` now delegate retained dialog construction,
overlay close wiring, and remote-page/button ownership through
`src/legacy_info_dialog_services.*` with explicit `App&` plus overlay-anchor `src/legacy_info_dialog_services.*` with explicit `App&` plus overlay-anchor
dependencies, so the app dialog shell no longer owns that info-dialog family dependencies, so the app dialog shell no longer owns that info-dialog family
inline while `open_whatsnew_dialog()` still owns the retained whats-new flow
inline. inline.
- 2026-06-17: `DEBT-0058`/`DEBT-0063` were narrowed again. The retained New - 2026-06-17: `DEBT-0058`/`DEBT-0063` were narrowed again. The retained New
Document and Save dialog openers in `src/app_dialogs_workflow.cpp` now Document and Save dialog openers in `src/app_dialogs_workflow.cpp` now
delegate retained dialog construction and button wiring through delegate retained dialog construction and button wiring through
`src/legacy_document_session_services.*` with explicit `App&` ownership, so `src/legacy_document_session_services.*` with explicit `App&` ownership, so
the app dialog shell no longer owns that document-session dialog family the app dialog shell no longer owns that document-session dialog family
inline while browse, open, and resize flows still remain there. inline.
- 2026-06-17: `DEBT-0058`/`DEBT-0063` were narrowed again. The retained Open,
Browse, and Resize dialog openers in `src/app_dialogs_workflow.cpp` now
delegate retained dialog construction and overlay/button wiring through
`src/legacy_document_open_services.*` and
`src/legacy_document_session_services.*`, so the app dialog shell no longer
owns those workflow dialog families inline.
- 2026-06-17: `DEBT-0035` was narrowed again. `App::title_update()` and the - 2026-06-17: `DEBT-0035` was narrowed again. `App::title_update()` and the
draw-toolbar/stroke-popup binding families now delegate through draw-toolbar/stroke-popup/grid-popup binding families now delegate through
`src/legacy_app_status_services.*`, `src/legacy_app_status_services.*`,
`src/legacy_draw_toolbar_binding_services.*`, and `src/legacy_draw_toolbar_binding_services.*`, and
`src/legacy_sidebar_stroke_popup_services.*`, so `src/legacy_sidebar_stroke_popup_services.*`, and
`src/legacy_sidebar_grid_popup_services.*`, so
`src/app_layout.cpp`, `src/app_layout_draw_toolbar.cpp`, and part of `src/app_layout.cpp`, `src/app_layout_draw_toolbar.cpp`, and part of
`src/app_layout_sidebar.cpp` are thinner adapters while retained app-shell `src/app_layout_sidebar.cpp` are thinner adapters while retained app-shell
execution and remaining popup families still stay open under the same debt. execution and the remaining layer popup family still stay open under the same
debt.
- 2026-06-17: `DEBT-0030`/`DEBT-0043`/`DEBT-0044` were narrowed again.
`src/app_dialogs_export.cpp` is now a thin forwarding adapter. Retained
document export start/branching flows now live in
`src/legacy_document_export_services.*`, and the PPBR export dialog opener
now lives in `src/legacy_brush_package_export_services.*`; retained export
execution still remains behind those legacy bridges.
- 2026-06-15: `DEBT-0036` was narrowed again. `NodeStrokePreview` now drops the - 2026-06-15: `DEBT-0036` was narrowed again. `NodeStrokePreview` now drops the
retained pass-sequence, mix-execution, final-composite-request, background retained pass-sequence, mix-execution, final-composite-request, background
capture, and preview-copy wrapper structs/functions in favor of direct capture, and preview-copy wrapper structs/functions in favor of direct

View File

@@ -93,12 +93,13 @@ Current conclusion:
app shell is down to adapter calls even though runtime draw/event/sidebar app shell is down to adapter calls even though runtime draw/event/sidebar
execution still remains. execution still remains.
- New-document/save dialog session wiring, app-title rendering, draw-toolbar - New-document/save dialog session wiring, app-title rendering, draw-toolbar
binding, sidebar stroke-popup binding, and usermanual/changelog/about opener binding, sidebar stroke/grid-popup binding, export-dialog start wiring, and
wiring now live in dedicated `legacy_*services.*` seams, so the full info-opener family now live in dedicated `legacy_*services.*` seams,
`src/app_dialogs_workflow.cpp`, `src/app_layout.cpp`, so `src/app_dialogs_workflow.cpp`, `src/app_dialogs_export.cpp`,
`src/app_layout_draw_toolbar.cpp`, `src/app_layout_sidebar.cpp`, and `src/app_layout.cpp`, `src/app_layout_draw_toolbar.cpp`,
`src/app_dialogs_info_openers.cpp` are thinner adapters even though broader `src/app_layout_sidebar.cpp`, and `src/app_dialogs_info_openers.cpp` are
retained dialog/sidebar execution still remains. thinner adapters even though broader retained dialog/sidebar execution still
remains.
- Platform extraction improved substantially and the root app source group no - Platform extraction improved substantially and the root app source group no
longer compiles Web platform sources directly, but broader CMake and longer compiles Web platform sources directly, but broader CMake and
entrypoint cleanup are not complete. entrypoint cleanup are not complete.

View File

@@ -65,6 +65,12 @@ Key facts:
- `App::dialog_browse()` no longer owns browse-dialog button wiring inline; the - `App::dialog_browse()` no longer owns browse-dialog button wiring inline; the
retained document-open bridge now owns that handoff in retained document-open bridge now owns that handoff in
`src/legacy_document_open_services.*`. `src/legacy_document_open_services.*`.
- `App::dialog_open()`, `App::dialog_browse()`, and `App::dialog_resize()` now
delegate retained dialog construction and overlay/button wiring through
`src/legacy_document_open_services.*` and
`src/legacy_document_session_services.*`, so
`src/app_dialogs_workflow.cpp` is thinner while the save-before-workflow
policy seam remains local.
- `App::init_toolbar_main()` now delegates retained main-toolbar button wiring - `App::init_toolbar_main()` now delegates retained main-toolbar button wiring
through `src/legacy_main_toolbar_binding_services.*`, so through `src/legacy_main_toolbar_binding_services.*`, so
`src/app_layout_main_toolbar.cpp` is down to a thin root lookup and adapter `src/app_layout_main_toolbar.cpp` is down to a thin root lookup and adapter
@@ -104,13 +110,13 @@ Key facts:
- `App::dialog_usermanual()`, `App::dialog_changelog()`, and - `App::dialog_usermanual()`, `App::dialog_changelog()`, and
`App::dialog_about()` now delegate the retained info-dialog construction and `App::dialog_about()` now delegate the retained info-dialog construction and
overlay close wiring through `src/legacy_info_dialog_services.*` with overlay close wiring through `src/legacy_info_dialog_services.*` with
explicit `App&` plus overlay-anchor dependencies, so explicit `App&` plus overlay-anchor dependencies, and the remaining
`src/app_dialogs_info_openers.cpp` is thinner while `open_whatsnew_dialog()` What's New plus shortcuts flows now route through the same seam, so
still owns the retained whats-new flow inline. `src/app_dialogs_info_openers.cpp` is down to thin forwarding only.
- `App::dialog_newdoc()` and `App::dialog_save()` now delegate retained dialog - `App::dialog_newdoc()` and `App::dialog_save()` now delegate retained dialog
construction and button wiring through `src/legacy_document_session_services.*`, construction and button wiring through
so `src/app_dialogs_workflow.cpp` is thinner at the document-session seam `src/legacy_document_session_services.*`, so
while browse, open, and resize retained workflows still remain there. `src/app_dialogs_workflow.cpp` is thinner at the document-session seam.
- `App::title_update()` now delegates retained document-title and DPI-label - `App::title_update()` now delegates retained document-title and DPI-label
rendering through `src/legacy_app_status_services.*`, so rendering through `src/legacy_app_status_services.*`, so
`src/app_layout.cpp` no longer owns that app-status family inline. `src/app_layout.cpp` no longer owns that app-status family inline.
@@ -122,8 +128,16 @@ Key facts:
- `App::init_sidebar()` now delegates the retained stroke-popup open/anchor/tick - `App::init_sidebar()` now delegates the retained stroke-popup open/anchor/tick
wiring through `src/legacy_sidebar_stroke_popup_services.*` with explicit wiring through `src/legacy_sidebar_stroke_popup_services.*` with explicit
`App&`, popup-root, trigger-button, and panel dependencies, so `App&`, popup-root, trigger-button, and panel dependencies, so
`src/app_layout_sidebar.cpp` is thinner while the retained grid and layer `src/app_layout_sidebar.cpp` is thinner while the retained layer popup family
popup families still remain inline. still remains inline.
- `App::init_sidebar()` now delegates the retained grid-popup
open/anchor/tick/close wiring through
`src/legacy_sidebar_grid_popup_services.*`, so the `btn-grids-panel` path in
`src/app_layout_sidebar.cpp` is down to a thin adapter.
- `src/app_dialogs_export.cpp` is now a forwarding adapter; the retained
document export start/branching flows live in
`src/legacy_document_export_services.*`, and the PPBR dialog opener now lives
in `src/legacy_brush_package_export_services.*`.
## Parallel Assignment Rules ## Parallel Assignment Rules

View File

@@ -1,316 +1,51 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "app_core/app_dialog.h"
#include "app_core/document_export.h"
#include "legacy_brush_package_export_services.h" #include "legacy_brush_package_export_services.h"
#include "legacy_document_export_services.h" #include "legacy_document_export_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_dialog_export_ppbr.h"
#include <codec_api.h>
#define MP4V2_NO_STDINT_DEFS
#include <mp4v2/mp4v2.h>
#include <utility> #include <utility>
namespace pp::panopainter { namespace pp::panopainter {
namespace {
[[nodiscard]] bool can_start_document_export(App& app, bool requires_license)
{
const auto decision = pp::app::plan_document_export_start(
requires_license,
!requires_license || app.check_license(),
app.canvas != nullptr);
switch (decision) {
case pp::app::DocumentExportStartDecision::start_now:
return true;
case pp::app::DocumentExportStartDecision::show_license_disabled:
{
const auto plan = pp::app::plan_document_export_license_disabled_dialog();
app.message_box(plan.title, plan.message, plan.show_cancel);
return false;
}
case pp::app::DocumentExportStartDecision::unavailable_no_canvas:
return false;
}
return false;
}
void start_document_export_collection(
App& app,
pp::app::DocumentExportCollectionKind kind)
{
const auto plan = pp::app::plan_document_export_collection_target(
kind,
app.uses_work_directory_document_export_collections());
const auto success_kind = pp::app::document_export_collection_success_kind(kind);
if (plan.destination == pp::app::DocumentExportCollectionDestination::work_directory_collection) {
const auto target = pp::app::make_document_export_collection_target(
app.work_path,
app.doc_name,
plan.suffix);
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(success_kind, target.status().message);
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
const auto status = pp::panopainter::execute_legacy_document_export_collection(
app,
plan.kind,
target.value());
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(
kind == pp::app::DocumentExportCollectionKind::layers
? pp::app::DocumentExportExecutionKind::layers_collection
: pp::app::DocumentExportExecutionKind::animation_frames_collection),
status.message);
return;
}
app.pick_dir([
&app,
kind = plan.kind,
success_kind
](std::string path) {
const auto target = pp::app::make_document_export_stem_target(path, app.doc_name);
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(success_kind, target.status().message);
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
const auto status = pp::panopainter::execute_legacy_document_export_stem(
app,
kind,
target.value());
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(
kind == pp::app::DocumentExportCollectionKind::layers
? pp::app::DocumentExportExecutionKind::layers_stem
: pp::app::DocumentExportExecutionKind::animation_frames_stem),
status.message);
});
}
void start_document_video_export(
App& app,
pp::app::DocumentVideoExportKind kind,
pp::app::DocumentExportSuccessKind success_kind,
const char* suffix,
pp::app::DocumentExportExecutionKind execution_kind)
{
if (!can_start_document_export(app, false))
return;
if (app.uses_prepared_file_writes())
{
const auto target = pp::app::make_document_export_suggested_name(app.doc_name, suffix);
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(
success_kind,
target.status().message);
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
app.pick_file_save("mp4", target.value().name,
[&app, kind, execution_kind](std::string path) {
const auto status = pp::panopainter::execute_legacy_document_video_export(
app,
kind,
path,
false);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(execution_kind),
status.message);
},
[](const std::string& path, bool saved) {
(void)path;
(void)saved;
}
);
return;
}
app.pick_file_save({ "mp4" }, [&app, kind, execution_kind](std::string path) {
const auto status = pp::panopainter::execute_legacy_document_video_export(
app,
kind,
path,
true);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(execution_kind),
status.message);
});
}
} // namespace
void open_document_export_dialog(App& app, std::string ext) void open_document_export_dialog(App& app, std::string ext)
{ {
if (!can_start_document_export(app, true)) open_legacy_document_export_dialog(app, std::move(ext));
return;
// TODO: use picker
const auto target = pp::app::make_document_export_file_target(app.work_path, app.doc_name, ext);
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(
pp::app::DocumentExportSuccessKind::equirectangular,
target.status().message);
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
const auto status = pp::panopainter::execute_legacy_document_export_file(app, target.value());
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(
pp::app::DocumentExportExecutionKind::equirectangular_file),
status.message);
} }
void open_document_export_layers_dialog(App& app) void open_document_export_layers_dialog(App& app)
{ {
if (!can_start_document_export(app, true)) open_legacy_document_export_layers_dialog(app);
return;
start_document_export_collection(
app,
pp::app::DocumentExportCollectionKind::layers);
} }
void open_document_export_anim_frames_dialog(App& app) void open_document_export_anim_frames_dialog(App& app)
{ {
if (!can_start_document_export(app, true)) open_legacy_document_export_anim_frames_dialog(app);
return;
start_document_export_collection(
app,
pp::app::DocumentExportCollectionKind::animation_frames);
} }
void open_document_export_depth_dialog(App& app) void open_document_export_depth_dialog(App& app)
{ {
if (!can_start_document_export(app, true)) open_legacy_document_export_depth_dialog(app);
return;
const auto status = pp::panopainter::execute_legacy_document_export_depth(app, app.doc_name);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::depth),
status.message);
} }
void open_document_export_cube_faces_dialog(App& app) void open_document_export_cube_faces_dialog(App& app)
{ {
if (!can_start_document_export(app, false)) open_legacy_document_export_cube_faces_dialog(app);
return;
const auto status = pp::panopainter::execute_legacy_document_export_cube_faces(app, app.doc_name);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::cube_faces),
status.message);
}
void open_document_timelapse_export_dialog(App& app)
{
start_document_video_export(
app,
pp::app::DocumentVideoExportKind::timelapse,
pp::app::DocumentExportSuccessKind::timelapse,
"-timelapse",
pp::app::DocumentExportExecutionKind::timelapse);
}
void open_document_export_mp4_dialog(App& app)
{
start_document_video_export(
app,
pp::app::DocumentVideoExportKind::animation_mp4,
pp::app::DocumentExportSuccessKind::animation_mp4,
"-animation",
pp::app::DocumentExportExecutionKind::animation_mp4);
} }
void open_ppbr_export_dialog(App& app) void open_ppbr_export_dialog(App& app)
{ {
auto* overlay_anchor = app.layout[app.main_id]; open_legacy_ppbr_export_dialog(app);
if (!overlay_anchor) { }
LOG("PPBR export dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogExportPPBR>(app); void open_document_timelapse_export_dialog(App& app)
{
open_legacy_document_timelapse_export_dialog(app);
}
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog); void open_document_export_mp4_dialog(App& app)
if (!overlay) { {
LOG("PPBR export dialog open failed: %s", overlay.status().message); open_legacy_document_export_mp4_dialog(app);
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 = [&app, dialog] (Node*) {
const auto request = pp::panopainter::make_legacy_brush_package_export_request(*dialog);
if (app.uses_prepared_file_writes())
{
app.pick_file_save("ppbr", "exported-brushes",
[&app, dialog, request] (std::string path) {
const auto status = pp::panopainter::execute_legacy_brush_package_export(
app,
*dialog,
request,
path,
pp::panopainter::LegacyBrushPackageExportMode::inline_export_only);
if (!status.ok())
LOG("PPBR export failed: %s", status.message);
},
[dialog] (const std::string& path, bool saved) {
(void)path;
pp::panopainter::complete_legacy_brush_package_export(*dialog, saved);
}
);
return;
}
app.pick_file_save({ "ppbr" }, [&app, dialog, request] (std::string path) {
const auto status = pp::panopainter::execute_legacy_brush_package_export(
app,
*dialog,
request,
path,
pp::panopainter::LegacyBrushPackageExportMode::desktop_async_close_and_message);
if (!status.ok())
LOG("PPBR export failed: %s", status.message);
});
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
} }
} // namespace pp::panopainter } // namespace pp::panopainter

View File

@@ -1,11 +1,6 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "legacy_preference_storage.h"
#include "legacy_ui_overlay_services.h"
#include "legacy_info_dialog_services.h" #include "legacy_info_dialog_services.h"
#include "node_remote_page.h"
#include "node_shorcuts.h"
#include "version.h"
namespace pp::panopainter { namespace pp::panopainter {
@@ -45,70 +40,12 @@ void open_about_dialog(App& app)
void open_whatsnew_dialog(App& app, bool force_show) void open_whatsnew_dialog(App& app, bool force_show)
{ {
auto* overlay_anchor = app.layout[app.main_id]; pp::panopainter::open_legacy_whatsnew_dialog(app, force_show);
if (!overlay_anchor) {
LOG("What's new dialog open failed: main layout anchor is missing");
return;
}
const auto overlay_handle = std::make_shared<pp::ui::NodeHandle>();
const auto open_overlay = [overlay_anchor, overlay_handle](const std::shared_ptr<NodeRemotePage>& page) {
if (overlay_handle->valid()) {
return;
}
const auto overlay = open_legacy_overlay_node_with_handle(*overlay_anchor, page);
if (!overlay) {
return;
}
*overlay_handle = overlay.value();
};
const auto close_overlay = [overlay_anchor, overlay_handle]() {
if (!overlay_handle->valid()) {
return;
}
const auto close_status = close_legacy_overlay_node(*overlay_anchor, *overlay_handle);
(void)close_status;
*overlay_handle = {};
};
auto whatsnew = std::make_shared<NodeRemotePage>();
whatsnew->set_manager(&app.layout);
whatsnew->init();
std::string url = fmt::format("https://panopainter.com/app-content/whatsnew/?version={}", g_version_build);
whatsnew->load_url(url, [whatsnew, force_show, open_overlay](bool success) {
if (success)
{
int last_id = legacy_whatsnew_id_or(0);
if (force_show || (whatsnew->m_page_id <= g_version_build && whatsnew->m_page_id > last_id))
{
whatsnew->set_title(fmt::format("What's new in version {}", g_version_number));
if (!force_show)
open_overlay(whatsnew);
}
}
});
whatsnew->add_button("Reload", 120, [whatsnew](Node*) {
whatsnew->reload();
});
whatsnew->add_button("Read Later", 120, [whatsnew, close_overlay](Node*) {
clear_legacy_whatsnew_id();
save_legacy_preferences();
close_overlay();
});
whatsnew->add_button("Close", 100, [whatsnew, close_overlay](Node*) {
set_legacy_whatsnew_id(whatsnew->m_page_id);
save_legacy_preferences();
close_overlay();
});
if (force_show)
open_overlay(whatsnew);
} }
void open_shortcuts_dialog(App& app) void open_shortcuts_dialog(App& app)
{ {
(void)add_legacy_overlay_node<NodeShortcuts>(app); pp::panopainter::open_legacy_shortcuts_dialog(app);
} }
} // namespace pp::panopainter } // namespace pp::panopainter

View File

@@ -1,13 +1,7 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "app_core/document_resize.h"
#include "legacy_document_open_services.h" #include "legacy_document_open_services.h"
#include "legacy_document_canvas_services.h"
#include "legacy_document_session_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"
void App::continue_document_workflow_after_optional_save(std::function<void()> action) void App::continue_document_workflow_after_optional_save(std::function<void()> action)
{ {
@@ -34,80 +28,16 @@ void App::dialog_newdoc()
// DEPRECATED // DEPRECATED
void App::dialog_open() void App::dialog_open()
{ {
auto show_dialog = [this] { continue_document_workflow_after_optional_save([this] {
// load thumbnail test pp::panopainter::open_legacy_document_open_dialog(*this);
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() void App::dialog_browse()
{ {
auto show_dialog = [this] { continue_document_workflow_after_optional_save([this] {
auto* overlay_anchor = layout[main_id]; pp::panopainter::open_legacy_document_browse_dialog(*this);
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();
pp::panopainter::wire_legacy_document_browse_dialog_actions(
*this,
dialog,
*overlay_anchor,
overlay_handle);
};
continue_document_workflow_after_optional_save(show_dialog);
} }
void App::dialog_save_ver() void App::dialog_save_ver()
@@ -148,41 +78,5 @@ void App::dialog_save()
void App::dialog_resize() void App::dialog_resize()
{ {
auto* overlay_anchor = layout[main_id]; pp::panopainter::open_legacy_document_resize_dialog(*this);
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

@@ -4,6 +4,7 @@
#include "node_panel_floating.h" #include "node_panel_floating.h"
#include "app_core/brush_ui.h" #include "app_core/brush_ui.h"
#include "app_core/document_layer.h" #include "app_core/document_layer.h"
#include "legacy_sidebar_grid_popup_services.h"
#include "legacy_sidebar_color_popup_services.h" #include "legacy_sidebar_color_popup_services.h"
#include "legacy_sidebar_stroke_popup_services.h" #include "legacy_sidebar_stroke_popup_services.h"
#include "legacy_brush_ui_services.h" #include "legacy_brush_ui_services.h"
@@ -321,49 +322,7 @@ void App::init_sidebar()
if (!popup_root) { if (!popup_root) {
return; return;
} }
auto screen = popup_root->m_size; pp::panopainter::open_legacy_sidebar_grid_popup(*popup_root, *button, *grid);
glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
grid->find("title")->SetVisibility(true);
grid->SetSize(350, YGUndefined);
if (grid->m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(grid->m_parent->m_parent))
{
pp::panopainter::detach_legacy_node_from_parent(*grid);
pp::panopainter::close_legacy_dialog_node(*fp);
}
}
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, grid);
if (!popup_overlay)
{
LOG("Grid popup overlay failed: %s", popup_overlay.status().message);
return;
}
auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root->update();
grid->SetPosition(pos.x - grid->m_size.x / 2.f, pos.y + 16);
grid->SetPositioning(YGPositionTypeAbsolute);
pp::panopainter::activate_legacy_popup_overlay(*grid);
auto scroll = grid->find<NodeScroll>("scroller");
scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 250.f));
grid->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
};
}; };
} }
} }

View File

@@ -9,6 +9,7 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include <utility>
namespace pp::panopainter { namespace pp::panopainter {
namespace { namespace {
@@ -134,4 +135,67 @@ void complete_legacy_brush_package_export(NodeDialogExportPPBR& dialog, bool sav
} }
} }
void open_legacy_ppbr_export_dialog(App& app)
{
auto* overlay_anchor = app.layout[app.main_id];
if (!overlay_anchor) {
LOG("PPBR export dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogExportPPBR>(app);
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("PPBR export 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 = [&app, dialog] (Node*) {
const auto request = pp::panopainter::make_legacy_brush_package_export_request(*dialog);
if (app.uses_prepared_file_writes())
{
app.pick_file_save("ppbr", "exported-brushes",
[&app, dialog, request] (std::string path) {
const auto status = pp::panopainter::execute_legacy_brush_package_export(
app,
*dialog,
request,
path,
pp::panopainter::LegacyBrushPackageExportMode::inline_export_only);
if (!status.ok())
LOG("PPBR export failed: %s", status.message);
},
[dialog] (const std::string& path, bool saved) {
(void)path;
pp::panopainter::complete_legacy_brush_package_export(*dialog, saved);
}
);
return;
}
app.pick_file_save({ "ppbr" }, [&app, dialog, request] (std::string path) {
const auto status = pp::panopainter::execute_legacy_brush_package_export(
app,
*dialog,
request,
path,
pp::panopainter::LegacyBrushPackageExportMode::desktop_async_close_and_message);
if (!status.ok())
LOG("PPBR export failed: %s", status.message);
});
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
}
} // namespace pp::panopainter } // namespace pp::panopainter

View File

@@ -26,5 +26,6 @@ enum class LegacyBrushPackageExportMode {
LegacyBrushPackageExportMode mode); LegacyBrushPackageExportMode mode);
void complete_legacy_brush_package_export(NodeDialogExportPPBR& dialog, bool saved); void complete_legacy_brush_package_export(NodeDialogExportPPBR& dialog, bool saved);
void open_legacy_ppbr_export_dialog(App& app);
} // namespace pp::panopainter } // namespace pp::panopainter

View File

@@ -13,10 +13,34 @@
#include <limits> #include <limits>
#include <span> #include <span>
#include <string> #include <string>
#include <utility>
namespace pp::panopainter { namespace pp::panopainter {
namespace { namespace {
[[nodiscard]] bool can_start_legacy_document_export(App& app, bool requires_license)
{
const auto decision = pp::app::plan_document_export_start(
requires_license,
!requires_license || app.check_license(),
app.canvas != nullptr);
switch (decision) {
case pp::app::DocumentExportStartDecision::start_now:
return true;
case pp::app::DocumentExportStartDecision::show_license_disabled:
{
const auto plan = pp::app::plan_document_export_license_disabled_dialog();
app.message_box(plan.title, plan.message, plan.show_cancel);
return false;
}
case pp::app::DocumentExportStartDecision::unavailable_no_canvas:
return false;
}
return false;
}
void show_export_success_dialog( void show_export_success_dialog(
App& app, App& app,
const pp::app::DocumentExportSuccessDialogPlan& plan) const pp::app::DocumentExportSuccessDialogPlan& plan)
@@ -26,6 +50,124 @@ void show_export_success_dialog(
} }
} }
void start_legacy_document_export_collection(
App& app,
pp::app::DocumentExportCollectionKind kind)
{
const auto plan = pp::app::plan_document_export_collection_target(
kind,
app.uses_work_directory_document_export_collections());
const auto success_kind = pp::app::document_export_collection_success_kind(kind);
if (plan.destination == pp::app::DocumentExportCollectionDestination::work_directory_collection) {
const auto target = pp::app::make_document_export_collection_target(
app.work_path,
app.doc_name,
plan.suffix);
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(success_kind, target.status().message);
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
const auto status = pp::panopainter::execute_legacy_document_export_collection(
app,
plan.kind,
target.value());
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(
kind == pp::app::DocumentExportCollectionKind::layers
? pp::app::DocumentExportExecutionKind::layers_collection
: pp::app::DocumentExportExecutionKind::animation_frames_collection),
status.message);
return;
}
app.pick_dir([
&app,
kind = plan.kind,
success_kind
](std::string path) {
const auto target = pp::app::make_document_export_stem_target(path, app.doc_name);
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(success_kind, target.status().message);
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
const auto status = pp::panopainter::execute_legacy_document_export_stem(
app,
kind,
target.value());
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(
kind == pp::app::DocumentExportCollectionKind::layers
? pp::app::DocumentExportExecutionKind::layers_stem
: pp::app::DocumentExportExecutionKind::animation_frames_stem),
status.message);
});
}
void start_legacy_document_video_export(
App& app,
pp::app::DocumentVideoExportKind kind,
pp::app::DocumentExportSuccessKind success_kind,
const char* suffix,
pp::app::DocumentExportExecutionKind execution_kind)
{
if (!can_start_legacy_document_export(app, false))
return;
if (app.uses_prepared_file_writes())
{
const auto target = pp::app::make_document_export_suggested_name(app.doc_name, suffix);
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(
success_kind,
target.status().message);
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
app.pick_file_save("mp4", target.value().name,
[&app, kind, execution_kind](std::string path) {
const auto status = pp::panopainter::execute_legacy_document_video_export(
app,
kind,
path,
false);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(execution_kind),
status.message);
},
[](const std::string& path, bool saved) {
(void)path;
(void)saved;
}
);
return;
}
app.pick_file_save({ "mp4" }, [&app, kind, execution_kind](std::string path) {
const auto status = pp::panopainter::execute_legacy_document_video_export(
app,
kind,
path,
true);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(execution_kind),
status.message);
});
}
struct LegacyDocumentExportSnapshotReports { struct LegacyDocumentExportSnapshotReports {
pp::app::DocumentCanvasSnapshotResult snapshot; pp::app::DocumentCanvasSnapshotResult snapshot;
pp::paint_renderer::DocumentFrameFacePngExportResult face_pngs; pp::paint_renderer::DocumentFrameFacePngExportResult face_pngs;
@@ -834,6 +976,96 @@ void execute_legacy_document_export_equirectangular_thread(std::string file_path
execute_legacy_document_export_equirectangular_thread_impl(std::move(file_path)); execute_legacy_document_export_equirectangular_thread_impl(std::move(file_path));
} }
void open_legacy_document_export_dialog(App& app, std::string ext)
{
if (!can_start_legacy_document_export(app, true))
return;
// TODO: use picker
const auto target = pp::app::make_document_export_file_target(app.work_path, app.doc_name, ext);
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(
pp::app::DocumentExportSuccessKind::equirectangular,
target.status().message);
app.message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
const auto status = pp::panopainter::execute_legacy_document_export_file(app, target.value());
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(
pp::app::DocumentExportExecutionKind::equirectangular_file),
status.message);
}
void open_legacy_document_export_layers_dialog(App& app)
{
if (!can_start_legacy_document_export(app, true))
return;
start_legacy_document_export_collection(
app,
pp::app::DocumentExportCollectionKind::layers);
}
void open_legacy_document_export_anim_frames_dialog(App& app)
{
if (!can_start_legacy_document_export(app, true))
return;
start_legacy_document_export_collection(
app,
pp::app::DocumentExportCollectionKind::animation_frames);
}
void open_legacy_document_export_depth_dialog(App& app)
{
if (!can_start_legacy_document_export(app, true))
return;
const auto status = pp::panopainter::execute_legacy_document_export_depth(app, app.doc_name);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::depth),
status.message);
}
void open_legacy_document_export_cube_faces_dialog(App& app)
{
if (!can_start_legacy_document_export(app, false))
return;
const auto status = pp::panopainter::execute_legacy_document_export_cube_faces(app, app.doc_name);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::cube_faces),
status.message);
}
void open_legacy_document_timelapse_export_dialog(App& app)
{
start_legacy_document_video_export(
app,
pp::app::DocumentVideoExportKind::timelapse,
pp::app::DocumentExportSuccessKind::timelapse,
"-timelapse",
pp::app::DocumentExportExecutionKind::timelapse);
}
void open_legacy_document_export_mp4_dialog(App& app)
{
start_legacy_document_video_export(
app,
pp::app::DocumentVideoExportKind::animation_mp4,
pp::app::DocumentExportSuccessKind::animation_mp4,
"-animation",
pp::app::DocumentExportExecutionKind::animation_mp4);
}
pp::foundation::Status execute_legacy_document_export_file( pp::foundation::Status execute_legacy_document_export_file(
App& app, App& app,
const pp::app::DocumentExportFileTarget& target) const pp::app::DocumentExportFileTarget& target)

View File

@@ -7,6 +7,7 @@ class App;
#include <functional> #include <functional>
#include <string> #include <string>
#include <string_view>
namespace pp::panopainter { namespace pp::panopainter {
@@ -38,6 +39,14 @@ namespace pp::panopainter {
std::string_view path, std::string_view path,
bool asynchronous); bool asynchronous);
void open_legacy_document_export_dialog(App& app, std::string ext);
void open_legacy_document_export_layers_dialog(App& app);
void open_legacy_document_export_anim_frames_dialog(App& app);
void open_legacy_document_export_depth_dialog(App& app);
void open_legacy_document_export_cube_faces_dialog(App& app);
void open_legacy_document_timelapse_export_dialog(App& app);
void open_legacy_document_export_mp4_dialog(App& app);
void execute_legacy_document_export_equirectangular( void execute_legacy_document_export_equirectangular(
std::string file_path, std::string file_path,
std::function<void()> on_complete); std::function<void()> on_complete);

View File

@@ -9,6 +9,7 @@
#include "legacy_history_services.h" #include "legacy_history_services.h"
#include "legacy_ui_overlay_services.h" #include "legacy_ui_overlay_services.h"
#include "log.h" #include "log.h"
#include "node_dialog_open.h"
#include "node_dialog_browse.h" #include "node_dialog_browse.h"
#include "node_panel_brush.h" #include "node_panel_brush.h"
#include "node_panel_layer.h" #include "node_panel_layer.h"
@@ -112,6 +113,38 @@ void prompt_discard_unsaved_project(App& app, const pp::app::DocumentOpenRoute&
}; };
} }
void open_legacy_document_open_dialog_impl(App& app)
{
auto* overlay_anchor = app.layout[app.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>(app);
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 = [](Node*)
{
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
}
class LegacyDocumentOpenServices final : public pp::app::DocumentOpenServices { class LegacyDocumentOpenServices final : public pp::app::DocumentOpenServices {
public: public:
explicit LegacyDocumentOpenServices(App& app) noexcept explicit LegacyDocumentOpenServices(App& app) noexcept
@@ -181,6 +214,30 @@ void wire_legacy_document_browse_dialog_actions(
}; };
} }
void open_legacy_document_browse_dialog(App& app)
{
auto* overlay_anchor = app.layout[app.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>(app);
dialog->search_paths = app.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;
}
pp::panopainter::wire_legacy_document_browse_dialog_actions(
app,
dialog,
*overlay_anchor,
overlay.value());
}
pp::foundation::Status execute_legacy_document_open_plan( pp::foundation::Status execute_legacy_document_open_plan(
App& app, App& app,
pp::app::DocumentOpenPlanAction action, pp::app::DocumentOpenPlanAction action,
@@ -219,4 +276,9 @@ void execute_legacy_downloaded_project_open(
reconcile_downloaded_project_open(app); reconcile_downloaded_project_open(app);
} }
void open_legacy_document_open_dialog(App& app)
{
open_legacy_document_open_dialog_impl(app);
}
} // namespace pp::panopainter } // namespace pp::panopainter

View File

@@ -10,6 +10,7 @@
class App; class App;
class Node; class Node;
class NodeDialogOpen;
class NodeDialogBrowse; class NodeDialogBrowse;
namespace pp::panopainter { namespace pp::panopainter {
@@ -18,11 +19,15 @@ namespace pp::panopainter {
App& app, App& app,
std::string path); std::string path);
void open_legacy_document_open_dialog(App& app);
[[nodiscard]] pp::foundation::Status execute_legacy_document_open_plan( [[nodiscard]] pp::foundation::Status execute_legacy_document_open_plan(
App& app, App& app,
pp::app::DocumentOpenPlanAction action, pp::app::DocumentOpenPlanAction action,
const pp::app::DocumentOpenRoute& route); const pp::app::DocumentOpenRoute& route);
void open_legacy_document_browse_dialog(App& app);
void wire_legacy_document_browse_dialog_actions( void wire_legacy_document_browse_dialog_actions(
App& app, App& app,
const std::shared_ptr<NodeDialogBrowse>& dialog, const std::shared_ptr<NodeDialogBrowse>& dialog,

View File

@@ -10,6 +10,7 @@
#include "legacy_history_services.h" #include "legacy_history_services.h"
#include "legacy_ui_overlay_services.h" #include "legacy_ui_overlay_services.h"
#include "node_dialog_open.h" #include "node_dialog_open.h"
#include "node_dialog_resize.h"
#include <utility> #include <utility>
@@ -487,6 +488,47 @@ void open_legacy_document_file_save_dialog_impl(App& app)
wire_document_file_save_dialog_buttons(app, dialog); wire_document_file_save_dialog_buttons(app, dialog);
} }
void open_legacy_document_resize_dialog_impl(App& app)
{
auto* overlay_anchor = app.layout[app.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>(app);
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 = [&app, 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(app, plan.value());
if (!status.ok())
LOG("Document resize failed: %s", status.message);
close_dialog();
};
dialog->btn_cancel->on_click = [close_dialog](Node*) {
close_dialog();
};
}
} // namespace } // namespace
pp::foundation::Status execute_legacy_close_request_decision( pp::foundation::Status execute_legacy_close_request_decision(
@@ -561,6 +603,11 @@ void open_legacy_document_file_save_dialog(App& app)
open_legacy_document_file_save_dialog_impl(app); open_legacy_document_file_save_dialog_impl(app);
} }
void open_legacy_document_resize_dialog(App& app)
{
open_legacy_document_resize_dialog_impl(app);
}
pp::foundation::Status execute_legacy_document_version_save( pp::foundation::Status execute_legacy_document_version_save(
App& app, App& app,
const pp::app::DocumentVersionTarget& target) const pp::app::DocumentVersionTarget& target)

View File

@@ -9,6 +9,7 @@
class App; class App;
class NodeDialogNewDoc; class NodeDialogNewDoc;
class NodeDialogSave; class NodeDialogSave;
class NodeDialogResize;
namespace pp::panopainter { namespace pp::panopainter {
@@ -44,6 +45,8 @@ void open_legacy_new_document_dialog(App& app);
void open_legacy_document_file_save_dialog(App& app); void open_legacy_document_file_save_dialog(App& app);
void open_legacy_document_resize_dialog(App& app);
[[nodiscard]] pp::foundation::Status execute_legacy_document_version_save( [[nodiscard]] pp::foundation::Status execute_legacy_document_version_save(
App& app, App& app,
const pp::app::DocumentVersionTarget& target); const pp::app::DocumentVersionTarget& target);

View File

@@ -3,14 +3,27 @@
#include "legacy_info_dialog_services.h" #include "legacy_info_dialog_services.h"
#include "app.h" #include "app.h"
#include "legacy_preference_storage.h"
#include "legacy_ui_overlay_services.h" #include "legacy_ui_overlay_services.h"
#include "node_remote_page.h"
#include "node_shorcuts.h"
#include "node_about.h" #include "node_about.h"
#include "node_changelog.h" #include "node_changelog.h"
#include "node_usermanual.h" #include "node_usermanual.h"
#include "version.h"
namespace pp::panopainter { namespace pp::panopainter {
namespace { namespace {
Node* get_legacy_info_dialog_overlay_anchor(App& app, const char* log_name)
{
auto* overlay_anchor = app.layout[app.main_id];
if (!overlay_anchor) {
LOG("%s dialog open failed: main layout anchor is missing", log_name);
}
return overlay_anchor;
}
template <typename DialogNode> template <typename DialogNode>
void open_legacy_info_dialog(App& app, Node& overlay_anchor, const char* log_name) void open_legacy_info_dialog(App& app, Node& overlay_anchor, const char* log_name)
{ {
@@ -49,4 +62,71 @@ void open_about_dialog(App& app, Node& overlay_anchor, const char* log_name)
open_legacy_info_dialog<NodeAbout>(app, overlay_anchor, log_name); open_legacy_info_dialog<NodeAbout>(app, overlay_anchor, log_name);
} }
void open_legacy_whatsnew_dialog(App& app, bool force_show)
{
auto* overlay_anchor = get_legacy_info_dialog_overlay_anchor(app, "What's new");
if (!overlay_anchor) {
return;
}
const auto overlay_handle = std::make_shared<pp::ui::NodeHandle>();
const auto open_overlay = [overlay_anchor, overlay_handle](const std::shared_ptr<NodeRemotePage>& page) {
if (overlay_handle->valid()) {
return;
}
const auto overlay = open_legacy_overlay_node_with_handle(*overlay_anchor, page);
if (!overlay) {
return;
}
*overlay_handle = overlay.value();
};
const auto close_overlay = [overlay_anchor, overlay_handle]() {
if (!overlay_handle->valid()) {
return;
}
const auto close_status = close_legacy_overlay_node(*overlay_anchor, *overlay_handle);
(void)close_status;
*overlay_handle = {};
};
auto whatsnew = std::make_shared<NodeRemotePage>();
whatsnew->set_manager(&app.layout);
whatsnew->init();
const std::string url = fmt::format("https://panopainter.com/app-content/whatsnew/?version={}", g_version_build);
whatsnew->load_url(url, [whatsnew, force_show, open_overlay](bool success) {
if (success) {
const int last_id = legacy_whatsnew_id_or(0);
if (force_show || (whatsnew->m_page_id <= g_version_build && whatsnew->m_page_id > last_id)) {
whatsnew->set_title(fmt::format("What's new in version {}", g_version_number));
if (!force_show) {
open_overlay(whatsnew);
}
}
}
});
whatsnew->add_button("Reload", 120, [whatsnew](Node*) {
whatsnew->reload();
});
whatsnew->add_button("Read Later", 120, [whatsnew, close_overlay](Node*) {
clear_legacy_whatsnew_id();
save_legacy_preferences();
close_overlay();
});
whatsnew->add_button("Close", 100, [whatsnew, close_overlay](Node*) {
set_legacy_whatsnew_id(whatsnew->m_page_id);
save_legacy_preferences();
close_overlay();
});
if (force_show) {
open_overlay(whatsnew);
}
}
void open_legacy_shortcuts_dialog(App& app)
{
(void)add_legacy_overlay_node<NodeShortcuts>(app);
}
} // namespace pp::panopainter } // namespace pp::panopainter

View File

@@ -8,5 +8,7 @@ namespace pp::panopainter {
void open_usermanual_dialog(App& app, Node& overlay_anchor, const char* log_name); void open_usermanual_dialog(App& app, Node& overlay_anchor, const char* log_name);
void open_changelog_dialog(App& app, Node& overlay_anchor, const char* log_name); void open_changelog_dialog(App& app, Node& overlay_anchor, const char* log_name);
void open_about_dialog(App& app, Node& overlay_anchor, const char* log_name); void open_about_dialog(App& app, Node& overlay_anchor, const char* log_name);
void open_legacy_whatsnew_dialog(App& app, bool force_show);
void open_legacy_shortcuts_dialog(App& app);
} // namespace pp::panopainter } // namespace pp::panopainter

View File

@@ -0,0 +1,73 @@
#include "pch.h"
#include "legacy_sidebar_grid_popup_services.h"
#include "legacy_ui_overlay_services.h"
#include "log.h"
#include "node_button_custom.h"
#include "node_image.h"
#include "node_panel_floating.h"
#include "node_panel_grid.h"
#include "node_scroll.h"
namespace pp::panopainter {
void open_legacy_sidebar_grid_popup(
Node& popup_root,
NodeButtonCustom& trigger_button,
NodePanelGrid& popup_panel)
{
const glm::vec2 pos = trigger_button.m_pos + glm::vec2(trigger_button.m_size.x * 0.5f, trigger_button.m_size.y);
const auto screen = popup_root.m_size;
popup_panel.find("title")->SetVisibility(true);
popup_panel.SetSize(350, YGUndefined);
if (popup_panel.m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(popup_panel.m_parent->m_parent))
{
detach_legacy_node_from_parent(popup_panel);
close_legacy_dialog_node(*fp);
}
}
const auto popup_overlay = open_legacy_overlay_node_with_handle(
popup_root,
std::static_pointer_cast<NodePanelGrid>(popup_panel.shared_from_this()));
if (!popup_overlay)
{
LOG("Grid popup overlay failed: %s", popup_overlay.status().message);
return;
}
auto tick = make_legacy_overlay_node_for_anchor<NodeImage>(popup_root);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
const auto tick_overlay = open_legacy_overlay_node_with_handle(popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root.update();
popup_panel.SetPosition(pos.x - popup_panel.m_size.x / 2.f, pos.y + 16);
popup_panel.SetPositioning(YGPositionTypeAbsolute);
activate_legacy_popup_overlay(popup_panel);
auto scroll = popup_panel.find<NodeScroll>("scroller");
scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 250.f));
popup_panel.on_popup_close = [&popup_root, popup_handle, tick_handle](Node*) {
close_legacy_overlay_handle_ignoring_status(popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(popup_root, tick_handle);
};
}
} // namespace pp::panopainter

View File

@@ -0,0 +1,14 @@
#pragma once
class Node;
class NodeButtonCustom;
class NodePanelGrid;
namespace pp::panopainter {
void open_legacy_sidebar_grid_popup(
Node& popup_root,
NodeButtonCustom& trigger_button,
NodePanelGrid& popup_panel);
} // namespace pp::panopainter