Extract about menu action planning

This commit is contained in:
2026-06-03 12:27:47 +02:00
parent c640519772
commit b5bd6d42f7
8 changed files with 476 additions and 44 deletions

View File

@@ -223,6 +223,7 @@ target_link_libraries(pp_platform_api
pp_project_warnings) pp_project_warnings)
add_library(pp_app_core STATIC add_library(pp_app_core STATIC
src/app_core/about_menu.h
src/app_core/app_preferences.h src/app_core/app_preferences.h
src/app_core/app_status.h src/app_core/app_status.h
src/app_core/brush_ui.h src/app_core/brush_ui.h

File diff suppressed because one or more lines are too long

View File

@@ -545,6 +545,11 @@ grid-clear, and platform-only SonarPen gating before legacy UI/panel/canvas
execution continues. The live animation panel route now also checks animation execution continues. The live animation panel route now also checks animation
panel visibility and applies animation panel layout state instead of using the panel visibility and applies animation panel layout state instead of using the
grid panel by mistake. grid panel by mistake.
`pano_cli plan-about-menu` exposes app-core planning for About menu help,
about, what's-new, crash-test, and performance-test commands, including
versioned what's-new labels, diagnostic gating, and no-canvas performance-test
blocking before legacy dialogs, platform crash hooks, and Canvas performance
strokes continue.
`pp_platform_api` now owns a headless `PlatformServices` interface for `pp_platform_api` now owns a headless `PlatformServices` interface for
startup storage path preparation, clipboard text, cursor visibility, startup storage path preparation, clipboard text, cursor visibility,
virtual-keyboard visibility, UI-thread lifecycle hooks, render-context virtual-keyboard visibility, UI-thread lifecycle hooks, render-context
@@ -1273,6 +1278,14 @@ Results:
`pano_cli_plan_tools_panel_visible_noop_smoke`, and `pano_cli_plan_tools_panel_visible_noop_smoke`, and
`pano_cli_plan_tools_panel_rejects_unknown` passed and expose live Tools `pano_cli_plan_tools_panel_rejects_unknown` passed and expose live Tools
menu/panel planning as JSON automation. menu/panel planning as JSON automation.
- `pp_app_core_about_menu_tests` passed, covering About/help/what's-new dialog
routing, versioned what's-new labels, crash diagnostic gating, performance
workload metadata, and no-canvas performance-test blocking.
- `pano_cli_plan_about_menu_news_smoke`,
`pano_cli_plan_about_menu_performance_no_canvas_smoke`,
`pano_cli_plan_about_menu_crash_disabled_smoke`, and
`pano_cli_plan_about_menu_rejects_unknown` passed and expose live About menu
planning as JSON automation.
- `pp_app_core_document_sharing_tests` passed, covering saved-path gating before - `pp_app_core_document_sharing_tests` passed, covering saved-path gating before
platform share execution. platform share execution.
- `pano_cli_plan_share_file_unsaved_smoke` and - `pano_cli_plan_share_file_unsaved_smoke` and

86
src/app_core/about_menu.h Normal file
View File

@@ -0,0 +1,86 @@
#pragma once
#include <string>
namespace pp::app {
enum class AboutMenuCommand {
help_guide,
about_app,
whats_new,
induce_crash,
performance_test,
};
enum class AboutMenuAction {
show_user_manual,
show_about_dialog,
show_whats_new_dialog,
trigger_crash_test,
run_performance_test,
no_op_unavailable,
};
struct AboutMenuPlan {
AboutMenuCommand command = AboutMenuCommand::help_guide;
AboutMenuAction action = AboutMenuAction::show_user_manual;
std::string label;
bool closes_root_popup = true;
bool requires_canvas = false;
int performance_iterations = 0;
int performance_updates_per_iteration = 0;
};
[[nodiscard]] inline AboutMenuPlan plan_about_menu_command(
AboutMenuCommand command,
int version_major,
int version_minor,
int version_fix,
bool diagnostics_available = true,
bool has_canvas = true)
{
AboutMenuPlan plan;
plan.command = command;
switch (command) {
case AboutMenuCommand::help_guide:
plan.action = AboutMenuAction::show_user_manual;
plan.label = "Help Guide";
break;
case AboutMenuCommand::about_app:
plan.action = AboutMenuAction::show_about_dialog;
plan.label = "About PanoPainter";
break;
case AboutMenuCommand::whats_new:
plan.action = AboutMenuAction::show_whats_new_dialog;
plan.label = "What's new in "
+ std::to_string(version_major)
+ "."
+ std::to_string(version_minor)
+ "."
+ std::to_string(version_fix)
+ "?";
break;
case AboutMenuCommand::induce_crash:
plan.label = "Induce crash";
plan.action = diagnostics_available
? AboutMenuAction::trigger_crash_test
: AboutMenuAction::no_op_unavailable;
plan.closes_root_popup = diagnostics_available;
break;
case AboutMenuCommand::performance_test:
plan.label = has_canvas ? "Performance test" : "Performance test (No canvas)";
plan.requires_canvas = true;
plan.performance_iterations = 100;
plan.performance_updates_per_iteration = 10;
plan.action = has_canvas
? AboutMenuAction::run_performance_test
: AboutMenuAction::no_op_unavailable;
plan.closes_root_popup = has_canvas;
break;
}
return plan;
}
}

View File

@@ -6,6 +6,7 @@
#include "node_progress_bar.h" #include "node_progress_bar.h"
#include "node_dialog_picker.h" #include "node_dialog_picker.h"
#include "node_panel_floating.h" #include "node_panel_floating.h"
#include "app_core/about_menu.h"
#include "app_core/app_preferences.h" #include "app_core/app_preferences.h"
#include "app_core/brush_ui.h" #include "app_core/brush_ui.h"
#include "app_core/canvas_tool_ui.h" #include "app_core/canvas_tool_ui.h"
@@ -1495,76 +1496,131 @@ void App::init_menu_about()
layout[main_id]->add_child(popup); layout[main_id]->add_child(popup);
popup->find<NodeButtonCustom>("about-app")->on_click = [this, popup](Node*) { popup->find<NodeButtonCustom>("about-app")->on_click = [this, popup](Node*) {
dialog_about(); const auto plan = pp::app::plan_about_menu_command(
popup->mouse_release(); pp::app::AboutMenuCommand::about_app,
popup->destroy(); g_version_major,
g_version_minor,
g_version_fix);
if (plan.action == pp::app::AboutMenuAction::show_about_dialog)
dialog_about();
if (plan.closes_root_popup)
{
popup->mouse_release();
popup->destroy();
}
}; };
popup->find<NodeButtonCustom>("about-doc")->on_click = [this, popup](Node*) { popup->find<NodeButtonCustom>("about-doc")->on_click = [this, popup](Node*) {
// auto path = Asset::absolute("data/doc/test.pdf"); // auto path = Asset::absolute("data/doc/test.pdf");
// display_file(path); // display_file(path);
dialog_usermanual(); const auto plan = pp::app::plan_about_menu_command(
popup->mouse_release(); pp::app::AboutMenuCommand::help_guide,
popup->destroy(); g_version_major,
g_version_minor,
g_version_fix);
if (plan.action == pp::app::AboutMenuAction::show_user_manual)
dialog_usermanual();
if (plan.closes_root_popup)
{
popup->mouse_release();
popup->destroy();
}
}; };
if (auto item = popup->find<NodeButtonCustom>("about-news")) if (auto item = popup->find<NodeButtonCustom>("about-news"))
{ {
if (auto text = item->find<NodeText>("menu-label")) if (auto text = item->find<NodeText>("menu-label"))
{ {
static char label[128]; const auto plan = pp::app::plan_about_menu_command(
sprintf(label, "What's new in %d.%d.%d?", g_version_major, g_version_minor, g_version_fix); pp::app::AboutMenuCommand::whats_new,
text->set_text(label); g_version_major,
g_version_minor,
g_version_fix);
text->set_text(plan.label.c_str());
} }
item->on_click = [this, popup](Node*) { item->on_click = [this, popup](Node*) {
dialog_whatsnew(true); const auto plan = pp::app::plan_about_menu_command(
popup->mouse_release(); pp::app::AboutMenuCommand::whats_new,
popup->destroy(); g_version_major,
g_version_minor,
g_version_fix);
if (plan.action == pp::app::AboutMenuAction::show_whats_new_dialog)
dialog_whatsnew(true);
if (plan.closes_root_popup)
{
popup->mouse_release();
popup->destroy();
}
}; };
} }
if (auto b = popup->find<NodeButtonCustom>("about-crash")) if (auto b = popup->find<NodeButtonCustom>("about-crash"))
{ {
b->on_click = [this, popup](Node*) { b->on_click = [this, popup](Node*) {
LOG("crashing"); const auto plan = pp::app::plan_about_menu_command(
crash_test(); pp::app::AboutMenuCommand::induce_crash,
popup->mouse_release(); g_version_major,
popup->destroy(); g_version_minor,
g_version_fix);
if (plan.action == pp::app::AboutMenuAction::trigger_crash_test)
{
LOG("crashing");
crash_test();
}
if (plan.closes_root_popup)
{
popup->mouse_release();
popup->destroy();
}
}; };
} }
if (auto b = popup->find<NodeButtonCustom>("about-perf")) if (auto b = popup->find<NodeButtonCustom>("about-perf"))
{ {
b->on_click = [this, popup](Node*) { b->on_click = [this, popup](Node*) {
LOG("perf"); const auto plan = pp::app::plan_about_menu_command(
static char str[256]; pp::app::AboutMenuCommand::performance_test,
render_task([&] g_version_major,
g_version_minor,
g_version_fix,
true,
Canvas::I != nullptr);
if (plan.action == pp::app::AboutMenuAction::run_performance_test)
{ {
auto start = std::chrono::high_resolution_clock::now(); LOG("perf");
Canvas::I->stroke_start({ 0, 0, 0 }, 0.9f); std::string message;
for (int i = 0; i < 100; i++) const int performance_iterations = plan.performance_iterations;
render_task([&]
{ {
Canvas::I->stroke_update({ 100, 100, 0 }, 0.9f); auto start = std::chrono::high_resolution_clock::now();
Canvas::I->stroke_update({ 200, 200, 0 }, 0.9f); Canvas::I->stroke_start({ 0, 0, 0 }, 0.9f);
Canvas::I->stroke_update({ 200, 100, 0 }, 0.9f); for (int i = 0; i < performance_iterations; i++)
Canvas::I->stroke_update({ 100, 200, 0 }, 0.9f); {
Canvas::I->stroke_update({ 300, 300, 0 }, 0.9f); Canvas::I->stroke_update({ 100, 100, 0 }, 0.9f);
Canvas::I->stroke_update({ 200, 500, 0 }, 0.9f); Canvas::I->stroke_update({ 200, 200, 0 }, 0.9f);
Canvas::I->stroke_update({ 500, 500, 0 }, 0.9f); Canvas::I->stroke_update({ 200, 100, 0 }, 0.9f);
Canvas::I->stroke_update({ 400, 400, 0 }, 0.9f); Canvas::I->stroke_update({ 100, 200, 0 }, 0.9f);
Canvas::I->stroke_update({ 0, 200, 0 }, 0.9f); Canvas::I->stroke_update({ 300, 300, 0 }, 0.9f);
Canvas::I->stroke_update({ 200, 0, 0 }, 0.9f); Canvas::I->stroke_update({ 200, 500, 0 }, 0.9f);
Canvas::I->stroke_draw(); Canvas::I->stroke_update({ 500, 500, 0 }, 0.9f);
} Canvas::I->stroke_update({ 400, 400, 0 }, 0.9f);
Canvas::I->stroke_end(); Canvas::I->stroke_update({ 0, 200, 0 }, 0.9f);
auto diff = std::chrono::high_resolution_clock::now() - start; Canvas::I->stroke_update({ 200, 0, 0 }, 0.9f);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(diff).count(); Canvas::I->stroke_draw();
LOG("%lld ms", ms); }
sprintf(str, "Time %lld ms", ms); Canvas::I->stroke_end();
}); auto diff = std::chrono::high_resolution_clock::now() - start;
message_box("Performance test", str); auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(diff).count();
popup->mouse_release(); LOG("%lld ms", ms);
popup->destroy(); message = "Time " + std::to_string(ms) + " ms";
});
message_box("Performance test", message);
}
if (plan.closes_root_popup)
{
popup->mouse_release();
popup->destroy();
}
}; };
} }
}; };

View File

@@ -268,6 +268,16 @@ add_test(NAME pp_ui_core_layout_xml_tests COMMAND pp_ui_core_layout_xml_tests)
set_tests_properties(pp_ui_core_layout_xml_tests PROPERTIES set_tests_properties(pp_ui_core_layout_xml_tests PROPERTIES
LABELS "ui;desktop-fast;fuzz") LABELS "ui;desktop-fast;fuzz")
add_executable(pp_app_core_about_menu_tests
app_core/about_menu_tests.cpp)
target_link_libraries(pp_app_core_about_menu_tests PRIVATE
pp_app_core
pp_test_harness)
add_test(NAME pp_app_core_about_menu_tests COMMAND pp_app_core_about_menu_tests)
set_tests_properties(pp_app_core_about_menu_tests PROPERTIES
LABELS "app;ui;desktop-fast;fuzz")
add_executable(pp_app_core_brush_ui_tests add_executable(pp_app_core_brush_ui_tests
app_core/brush_ui_tests.cpp) app_core/brush_ui_tests.cpp)
target_link_libraries(pp_app_core_brush_ui_tests PRIVATE target_link_libraries(pp_app_core_brush_ui_tests PRIVATE
@@ -851,6 +861,30 @@ if(TARGET pano_cli)
LABELS "app;ui;integration;desktop-fast;fuzz" LABELS "app;ui;integration;desktop-fast;fuzz"
WILL_FAIL TRUE) WILL_FAIL TRUE)
add_test(NAME pano_cli_plan_about_menu_news_smoke
COMMAND pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7)
set_tests_properties(pano_cli_plan_about_menu_news_smoke PROPERTIES
LABELS "app;ui;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-about-menu\".*\"command\":\"news\".*\"versionMajor\":2.*\"action\":\"show-whats-new-dialog\".*\"label\":\"What's new in 2.5.7\\?\".*\"closesRootPopup\":true")
add_test(NAME pano_cli_plan_about_menu_performance_no_canvas_smoke
COMMAND pano_cli plan-about-menu --command performance --no-canvas)
set_tests_properties(pano_cli_plan_about_menu_performance_no_canvas_smoke PROPERTIES
LABELS "app;ui;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-about-menu\".*\"hasCanvas\":false.*\"action\":\"no-op-unavailable\".*\"label\":\"Performance test \\(No canvas\\)\".*\"closesRootPopup\":false.*\"requiresCanvas\":true")
add_test(NAME pano_cli_plan_about_menu_crash_disabled_smoke
COMMAND pano_cli plan-about-menu --command crash --no-diagnostics)
set_tests_properties(pano_cli_plan_about_menu_crash_disabled_smoke PROPERTIES
LABELS "app;ui;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-about-menu\".*\"diagnosticsAvailable\":false.*\"action\":\"no-op-unavailable\".*\"closesRootPopup\":false")
add_test(NAME pano_cli_plan_about_menu_rejects_unknown
COMMAND pano_cli plan-about-menu --command missing)
set_tests_properties(pano_cli_plan_about_menu_rejects_unknown PROPERTIES
LABELS "app;ui;integration;desktop-fast;fuzz"
WILL_FAIL TRUE)
add_test(NAME pano_cli_plan_app_status_smoke add_test(NAME pano_cli_plan_app_status_smoke
COMMAND pano_cli plan-app-status COMMAND pano_cli plan-app-status
--doc-name demo --doc-name demo

View File

@@ -0,0 +1,81 @@
#include "app_core/about_menu.h"
#include "test_harness.h"
namespace {
void about_menu_maps_user_facing_dialog_actions(pp::tests::Harness& harness)
{
const auto help = pp::app::plan_about_menu_command(pp::app::AboutMenuCommand::help_guide, 1, 2, 3);
const auto about = pp::app::plan_about_menu_command(pp::app::AboutMenuCommand::about_app, 1, 2, 3);
const auto news = pp::app::plan_about_menu_command(pp::app::AboutMenuCommand::whats_new, 1, 2, 3);
PP_EXPECT(harness, help.action == pp::app::AboutMenuAction::show_user_manual);
PP_EXPECT(harness, help.label == "Help Guide");
PP_EXPECT(harness, help.closes_root_popup);
PP_EXPECT(harness, about.action == pp::app::AboutMenuAction::show_about_dialog);
PP_EXPECT(harness, about.label == "About PanoPainter");
PP_EXPECT(harness, news.action == pp::app::AboutMenuAction::show_whats_new_dialog);
PP_EXPECT(harness, news.label == "What's new in 1.2.3?");
}
void about_menu_gates_diagnostic_crash_action(pp::tests::Harness& harness)
{
const auto enabled = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::induce_crash,
1,
2,
3,
true);
const auto disabled = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::induce_crash,
1,
2,
3,
false);
PP_EXPECT(harness, enabled.action == pp::app::AboutMenuAction::trigger_crash_test);
PP_EXPECT(harness, enabled.closes_root_popup);
PP_EXPECT(harness, disabled.action == pp::app::AboutMenuAction::no_op_unavailable);
PP_EXPECT(harness, !disabled.closes_root_popup);
}
void about_menu_performance_test_declares_workload_and_canvas_requirement(pp::tests::Harness& harness)
{
const auto available = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::performance_test,
1,
2,
3,
true,
true);
const auto no_canvas = pp::app::plan_about_menu_command(
pp::app::AboutMenuCommand::performance_test,
1,
2,
3,
true,
false);
PP_EXPECT(harness, available.action == pp::app::AboutMenuAction::run_performance_test);
PP_EXPECT(harness, available.requires_canvas);
PP_EXPECT(harness, available.performance_iterations == 100);
PP_EXPECT(harness, available.performance_updates_per_iteration == 10);
PP_EXPECT(harness, available.closes_root_popup);
PP_EXPECT(harness, no_canvas.action == pp::app::AboutMenuAction::no_op_unavailable);
PP_EXPECT(harness, no_canvas.label == "Performance test (No canvas)");
PP_EXPECT(harness, no_canvas.requires_canvas);
PP_EXPECT(harness, !no_canvas.closes_root_popup);
}
}
int main()
{
pp::tests::Harness harness;
harness.run("about menu maps user facing dialog actions", about_menu_maps_user_facing_dialog_actions);
harness.run("about menu gates diagnostic crash action", about_menu_gates_diagnostic_crash_action);
harness.run(
"about menu performance test declares workload and canvas requirement",
about_menu_performance_test_declares_workload_and_canvas_requirement);
return harness.finish();
}

View File

@@ -1,3 +1,4 @@
#include "app_core/about_menu.h"
#include "app_core/app_preferences.h" #include "app_core/app_preferences.h"
#include "app_core/app_status.h" #include "app_core/app_status.h"
#include "app_core/brush_ui.h" #include "app_core/brush_ui.h"
@@ -352,6 +353,15 @@ struct PlanToolsPanelArgs {
bool already_visible = false; bool already_visible = false;
}; };
struct PlanAboutMenuArgs {
std::string command = "news";
int version_major = 1;
int version_minor = 0;
int version_fix = 0;
bool diagnostics_available = true;
bool has_canvas = true;
};
struct SimulateAppSessionArgs { struct SimulateAppSessionArgs {
bool has_canvas = true; bool has_canvas = true;
bool new_document = false; bool new_document = false;
@@ -737,6 +747,44 @@ const char* tools_panel_action_name(pp::app::ToolsPanelAction action) noexcept
return "no-op-already-visible"; return "no-op-already-visible";
} }
const char* about_menu_command_name(pp::app::AboutMenuCommand command) noexcept
{
switch (command) {
case pp::app::AboutMenuCommand::help_guide:
return "help";
case pp::app::AboutMenuCommand::about_app:
return "about";
case pp::app::AboutMenuCommand::whats_new:
return "news";
case pp::app::AboutMenuCommand::induce_crash:
return "crash";
case pp::app::AboutMenuCommand::performance_test:
return "performance";
}
return "help";
}
const char* about_menu_action_name(pp::app::AboutMenuAction action) noexcept
{
switch (action) {
case pp::app::AboutMenuAction::show_user_manual:
return "show-user-manual";
case pp::app::AboutMenuAction::show_about_dialog:
return "show-about-dialog";
case pp::app::AboutMenuAction::show_whats_new_dialog:
return "show-whats-new-dialog";
case pp::app::AboutMenuAction::trigger_crash_test:
return "trigger-crash-test";
case pp::app::AboutMenuAction::run_performance_test:
return "run-performance-test";
case pp::app::AboutMenuAction::no_op_unavailable:
return "no-op-unavailable";
}
return "no-op-unavailable";
}
pp::foundation::Result<pp::app::ToolsMenuCommand> parse_tools_menu_command(std::string_view command) pp::foundation::Result<pp::app::ToolsMenuCommand> parse_tools_menu_command(std::string_view command)
{ {
if (command == "panels") { if (command == "panels") {
@@ -790,6 +838,33 @@ pp::foundation::Result<pp::app::ToolsPanel> parse_tools_panel(std::string_view p
pp::foundation::Status::invalid_argument("unknown tools panel")); pp::foundation::Status::invalid_argument("unknown tools panel"));
} }
pp::foundation::Result<pp::app::AboutMenuCommand> parse_about_menu_command(std::string_view command)
{
if (command == "help" || command == "help-guide" || command == "manual") {
return pp::foundation::Result<pp::app::AboutMenuCommand>::success(
pp::app::AboutMenuCommand::help_guide);
}
if (command == "about" || command == "about-app") {
return pp::foundation::Result<pp::app::AboutMenuCommand>::success(
pp::app::AboutMenuCommand::about_app);
}
if (command == "news" || command == "whats-new") {
return pp::foundation::Result<pp::app::AboutMenuCommand>::success(
pp::app::AboutMenuCommand::whats_new);
}
if (command == "crash" || command == "induce-crash") {
return pp::foundation::Result<pp::app::AboutMenuCommand>::success(
pp::app::AboutMenuCommand::induce_crash);
}
if (command == "performance" || command == "perf") {
return pp::foundation::Result<pp::app::AboutMenuCommand>::success(
pp::app::AboutMenuCommand::performance_test);
}
return pp::foundation::Result<pp::app::AboutMenuCommand>::failure(
pp::foundation::Status::invalid_argument("unknown about menu command"));
}
const char* document_layer_rename_action_name(pp::app::DocumentLayerRenameAction action) noexcept const char* document_layer_rename_action_name(pp::app::DocumentLayerRenameAction action) noexcept
{ {
switch (action) { switch (action) {
@@ -1406,6 +1481,7 @@ void print_help()
<< " plan-app-status [--doc-name NAME] [--unsaved] [--resolution N] [--resolution-index N] [--zoom N] [--history-bytes N] [--recording-running] [--encoder-available] [--encoded-frames N]\n" << " plan-app-status [--doc-name NAME] [--unsaved] [--resolution N] [--resolution-index N] [--zoom N] [--history-bytes N] [--recording-running] [--encoder-available] [--encoded-frames N]\n"
<< " plan-tools-menu --command panels|options|clear-grids|reset-camera|shortcuts|sonarpen [--sonarpen-available]\n" << " plan-tools-menu --command panels|options|clear-grids|reset-camera|shortcuts|sonarpen [--sonarpen-available]\n"
<< " plan-tools-panel --panel presets|color|color-advanced|layers|brush|grids|animation [--already-visible]\n" << " plan-tools-panel --panel presets|color|color-advanced|layers|brush|grids|animation [--already-visible]\n"
<< " plan-about-menu --command help|about|news|crash|performance [--version-major N] [--version-minor N] [--version-fix N] [--no-diagnostics] [--no-canvas]\n"
<< " plan-canvas-clear [--no-canvas] [--r N] [--g N] [--b N] [--a N]\n" << " plan-canvas-clear [--no-canvas] [--r N] [--g N] [--b N] [--a N]\n"
<< " plan-image-import --width N --height N\n" << " plan-image-import --width N --height N\n"
<< " plan-document-resize [--current-resolution N] [--selected-resolution-index N]\n" << " plan-document-resize [--current-resolution N] [--selected-resolution-index N]\n"
@@ -3063,6 +3139,86 @@ int plan_tools_panel(int argc, char** argv)
return 0; return 0;
} }
pp::foundation::Status parse_plan_about_menu_args(
int argc,
char** argv,
PlanAboutMenuArgs& args)
{
for (int i = 2; i < argc; ++i) {
const std::string_view key(argv[i]);
if (key == "--command") {
if (i + 1 >= argc) {
return pp::foundation::Status::invalid_argument("missing value for option");
}
args.command = argv[++i];
} else if (key == "--version-major" || key == "--version-minor" || key == "--version-fix") {
if (i + 1 >= argc) {
return pp::foundation::Status::invalid_argument("missing value for option");
}
const auto value = parse_i32_arg(argv[++i]);
if (!value) {
return value.status();
}
if (key == "--version-major") {
args.version_major = value.value();
} else if (key == "--version-minor") {
args.version_minor = value.value();
} else {
args.version_fix = value.value();
}
} else if (key == "--no-diagnostics") {
args.diagnostics_available = false;
} else if (key == "--no-canvas") {
args.has_canvas = false;
} else {
return pp::foundation::Status::invalid_argument("unknown option");
}
}
return pp::foundation::Status::success();
}
int plan_about_menu(int argc, char** argv)
{
PlanAboutMenuArgs args;
const auto status = parse_plan_about_menu_args(argc, argv, args);
if (!status.ok()) {
print_error("plan-about-menu", status.message);
return 2;
}
const auto command = parse_about_menu_command(args.command);
if (!command) {
print_error("plan-about-menu", command.status().message);
return 2;
}
const auto plan = pp::app::plan_about_menu_command(
command.value(),
args.version_major,
args.version_minor,
args.version_fix,
args.diagnostics_available,
args.has_canvas);
std::cout << "{\"ok\":true,\"command\":\"plan-about-menu\""
<< ",\"state\":{\"command\":\"" << json_escape(args.command)
<< "\",\"versionMajor\":" << args.version_major
<< ",\"versionMinor\":" << args.version_minor
<< ",\"versionFix\":" << args.version_fix
<< ",\"diagnosticsAvailable\":" << json_bool(args.diagnostics_available)
<< ",\"hasCanvas\":" << json_bool(args.has_canvas)
<< "},\"plan\":{\"command\":\"" << about_menu_command_name(plan.command)
<< "\",\"action\":\"" << about_menu_action_name(plan.action)
<< "\",\"label\":\"" << json_escape(plan.label)
<< "\",\"closesRootPopup\":" << json_bool(plan.closes_root_popup)
<< ",\"requiresCanvas\":" << json_bool(plan.requires_canvas)
<< ",\"performanceIterations\":" << plan.performance_iterations
<< ",\"performanceUpdatesPerIteration\":" << plan.performance_updates_per_iteration
<< "}}\n";
return 0;
}
pp::foundation::Status parse_plan_app_status_args( pp::foundation::Status parse_plan_app_status_args(
int argc, int argc,
char** argv, char** argv,
@@ -6834,6 +6990,10 @@ int main(int argc, char** argv)
return plan_tools_panel(argc, argv); return plan_tools_panel(argc, argv);
} }
if (command == "plan-about-menu") {
return plan_about_menu(argc, argv);
}
if (command == "plan-document-resize") { if (command == "plan-document-resize") {
return plan_document_resize(argc, argv); return plan_document_resize(argc, argv);
} }