Plan app export targets in app core

This commit is contained in:
2026-06-02 22:50:42 +02:00
parent 5841878df9
commit b349f24931
11 changed files with 434 additions and 24 deletions

View File

@@ -0,0 +1 @@
#include "app_core/document_export.h"

View File

@@ -0,0 +1,111 @@
#pragma once
#include "foundation/result.h"
#include <string>
#include <string_view>
#include <utility>
namespace pp::app {
struct DocumentExportFileTarget {
std::string path;
std::string suggested_name;
};
struct DocumentExportCollectionTarget {
std::string directory;
std::string stem_path;
};
struct DocumentExportStemTarget {
std::string stem_path;
};
struct DocumentExportSuggestedName {
std::string name;
};
[[nodiscard]] inline pp::foundation::Result<DocumentExportFileTarget> make_document_export_file_target(
std::string_view work_directory,
std::string_view document_name,
std::string_view extension)
{
if (document_name.empty()) {
return pp::foundation::Result<DocumentExportFileTarget>::failure(
pp::foundation::Status::invalid_argument("document name must not be empty"));
}
if (extension.empty()) {
return pp::foundation::Result<DocumentExportFileTarget>::failure(
pp::foundation::Status::invalid_argument("extension must not be empty"));
}
DocumentExportFileTarget target;
target.suggested_name.reserve(document_name.size() + extension.size());
target.suggested_name += document_name;
target.suggested_name += extension;
target.path.reserve(work_directory.size() + target.suggested_name.size() + 1);
target.path += work_directory;
target.path += "/";
target.path += target.suggested_name;
return pp::foundation::Result<DocumentExportFileTarget>::success(std::move(target));
}
[[nodiscard]] inline pp::foundation::Result<DocumentExportCollectionTarget> make_document_export_collection_target(
std::string_view work_directory,
std::string_view document_name,
std::string_view suffix)
{
if (document_name.empty()) {
return pp::foundation::Result<DocumentExportCollectionTarget>::failure(
pp::foundation::Status::invalid_argument("document name must not be empty"));
}
DocumentExportCollectionTarget target;
target.directory.reserve(work_directory.size() + document_name.size() + suffix.size() + 1);
target.directory += work_directory;
target.directory += "/";
target.directory += document_name;
target.directory += suffix;
target.stem_path.reserve(target.directory.size() + document_name.size() + 1);
target.stem_path += target.directory;
target.stem_path += "/";
target.stem_path += document_name;
return pp::foundation::Result<DocumentExportCollectionTarget>::success(std::move(target));
}
[[nodiscard]] inline pp::foundation::Result<DocumentExportStemTarget> make_document_export_stem_target(
std::string_view directory,
std::string_view document_name)
{
if (document_name.empty()) {
return pp::foundation::Result<DocumentExportStemTarget>::failure(
pp::foundation::Status::invalid_argument("document name must not be empty"));
}
DocumentExportStemTarget target;
target.stem_path.reserve(directory.size() + document_name.size() + 1);
target.stem_path += directory;
target.stem_path += "/";
target.stem_path += document_name;
return pp::foundation::Result<DocumentExportStemTarget>::success(std::move(target));
}
[[nodiscard]] inline pp::foundation::Result<DocumentExportSuggestedName> make_document_export_suggested_name(
std::string_view document_name,
std::string_view suffix)
{
if (document_name.empty()) {
return pp::foundation::Result<DocumentExportSuggestedName>::failure(
pp::foundation::Status::invalid_argument("document name must not be empty"));
}
DocumentExportSuggestedName target;
target.name.reserve(document_name.size() + suffix.size());
target.name += document_name;
target.name += suffix;
return pp::foundation::Result<DocumentExportSuggestedName>::success(std::move(target));
}
}

View File

@@ -1,6 +1,7 @@
#include "pch.h"
#include "app.h"
#include "action.h"
#include "app_core/document_export.h"
#include "settings.h"
#include "node_dialog_open.h"
#include "node_dialog_browse.h"
@@ -421,9 +422,13 @@ void App::dialog_export(std::string ext)
if (canvas)
{
// TODO: use picker
auto path = work_path + "/" + doc_name + ext;
auto name = doc_name + ext;
canvas->m_canvas->export_equirectangular(path, [this, path, name]{
const auto target = pp::app::make_document_export_file_target(work_path, doc_name, ext);
if (!target) {
message_box("Export Equirectangular", target.status().message);
return;
}
canvas->m_canvas->export_equirectangular(target.value().path, [this, target = target.value()]{
#if defined(__IOS__)
message_box("Export Equirectangular", "Image exported to Photos");
#elif defined(__OSX__)
@@ -434,7 +439,7 @@ void App::dialog_export(std::string ext)
//auto result = ovr_Media_ShareToFacebook("Sharing from PanoPainter on Oculus Quest", path.c_str(), ovrMediaContentType_Photo);
#elif __WEB__
ui_task([=]{
webgl_pick_file_save(path, name, [](bool success){ });
webgl_pick_file_save(target.path, target.suggested_name, [](bool success){ });
});
#endif
});
@@ -452,19 +457,28 @@ void App::dialog_export_layers()
if (canvas)
{
#if defined(__IOS__)
auto dir = work_path + "/" + doc_name + "_layers";
if (Asset::create_dir(dir))
const auto target = pp::app::make_document_export_collection_target(work_path, doc_name, "_layers");
if (!target) {
message_box("Export Layers", target.status().message);
return;
}
if (Asset::create_dir(target.value().directory))
{
auto p = dir + "/" + doc_name;
canvas->m_canvas->export_layers(p, [this, p] {
canvas->m_canvas->export_layers(target.value().stem_path, [this] {
message_box("Export Layers", "Image layers exported to Files/PanoPainter");
});
}
#else
pick_dir([this](std::string path) {
auto p = path + "/" + doc_name;
canvas->m_canvas->export_layers(p, [this, p] {
message_box("Export Layers", "Layers exported to: " + p);
const auto target = pp::app::make_document_export_stem_target(path, doc_name);
if (!target) {
message_box("Export Layers", target.status().message);
return;
}
canvas->m_canvas->export_layers(target.value().stem_path, [this, target = target.value()] {
message_box("Export Layers", "Layers exported to: " + target.stem_path);
});
});
#endif
@@ -482,19 +496,28 @@ void App::dialog_export_anim_frames()
if (canvas)
{
#if defined(__IOS__)
auto dir = work_path + "/" + doc_name + "_frames";
if (Asset::create_dir(dir))
const auto target = pp::app::make_document_export_collection_target(work_path, doc_name, "_frames");
if (!target) {
message_box("Export Layers", target.status().message);
return;
}
if (Asset::create_dir(target.value().directory))
{
auto p = dir + "/" + doc_name;
canvas->m_canvas->export_anim_frames(p, [this, p] {
canvas->m_canvas->export_anim_frames(target.value().stem_path, [this] {
message_box("Export Layers", "Image layers exported to Files/PanoPainter");
});
}
#else
pick_dir([this](std::string path) {
auto p = path + "/" + doc_name;
canvas->m_canvas->export_anim_frames(p, [this, p] {
message_box("Export Layers", "Layers exported to: " + p);
const auto target = pp::app::make_document_export_stem_target(path, doc_name);
if (!target) {
message_box("Export Layers", target.status().message);
return;
}
canvas->m_canvas->export_anim_frames(target.value().stem_path, [this, target = target.value()] {
message_box("Export Layers", "Layers exported to: " + target.stem_path);
});
});
#endif
@@ -657,7 +680,13 @@ void App::dialog_ppbr_export()
void App::dialog_timelapse_export()
{
#if __IOS__ || __WEB__
pick_file_save("mp4", doc_name + "-timelapse",
const auto target = pp::app::make_document_export_suggested_name(doc_name, "-timelapse");
if (!target) {
message_box("Export Timelapse", target.status().message);
return;
}
pick_file_save("mp4", target.value().name,
[this](std::string path) {
rec_export(path);
},
@@ -679,7 +708,13 @@ void App::dialog_timelapse_export()
void App::dialog_export_mp4()
{
#if __IOS__ || __WEB__
pick_file_save("mp4", doc_name + "-animation",
const auto target = pp::app::make_document_export_suggested_name(doc_name, "-animation");
if (!target) {
message_box("Export Animation", target.status().message);
return;
}
pick_file_save("mp4", target.value().name,
[this](std::string path) {
export_anim_mp4(path);
},