Route canvas hotkeys through app core

This commit is contained in:
2026-06-03 20:30:07 +02:00
parent 16a1d1e15b
commit 6945ce7e23
8 changed files with 953 additions and 52 deletions

View File

@@ -2,6 +2,7 @@
#include "app_core/app_preferences.h"
#include "app_core/app_status.h"
#include "app_core/brush_ui.h"
#include "app_core/canvas_hotkey.h"
#include "app_core/canvas_tool_ui.h"
#include "app_core/document_animation.h"
#include "app_core/document_canvas.h"
@@ -406,6 +407,17 @@ struct PlanCanvasToolArgs {
bool current_mode_draw = false;
};
struct PlanCanvasHotkeyArgs {
std::string event = "key-up";
std::string key = "z";
bool ctrl_down = false;
bool shift_down = false;
bool mouse_focused = false;
int undo_count = 1;
int redo_count = 1;
int touch_finger_count = 0;
};
struct PlanCanvasToolStateArgs {
std::string mode = "draw";
bool picking = false;
@@ -1323,6 +1335,68 @@ const char* canvas_tool_transform_action_name(pp::app::CanvasToolTransformAction
return "none";
}
const char* canvas_hotkey_event_name(pp::app::CanvasHotkeyEvent event) noexcept
{
switch (event) {
case pp::app::CanvasHotkeyEvent::key_down:
return "key-down";
case pp::app::CanvasHotkeyEvent::key_up:
return "key-up";
case pp::app::CanvasHotkeyEvent::touch_tap:
return "touch-tap";
}
return "key-up";
}
const char* canvas_hotkey_key_name(pp::app::CanvasHotkeyKey key) noexcept
{
switch (key) {
case pp::app::CanvasHotkeyKey::other:
return "other";
case pp::app::CanvasHotkeyKey::android_back:
return "android-back";
case pp::app::CanvasHotkeyKey::alt:
return "alt";
case pp::app::CanvasHotkeyKey::e:
return "e";
case pp::app::CanvasHotkeyKey::s:
return "s";
case pp::app::CanvasHotkeyKey::tab:
return "tab";
case pp::app::CanvasHotkeyKey::z:
return "z";
case pp::app::CanvasHotkeyKey::bracket_left:
return "bracket-left";
case pp::app::CanvasHotkeyKey::bracket_right:
return "bracket-right";
}
return "other";
}
const char* canvas_hotkey_action_name(pp::app::CanvasHotkeyAction action) noexcept
{
switch (action) {
case pp::app::CanvasHotkeyAction::none:
return "none";
case pp::app::CanvasHotkeyAction::select_tool:
return "select-tool";
case pp::app::CanvasHotkeyAction::history:
return "history";
case pp::app::CanvasHotkeyAction::save_document:
return "save-document";
case pp::app::CanvasHotkeyAction::toggle_ui:
return "toggle-ui";
case pp::app::CanvasHotkeyAction::adjust_brush_size:
return "adjust-brush-size";
case pp::app::CanvasHotkeyAction::show_cursor:
return "show-cursor";
}
return "none";
}
const char* grid_ui_operation_name(pp::app::GridUiOperation operation) noexcept
{
switch (operation) {
@@ -1796,6 +1870,7 @@ void print_help()
<< " plan-brush-stroke-control --kind float|bool|blend|tip-aspect-reset|default-reset [--setting NAME] [--value N] [--enabled|--disabled] [--blend-mode N]\n"
<< " plan-paint-feedback [--width N] [--height N] [--simple|--complex] [--framebuffer-fetch] [--texture-copy] [--blit] [--explicit-transitions] [--render-only] [--depth]\n"
<< " plan-stroke-composite [--width N] [--height N] [--layer-blend N] [--stroke-blend N] [--dual-blend] [--pattern-blend] [--framebuffer-fetch] [--texture-copy] [--blit] [--explicit-transitions] [--render-only] [--depth]\n"
<< " plan-canvas-hotkey --event key-down|key-up|touch-tap --key e|z|s|tab|alt|android-back|bracket-left|bracket-right [--ctrl] [--shift] [--mouse-focus] [--undo-count N] [--redo-count N] [--touch-fingers N]\n"
<< " plan-canvas-tool --kind draw|erase|line|camera|grid|copy|cut|fill|mask-free|mask-line|bucket|pick|touch-lock [--current-mode-draw]\n"
<< " plan-canvas-tool-state [--mode draw|erase|line|camera|grid|copy|cut|fill|mask-free|mask-line|bucket] [--picking] [--touch-lock]\n"
<< " plan-grid-operation --kind pick|load|reload|clear|render|commit [--path FILE] [--no-heightmap] [--no-canvas] [--float32] [--float16] [--texture-resolution N] [--samples N]\n"
@@ -5340,6 +5415,166 @@ int plan_canvas_tool(int argc, char** argv)
return 0;
}
pp::foundation::Result<pp::app::CanvasHotkeyEvent> parse_canvas_hotkey_event(std::string_view event)
{
if (event == "key-down") {
return pp::foundation::Result<pp::app::CanvasHotkeyEvent>::success(
pp::app::CanvasHotkeyEvent::key_down);
}
if (event == "key-up") {
return pp::foundation::Result<pp::app::CanvasHotkeyEvent>::success(
pp::app::CanvasHotkeyEvent::key_up);
}
if (event == "touch-tap") {
return pp::foundation::Result<pp::app::CanvasHotkeyEvent>::success(
pp::app::CanvasHotkeyEvent::touch_tap);
}
return pp::foundation::Result<pp::app::CanvasHotkeyEvent>::failure(
pp::foundation::Status::invalid_argument("unknown canvas hotkey event"));
}
pp::foundation::Result<pp::app::CanvasHotkeyKey> parse_canvas_hotkey_key(std::string_view key)
{
if (key == "other") {
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::success(
pp::app::CanvasHotkeyKey::other);
}
if (key == "android-back") {
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::success(
pp::app::CanvasHotkeyKey::android_back);
}
if (key == "alt") {
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::success(
pp::app::CanvasHotkeyKey::alt);
}
if (key == "e") {
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::success(pp::app::CanvasHotkeyKey::e);
}
if (key == "s") {
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::success(pp::app::CanvasHotkeyKey::s);
}
if (key == "tab") {
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::success(pp::app::CanvasHotkeyKey::tab);
}
if (key == "z") {
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::success(pp::app::CanvasHotkeyKey::z);
}
if (key == "bracket-left") {
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::success(
pp::app::CanvasHotkeyKey::bracket_left);
}
if (key == "bracket-right") {
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::success(
pp::app::CanvasHotkeyKey::bracket_right);
}
return pp::foundation::Result<pp::app::CanvasHotkeyKey>::failure(
pp::foundation::Status::invalid_argument("unknown canvas hotkey key"));
}
pp::foundation::Status parse_plan_canvas_hotkey_args(
int argc,
char** argv,
PlanCanvasHotkeyArgs& args)
{
for (int i = 2; i < argc; ++i) {
const std::string_view key(argv[i]);
if (key == "--event" || key == "--key") {
if (i + 1 >= argc) {
return pp::foundation::Status::invalid_argument("missing value for option");
}
if (key == "--event") {
args.event = argv[++i];
} else {
args.key = argv[++i];
}
} else if (key == "--undo-count" || key == "--redo-count" || key == "--touch-fingers") {
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 == "--undo-count") {
args.undo_count = value.value();
} else if (key == "--redo-count") {
args.redo_count = value.value();
} else {
args.touch_finger_count = value.value();
}
} else if (key == "--ctrl") {
args.ctrl_down = true;
} else if (key == "--shift") {
args.shift_down = true;
} else if (key == "--mouse-focus") {
args.mouse_focused = true;
} else {
return pp::foundation::Status::invalid_argument("unknown option");
}
}
return pp::foundation::Status::success();
}
int plan_canvas_hotkey(int argc, char** argv)
{
PlanCanvasHotkeyArgs args;
const auto status = parse_plan_canvas_hotkey_args(argc, argv, args);
if (!status.ok()) {
print_error("plan-canvas-hotkey", status.message);
return 2;
}
const auto event = parse_canvas_hotkey_event(args.event);
if (!event) {
print_error("plan-canvas-hotkey", event.status().message);
return 2;
}
const auto key = parse_canvas_hotkey_key(args.key);
if (!key) {
print_error("plan-canvas-hotkey", key.status().message);
return 2;
}
pp::app::CanvasHotkeyState state;
state.ctrl_down = args.ctrl_down;
state.shift_down = args.shift_down;
state.mouse_focused = args.mouse_focused;
state.undo_count = args.undo_count;
state.redo_count = args.redo_count;
state.touch_finger_count = args.touch_finger_count;
const auto plan = pp::app::plan_canvas_hotkey(event.value(), key.value(), state);
if (!plan) {
print_error("plan-canvas-hotkey", plan.status().message);
return 2;
}
const auto& value = plan.value();
std::cout << "{\"ok\":true,\"command\":\"plan-canvas-hotkey\""
<< ",\"state\":{\"event\":\"" << json_escape(args.event)
<< "\",\"key\":\"" << json_escape(args.key)
<< "\",\"ctrl\":" << json_bool(args.ctrl_down)
<< ",\"shift\":" << json_bool(args.shift_down)
<< ",\"mouseFocus\":" << json_bool(args.mouse_focused)
<< ",\"undoCount\":" << args.undo_count
<< ",\"redoCount\":" << args.redo_count
<< ",\"touchFingers\":" << args.touch_finger_count
<< "},\"plan\":{\"event\":\"" << canvas_hotkey_event_name(value.event)
<< "\",\"key\":\"" << canvas_hotkey_key_name(value.key)
<< "\",\"action\":\"" << canvas_hotkey_action_name(value.action)
<< "\",\"toolMode\":\"" << canvas_tool_mode_name(value.tool.mode)
<< "\",\"historyOperation\":\"" << history_ui_operation_name(value.history.operation)
<< "\",\"historyNoOp\":" << json_bool(value.history.no_op)
<< ",\"saveIntent\":\"" << document_save_intent_name(value.save_intent)
<< "\",\"brushSizeDelta\":" << value.brush_size_delta
<< ",\"noOp\":" << json_bool(value.no_op)
<< "}}\n";
return 0;
}
pp::foundation::Status parse_plan_canvas_tool_state_args(
int argc,
char** argv,
@@ -8329,6 +8564,10 @@ int main(int argc, char** argv)
return plan_stroke_composite(argc, argv);
}
if (command == "plan-canvas-hotkey") {
return plan_canvas_hotkey(argc, argv);
}
if (command == "plan-canvas-tool") {
return plan_canvas_tool(argc, argv);
}