Files
panopainter/src/app_dialogs.cpp

909 lines
30 KiB
C++

#include "pch.h"
#include "app.h"
#include "app_core/app_dialog.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_brush_package_export_services.h"
#include "legacy_document_export_services.h"
#include "legacy_document_layer_services.h"
#include "legacy_document_session_services.h"
#include "legacy_preference_storage.h"
#include "legacy_ui_overlay_services.h"
#include "node_dialog_open.h"
#include "node_dialog_browse.h"
#include "node_dialog_resize.h"
#include "node_dialog_cloud.h"
#include "node_about.h"
#include "node_changelog.h"
#include "node_usermanual.h"
#include "node_dialog_export_ppbr.h"
#include "node_remote_page.h"
#include "node_shorcuts.h"
#include <codec_api.h>
#define MP4V2_NO_STDINT_DEFS
#include <mp4v2/mp4v2.h>
#include <utility>
#ifdef __QUEST__
#include "oculus_vr.h"
#endif
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 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*/)
{
const auto plan = pp::app::plan_app_progress_dialog(title, total);
const auto dialogs = pp::panopainter::make_legacy_app_dialog_factory(*this);
return pp::panopainter::legacy_progress_dialog_node(dialogs->show_progress_dialog(plan));
}
std::shared_ptr<NodeMessageBox> App::message_box(const std::string &title, const std::string& text, bool cancel_button)
{
const auto plan = pp::app::plan_app_message_dialog(title, text, cancel_button);
const auto dialogs = pp::panopainter::make_legacy_app_dialog_factory(*this);
return pp::panopainter::legacy_message_dialog_node(dialogs->show_message_dialog(plan));
}
std::shared_ptr<NodeInputBox> App::input_box(const std::string& title,
const std::string& field_name, const std::string& ok_caption /*= "Ok"*/)
{
const auto plan_result = pp::app::plan_app_input_dialog(title, field_name, ok_caption);
if (!plan_result) {
LOG("input dialog skipped: %s", plan_result.status().message);
return nullptr;
}
const auto dialogs = pp::panopainter::make_legacy_app_dialog_factory(*this);
return pp::panopainter::legacy_input_dialog_node(dialogs->show_input_dialog(plan_result.value()));
}
void App::dialog_usermanual()
{
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("User manual dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeUserManual>(*this);
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("User manual 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 = [close_dialog](Node*) {
close_dialog();
};
}
void App::dialog_changelog()
{
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Changelog dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeChangelog>(*this);
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("Changelog 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 = [close_dialog](Node*) {
close_dialog();
};
}
void App::dialog_about()
{
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("About dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeAbout>(*this);
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
LOG("About 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 = [close_dialog](Node*) {
close_dialog();
};
}
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)
{
if (!can_start_document_export(*this, true))
return;
// TODO: use picker
const auto target = pp::app::make_document_export_file_target(work_path, doc_name, ext);
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(
pp::app::DocumentExportSuccessKind::equirectangular,
target.status().message);
message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
const auto status = pp::panopainter::execute_legacy_document_export_file(*this, target.value());
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(
pp::app::DocumentExportExecutionKind::equirectangular_file),
status.message);
}
void App::dialog_export_layers()
{
if (!can_start_document_export(*this, true))
return;
start_document_export_collection(
*this,
pp::app::DocumentExportCollectionKind::layers);
}
void App::dialog_export_anim_frames()
{
if (!can_start_document_export(*this, true))
return;
start_document_export_collection(
*this,
pp::app::DocumentExportCollectionKind::animation_frames);
}
void App::dialog_export_depth()
{
if (!can_start_document_export(*this, true))
return;
const auto status = pp::panopainter::execute_legacy_document_export_depth(*this, doc_name);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::depth),
status.message);
}
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()
{
if (!can_start_document_export(*this, false))
return;
const auto status = pp::panopainter::execute_legacy_document_export_cube_faces(*this, doc_name);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::cube_faces),
status.message);
}
void App::dialog_layer_rename()
{
auto* overlay_anchor = layout[main_id];
if (!overlay_anchor) {
LOG("Layer rename dialog open failed: main layout anchor is missing");
return;
}
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeDialogLayerRename>(*this);
dialog->input->set_text(layers->m_current_layer->m_label_text);
App::I->showKeyboard();
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
if (!overlay) {
App::I->hideKeyboard();
LOG("Layer rename 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*)
{
const auto old_name = layers->m_current_layer->m_label_text;
const auto plan = pp::app::plan_document_layer_rename(old_name, dialog->get_name());
if (!plan)
return;
const auto status = pp::panopainter::execute_legacy_document_layer_rename_plan(*this, plan.value(), dialog);
if (!status.ok())
LOG("Layer rename failed: %s", status.message);
};
dialog->btn_cancel->on_click = [close_dialog](Node*)
{
close_dialog();
};
}
void App::dialog_preset_download()
{
}
void App::dialog_ppbr_export()
{
auto* overlay_anchor = layout[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>(*this);
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 = [this, dialog] (Node*) {
const auto request = pp::panopainter::make_legacy_brush_package_export_request(*dialog);
if (uses_prepared_file_writes())
{
pick_file_save("ppbr", "exported-brushes",
[this, dialog, request] (std::string path) {
const auto status = pp::panopainter::execute_legacy_brush_package_export(
*this,
*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;
}
pick_file_save({ "ppbr" }, [this, dialog, request] (std::string path) {
const auto status = pp::panopainter::execute_legacy_brush_package_export(
*this,
*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();
};
}
void App::dialog_timelapse_export()
{
if (!can_start_document_export(*this, false))
return;
if (uses_prepared_file_writes())
{
const auto target = pp::app::make_document_export_suggested_name(doc_name, "-timelapse");
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(
pp::app::DocumentExportSuccessKind::timelapse,
target.status().message);
message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
pick_file_save("mp4", target.value().name,
[this](std::string path) {
const auto status = pp::panopainter::execute_legacy_document_video_export(
*this,
pp::app::DocumentVideoExportKind::timelapse,
path,
false);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(
pp::app::DocumentExportExecutionKind::timelapse),
status.message);
},
[](const std::string& path, bool saved) {
(void)path;
(void)saved;
}
);
return;
}
pick_file_save({ "mp4" }, [this](std::string path) {
const auto status = pp::panopainter::execute_legacy_document_video_export(
*this,
pp::app::DocumentVideoExportKind::timelapse,
path,
true);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::timelapse),
status.message);
});
}
void App::dialog_export_mp4()
{
if (!can_start_document_export(*this, false))
return;
if (uses_prepared_file_writes())
{
const auto target = pp::app::make_document_export_suggested_name(doc_name, "-animation");
if (!target) {
const auto dialog = pp::app::plan_document_export_failure_dialog(
pp::app::DocumentExportSuccessKind::animation_mp4,
target.status().message);
message_box(dialog.title, dialog.message, dialog.show_cancel);
return;
}
pick_file_save("mp4", target.value().name,
[this](std::string path) {
const auto status = pp::panopainter::execute_legacy_document_video_export(
*this,
pp::app::DocumentVideoExportKind::animation_mp4,
path,
false);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(
pp::app::DocumentExportExecutionKind::animation_mp4),
status.message);
},
[](const std::string& path, bool saved) {
(void)path;
(void)saved;
}
);
return;
}
pick_file_save({ "mp4" }, [this](std::string path) {
const auto status = pp::panopainter::execute_legacy_document_video_export(
*this,
pp::app::DocumentVideoExportKind::animation_mp4,
path,
true);
if (!status.ok())
LOG(
"%s: %s",
pp::app::document_export_execution_log_message(pp::app::DocumentExportExecutionKind::animation_mp4),
status.message);
});
}
void App::dialog_whatsnew(bool force_show)
{
auto* overlay_anchor = layout[main_id];
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 = pp::panopainter::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 = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, *overlay_handle);
(void)close_status;
*overlay_handle = {};
};
auto whatsnew = std::make_shared<NodeRemotePage>();
whatsnew->set_manager(&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 = pp::panopainter::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*) {
pp::panopainter::clear_legacy_whatsnew_id();
pp::panopainter::save_legacy_preferences();
close_overlay();
});
whatsnew->add_button("Close", 100, [whatsnew, close_overlay](Node*) {
pp::panopainter::set_legacy_whatsnew_id(whatsnew->m_page_id);
pp::panopainter::save_legacy_preferences();
close_overlay();
});
if (force_show)
open_overlay(whatsnew);
}
void App::dialog_shortcuts()
{
(void)pp::panopainter::add_legacy_overlay_node<NodeShortcuts>(*this);
}