diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index f8e7ce72..776124fd 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,28 @@ agent or engineer to remove them without reconstructing context from chat. ## Reductions +- 2026-06-17: `DEBT-0039` was narrowed again. `App::dialog_browse()` in + `src/app_dialogs_workflow.cpp` now delegates retained browse-dialog button + wiring and selected-path open execution through + `src/legacy_document_open_services.*`, so the app dialog shell no longer + owns that document-open handoff inline while retained dialog creation, + unsaved-project prompting, project-open execution, and title/layer refresh + still remain. +- 2026-06-17: `DEBT-0038` was narrowed again. The retained cloud upload and + download background execution in `src/legacy_cloud_services.cpp` now routes + through `AppRuntime::canvas_async_task` instead of a file-static worker + singleton, while retained prompt/progress lifetime, OpenGL context guards, + thumbnail loading, and transfer execution still remain in the cloud bridge. +- 2026-06-17: `DEBT-0048` was narrowed again. The retained ABR/PPBR import path + in `src/legacy_brush_package_import_services.cpp` now uses + `AppRuntime::canvas_async_task` instead of a file-static worker singleton, + while the legacy preset panel remains the importer/storage owner and broader + brush asset/UI ownership transfer still remains open. +- 2026-06-17: `DEBT-0044` was narrowed again. The retained asynchronous + timelapse export path in `src/legacy_document_export_services.cpp` now routes + through `AppRuntime::canvas_async_task` instead of a file-static worker + singleton, while retained `App::rec_export`, animation MP4 execution, and + mobile/Web completion paths still remain in the video-export bridge. - 2026-06-17: `DEBT-0036` was narrowed again. `src/canvas_layer.cpp` now routes retained `Layer` / `LayerFrame` render queueing through `src/renderer_gl/render_runtime_dispatch.h` instead of direct diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 7c9255e2..8d8789d6 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -79,6 +79,10 @@ Current conclusion: - `AppRuntime` now owns synchronized running flags and explicit same-thread/post-reject queue behavior, but broader app/runtime singleton reach and retained shell ownership still remain. +- The document-browse dialog handoff now lives in + `src/legacy_document_open_services.*`, and retained cloud upload/download, + brush-package import, and timelapse-export async paths now use + `AppRuntime::canvas_async_task` instead of file-static worker singletons. - Platform extraction improved substantially and the root app source group no longer compiles Web platform sources directly, but broader CMake and entrypoint cleanup are not complete. diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index aad5b2b9..c16c8544 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -59,6 +59,12 @@ Key facts: - `AppRuntime` now owns synchronized running flags plus explicit post/reject, same-thread execution, and queue-drain behavior, but broader singleton reach and app-shell ownership remain. +- Retained cloud upload/download, brush-package import, and timelapse-export + async paths now route through `AppRuntime::canvas_async_task`, but dialog and + execution ownership still remains in retained app/document/cloud bridges. +- `App::dialog_browse()` no longer owns browse-dialog button wiring inline; the + retained document-open bridge now owns that handoff in + `src/legacy_document_open_services.*`. ## Parallel Assignment Rules diff --git a/src/app_dialogs_workflow.cpp b/src/app_dialogs_workflow.cpp index 5f87ffe5..0ce9c496 100644 --- a/src/app_dialogs_workflow.cpp +++ b/src/app_dialogs_workflow.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "app.h" #include "app_core/document_resize.h" +#include "legacy_document_open_services.h" #include "legacy_document_canvas_services.h" #include "legacy_document_session_services.h" #include "legacy_ui_overlay_services.h" @@ -10,32 +11,6 @@ namespace { -void wire_document_browse_dialog_actions( - App& app, - const std::shared_ptr& 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& dialog, @@ -201,7 +176,11 @@ void App::dialog_browse() return; } const auto overlay_handle = overlay.value(); - wire_document_browse_dialog_actions(*this, dialog, *overlay_anchor, overlay_handle); + pp::panopainter::wire_legacy_document_browse_dialog_actions( + *this, + dialog, + *overlay_anchor, + overlay_handle); }; continue_document_workflow_after_optional_save(show_dialog); diff --git a/src/legacy_brush_package_import_services.cpp b/src/legacy_brush_package_import_services.cpp index afcdf600..86f487d3 100644 --- a/src/legacy_brush_package_import_services.cpp +++ b/src/legacy_brush_package_import_services.cpp @@ -5,88 +5,32 @@ #include "app.h" #include "node_panel_brush.h" -#include -#include #include #include -#include -#include -#include namespace pp::panopainter { namespace { -class LegacyBrushPackageWorker final { -public: - LegacyBrushPackageWorker() - : worker_([this](std::stop_token stop_token) { - run(stop_token); - }) - { - } - - ~LegacyBrushPackageWorker() - { - shutdown(); - } - - void post(std::function task) - { - { - std::lock_guard lock(mutex_); - if (stopping_) - return; - tasks_.push_back(std::move(task)); - } - cv_.notify_one(); - } - -private: - void shutdown() - { - { - std::lock_guard lock(mutex_); - stopping_ = true; - } - cv_.notify_all(); - } - - void run(std::stop_token stop_token) - { - for (;;) { - std::function task; - { - std::unique_lock lock(mutex_); - cv_.wait(lock, [&] { - return stopping_ || stop_token.stop_requested() || !tasks_.empty(); - }); - if ((stopping_ || stop_token.stop_requested()) && tasks_.empty()) - break; - task = std::move(tasks_.front()); - tasks_.pop_front(); - } - - if (task) { - try { - task(); - } catch (...) { - LOG("brush package import worker task failed"); - } - } - } - } - - std::mutex mutex_; - std::condition_variable cv_; - std::deque> tasks_; - bool stopping_ = false; - std::jthread worker_; -}; - -LegacyBrushPackageWorker& brush_package_worker() +void queue_legacy_brush_package_import_job( + App& app, + std::shared_ptr presets, + pp::app::BrushPackageImportKind kind, + std::string path_string) { - static LegacyBrushPackageWorker worker; - return worker; + app.runtime().canvas_async_task([presets = std::move(presets), + kind, + path_string = std::move(path_string)]() mutable { + BT_SetTerminate(); + if (!presets) { + return; + } + if (kind == pp::app::BrushPackageImportKind::abr) { + presets->import_abr(path_string); + return; + } + + presets->import_ppbr(path_string); + }); } class LegacyBrushPackageImportServices final : public pp::app::BrushPackageImportServices { @@ -98,20 +42,8 @@ public: void import_brush_package(pp::app::BrushPackageImportKind kind, std::string_view path) override { - auto presets = app_.presets; const auto path_string = std::string(path); - brush_package_worker().post([presets, kind, path_string] { - BT_SetTerminate(); - if (!presets) { - return; - } - if (kind == pp::app::BrushPackageImportKind::abr) { - presets->import_abr(path_string); - return; - } - - presets->import_ppbr(path_string); - }); + queue_legacy_brush_package_import_job(app_, app_.presets, kind, std::move(path_string)); } private: diff --git a/src/legacy_cloud_services.cpp b/src/legacy_cloud_services.cpp index 0c5cfe17..44a4b3d3 100644 --- a/src/legacy_cloud_services.cpp +++ b/src/legacy_cloud_services.cpp @@ -21,79 +21,6 @@ pp::foundation::Status execute_legacy_cloud_download_selection_action( namespace { -class LegacyCloudWorker final { -public: - LegacyCloudWorker() - : worker_([this](std::stop_token stop_token) { - run(stop_token); - }) - { - } - - ~LegacyCloudWorker() - { - shutdown(); - } - - void post(std::function task) - { - { - std::lock_guard lock(mutex_); - if (stopping_) - return; - tasks_.push_back(std::move(task)); - } - cv_.notify_one(); - } - -private: - void shutdown() - { - { - std::lock_guard lock(mutex_); - stopping_ = true; - } - cv_.notify_all(); - } - - void run(std::stop_token stop_token) - { - for (;;) { - std::function task; - { - std::unique_lock lock(mutex_); - cv_.wait(lock, [&] { - return stopping_ || stop_token.stop_requested() || !tasks_.empty(); - }); - if ((stopping_ || stop_token.stop_requested()) && tasks_.empty()) - break; - task = std::move(tasks_.front()); - tasks_.pop_front(); - } - - if (task) { - try { - task(); - } catch (...) { - LOG("cloud worker task failed"); - } - } - } - } - - std::mutex mutex_; - std::condition_variable cv_; - std::deque> tasks_; - bool stopping_ = false; - std::jthread worker_; -}; - -LegacyCloudWorker& cloud_worker() -{ - static LegacyCloudWorker worker; - return worker; -} - #if WITH_CURL int progress_callback_download(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) @@ -330,7 +257,7 @@ void setup_cloud_publish_prompt(App& app, bool save_before_upload) const auto prompt_plan = pp::app::plan_cloud_publish_prompt(); auto dialog = app.message_box(prompt_plan.title, prompt_plan.message, prompt_plan.show_cancel); wire_cloud_publish_prompt_buttons(dialog, [&app, save_before_upload] { - queue_legacy_cloud_worker_task([app = &app, save_before_upload] { + app.runtime().canvas_async_task([app = &app, save_before_upload] { execute_cloud_publish_worker(*app, save_before_upload); }); }); @@ -438,7 +365,7 @@ public: void start_download(const pp::app::CloudDownloadRequest& request) override { - queue_legacy_cloud_worker_task([app = &app_, request] { + app_.runtime().canvas_async_task([app = &app_, request] { execute_cloud_download_thread(*app, request); }); } @@ -458,7 +385,11 @@ void show_cloud_save_required_warning(App& app) void queue_legacy_cloud_worker_task(std::function task) { - cloud_worker().post(std::move(task)); + if (App::I == nullptr) { + LOG("cloud worker task skipped: app unavailable"); + return; + } + App::I->runtime().canvas_async_task(std::move(task)); } pp::foundation::Status execute_legacy_cloud_upload_plan( diff --git a/src/legacy_document_export_services.cpp b/src/legacy_document_export_services.cpp index f3a541dc..c4b9b70e 100644 --- a/src/legacy_document_export_services.cpp +++ b/src/legacy_document_export_services.cpp @@ -8,16 +8,11 @@ #include "paint_renderer/compositor.h" #include -#include #include -#include #include -#include #include #include #include -#include -#include namespace pp::panopainter { namespace { @@ -36,79 +31,6 @@ struct LegacyDocumentExportSnapshotReports { pp::paint_renderer::DocumentFrameFacePngExportResult face_pngs; }; -class LegacyDocumentVideoExportWorker final { -public: - LegacyDocumentVideoExportWorker() - : worker_([this](std::stop_token stop_token) { - run(stop_token); - }) - { - } - - ~LegacyDocumentVideoExportWorker() - { - shutdown(); - } - - void post(std::function task) - { - { - std::lock_guard lock(mutex_); - if (stopping_) - return; - tasks_.push_back(std::move(task)); - } - cv_.notify_one(); - } - -private: - void shutdown() - { - { - std::lock_guard lock(mutex_); - stopping_ = true; - } - cv_.notify_all(); - } - - void run(std::stop_token stop_token) - { - for (;;) { - std::function task; - { - std::unique_lock lock(mutex_); - cv_.wait(lock, [&] { - return stopping_ || stop_token.stop_requested() || !tasks_.empty(); - }); - if ((stopping_ || stop_token.stop_requested()) && tasks_.empty()) - break; - task = std::move(tasks_.front()); - tasks_.pop_front(); - } - - if (task) { - try { - task(); - } catch (...) { - LOG("document video export worker task failed"); - } - } - } - } - - std::mutex mutex_; - std::condition_variable cv_; - std::deque> tasks_; - bool stopping_ = false; - std::jthread worker_; -}; - -LegacyDocumentVideoExportWorker& document_video_export_worker() -{ - static LegacyDocumentVideoExportWorker worker; - return worker; -} - pp::foundation::Status write_export_binary_file(std::string_view path, std::span bytes) { if (path.empty()) { @@ -846,7 +768,7 @@ public: auto* app = &app_; auto path_string = std::string(path); if (asynchronous_) { - document_video_export_worker().post([app, path_string = std::move(path_string)]() mutable { + app_.runtime().canvas_async_task([app, path_string = std::move(path_string)]() mutable { BT_SetTerminate(); app->rec_export(path_string); app->ui_task([app, path_string = std::move(path_string)]() mutable { diff --git a/src/legacy_document_open_services.cpp b/src/legacy_document_open_services.cpp index 85594c7e..0a4888ba 100644 --- a/src/legacy_document_open_services.cpp +++ b/src/legacy_document_open_services.cpp @@ -9,6 +9,7 @@ #include "legacy_history_services.h" #include "legacy_ui_overlay_services.h" #include "log.h" +#include "node_dialog_browse.h" #include "node_panel_brush.h" #include "node_panel_layer.h" @@ -154,6 +155,32 @@ private: } // namespace +void wire_legacy_document_browse_dialog_actions( + App& app, + const std::shared_ptr& 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(); + }; +} + pp::foundation::Status execute_legacy_document_open_plan( App& app, pp::app::DocumentOpenPlanAction action, diff --git a/src/legacy_document_open_services.h b/src/legacy_document_open_services.h index 88f1674e..6f0fc5d9 100644 --- a/src/legacy_document_open_services.h +++ b/src/legacy_document_open_services.h @@ -2,11 +2,15 @@ #include "app_core/document_session.h" #include "foundation/result.h" +#include "ui_core/overlay_lifetime.h" #include #include +#include class App; +class Node; +class NodeDialogBrowse; namespace pp::panopainter { @@ -19,6 +23,12 @@ namespace pp::panopainter { pp::app::DocumentOpenPlanAction action, const pp::app::DocumentOpenRoute& route); +void wire_legacy_document_browse_dialog_actions( + App& app, + const std::shared_ptr& dialog, + Node& overlay_anchor, + pp::ui::NodeHandle overlay_handle); + void execute_legacy_downloaded_project_open( App& app, std::string_view path,