226 lines
7.0 KiB
C++
226 lines
7.0 KiB
C++
#pragma once
|
|
|
|
#include "app_core/canvas_tool_ui.h"
|
|
#include "app_core/document_session.h"
|
|
#include "app_core/history_ui.h"
|
|
#include "foundation/result.h"
|
|
|
|
namespace pp::app {
|
|
|
|
enum class CanvasHotkeyEvent {
|
|
key_down,
|
|
key_up,
|
|
touch_tap,
|
|
};
|
|
|
|
enum class CanvasHotkeyKey {
|
|
other,
|
|
android_back,
|
|
alt,
|
|
e,
|
|
s,
|
|
tab,
|
|
z,
|
|
bracket_left,
|
|
bracket_right,
|
|
};
|
|
|
|
enum class CanvasHotkeyAction {
|
|
none,
|
|
select_tool,
|
|
history,
|
|
save_document,
|
|
toggle_ui,
|
|
adjust_brush_size,
|
|
show_cursor,
|
|
};
|
|
|
|
struct CanvasHotkeyState {
|
|
bool ctrl_down = false;
|
|
bool shift_down = false;
|
|
bool mouse_focused = false;
|
|
int undo_count = 0;
|
|
int redo_count = 0;
|
|
int touch_finger_count = 0;
|
|
};
|
|
|
|
struct CanvasHotkeyPlan {
|
|
CanvasHotkeyAction action = CanvasHotkeyAction::none;
|
|
CanvasHotkeyEvent event = CanvasHotkeyEvent::key_up;
|
|
CanvasHotkeyKey key = CanvasHotkeyKey::other;
|
|
CanvasToolPlan tool;
|
|
HistoryUiPlan history;
|
|
DocumentSaveIntent save_intent = DocumentSaveIntent::save;
|
|
float brush_size_delta = 0.0F;
|
|
bool no_op = true;
|
|
};
|
|
|
|
class CanvasHotkeyServices {
|
|
public:
|
|
virtual ~CanvasHotkeyServices() = default;
|
|
|
|
virtual pp::foundation::Status execute_tool(const CanvasToolPlan& plan) = 0;
|
|
virtual pp::foundation::Status execute_history(const HistoryUiPlan& plan) = 0;
|
|
virtual void save_document(DocumentSaveIntent intent) = 0;
|
|
virtual void toggle_ui() = 0;
|
|
virtual void adjust_brush_size(float delta) = 0;
|
|
virtual void show_cursor() = 0;
|
|
};
|
|
|
|
[[nodiscard]] inline pp::foundation::Status validate_canvas_hotkey_state(
|
|
const CanvasHotkeyState& state) noexcept
|
|
{
|
|
if (state.undo_count < 0) {
|
|
return pp::foundation::Status::out_of_range("undo action count must not be negative");
|
|
}
|
|
if (state.redo_count < 0) {
|
|
return pp::foundation::Status::out_of_range("redo action count must not be negative");
|
|
}
|
|
if (state.touch_finger_count < 0) {
|
|
return pp::foundation::Status::out_of_range("touch finger count must not be negative");
|
|
}
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<CanvasHotkeyPlan> plan_canvas_hotkey(
|
|
CanvasHotkeyEvent event,
|
|
CanvasHotkeyKey key,
|
|
const CanvasHotkeyState& state)
|
|
{
|
|
const auto state_status = validate_canvas_hotkey_state(state);
|
|
if (!state_status.ok()) {
|
|
return pp::foundation::Result<CanvasHotkeyPlan>::failure(state_status);
|
|
}
|
|
|
|
CanvasHotkeyPlan plan;
|
|
plan.event = event;
|
|
plan.key = key;
|
|
|
|
if (event == CanvasHotkeyEvent::touch_tap) {
|
|
if (state.touch_finger_count == 2) {
|
|
auto history = plan_history_undo(state.undo_count);
|
|
if (!history) {
|
|
return pp::foundation::Result<CanvasHotkeyPlan>::failure(history.status());
|
|
}
|
|
plan.action = CanvasHotkeyAction::history;
|
|
plan.history = history.value();
|
|
plan.no_op = plan.history.no_op;
|
|
}
|
|
return pp::foundation::Result<CanvasHotkeyPlan>::success(plan);
|
|
}
|
|
|
|
if (event == CanvasHotkeyEvent::key_down) {
|
|
switch (key) {
|
|
case CanvasHotkeyKey::e:
|
|
plan.action = CanvasHotkeyAction::select_tool;
|
|
plan.tool = plan_canvas_tool_select(CanvasToolMode::erase);
|
|
plan.no_op = false;
|
|
break;
|
|
case CanvasHotkeyKey::android_back: {
|
|
auto history = plan_history_undo(state.undo_count);
|
|
if (!history) {
|
|
return pp::foundation::Result<CanvasHotkeyPlan>::failure(history.status());
|
|
}
|
|
plan.action = CanvasHotkeyAction::history;
|
|
plan.history = history.value();
|
|
plan.no_op = plan.history.no_op;
|
|
break;
|
|
}
|
|
case CanvasHotkeyKey::alt:
|
|
if (state.mouse_focused) {
|
|
plan.action = CanvasHotkeyAction::show_cursor;
|
|
plan.no_op = false;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return pp::foundation::Result<CanvasHotkeyPlan>::success(plan);
|
|
}
|
|
|
|
switch (key) {
|
|
case CanvasHotkeyKey::e:
|
|
plan.action = CanvasHotkeyAction::select_tool;
|
|
plan.tool = plan_canvas_tool_select(CanvasToolMode::draw);
|
|
plan.no_op = false;
|
|
break;
|
|
case CanvasHotkeyKey::tab:
|
|
plan.action = CanvasHotkeyAction::toggle_ui;
|
|
plan.no_op = false;
|
|
break;
|
|
case CanvasHotkeyKey::z:
|
|
if (state.ctrl_down) {
|
|
auto history = state.shift_down
|
|
? plan_history_redo(state.redo_count)
|
|
: plan_history_undo(state.undo_count);
|
|
if (!history) {
|
|
return pp::foundation::Result<CanvasHotkeyPlan>::failure(history.status());
|
|
}
|
|
plan.action = CanvasHotkeyAction::history;
|
|
plan.history = history.value();
|
|
plan.no_op = plan.history.no_op;
|
|
}
|
|
break;
|
|
case CanvasHotkeyKey::s:
|
|
if (state.ctrl_down) {
|
|
plan.action = CanvasHotkeyAction::save_document;
|
|
plan.save_intent = state.shift_down
|
|
? DocumentSaveIntent::save_dirty_version
|
|
: DocumentSaveIntent::save;
|
|
plan.no_op = false;
|
|
}
|
|
break;
|
|
case CanvasHotkeyKey::bracket_left:
|
|
plan.action = CanvasHotkeyAction::adjust_brush_size;
|
|
plan.brush_size_delta = -0.05F;
|
|
plan.no_op = false;
|
|
break;
|
|
case CanvasHotkeyKey::bracket_right:
|
|
plan.action = CanvasHotkeyAction::adjust_brush_size;
|
|
plan.brush_size_delta = 0.05F;
|
|
plan.no_op = false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return pp::foundation::Result<CanvasHotkeyPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_canvas_hotkey_plan(
|
|
const CanvasHotkeyPlan& plan,
|
|
CanvasHotkeyServices& services)
|
|
{
|
|
if (plan.no_op || plan.action == CanvasHotkeyAction::none) {
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
switch (plan.action) {
|
|
case CanvasHotkeyAction::select_tool:
|
|
return services.execute_tool(plan.tool);
|
|
case CanvasHotkeyAction::history:
|
|
return services.execute_history(plan.history);
|
|
case CanvasHotkeyAction::save_document:
|
|
services.save_document(plan.save_intent);
|
|
return pp::foundation::Status::success();
|
|
case CanvasHotkeyAction::toggle_ui:
|
|
services.toggle_ui();
|
|
return pp::foundation::Status::success();
|
|
case CanvasHotkeyAction::adjust_brush_size:
|
|
if (plan.brush_size_delta == 0.0F) {
|
|
return pp::foundation::Status::invalid_argument("brush-size hotkey plan must include a delta");
|
|
}
|
|
services.adjust_brush_size(plan.brush_size_delta);
|
|
return pp::foundation::Status::success();
|
|
case CanvasHotkeyAction::show_cursor:
|
|
services.show_cursor();
|
|
return pp::foundation::Status::success();
|
|
case CanvasHotkeyAction::none:
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
return pp::foundation::Status::invalid_argument("unknown canvas hotkey action");
|
|
}
|
|
|
|
} // namespace pp::app
|