Route app input dispatch through app core
This commit is contained in:
166
src/app_core/app_input.h
Normal file
166
src/app_core/app_input.h
Normal file
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
#include "foundation/result.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace pp::app {
|
||||
|
||||
struct AppPointerDispatchPlan {
|
||||
bool request_redraw = true;
|
||||
bool dispatch_designer_first = false;
|
||||
bool dispatch_main_if_not_consumed = false;
|
||||
float normalized_x = 0.0F;
|
||||
float normalized_y = 0.0F;
|
||||
};
|
||||
|
||||
struct AppInputDispatchPlan {
|
||||
bool request_redraw = true;
|
||||
bool dispatch_main = false;
|
||||
};
|
||||
|
||||
struct AppGestureDispatchPlan {
|
||||
bool request_redraw = true;
|
||||
bool dispatch_main = false;
|
||||
float normalized_x = 0.0F;
|
||||
float normalized_y = 0.0F;
|
||||
float distance = 0.0F;
|
||||
float distance_delta = 0.0F;
|
||||
float position_delta_x = 0.0F;
|
||||
float position_delta_y = 0.0F;
|
||||
};
|
||||
|
||||
struct AppKeyDispatchPlan {
|
||||
bool request_redraw = true;
|
||||
bool dispatch_main = false;
|
||||
bool set_key_down = false;
|
||||
bool sync_vr_camera_rotation = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status validate_input_zoom(float zoom)
|
||||
{
|
||||
if (!std::isfinite(zoom) || zoom <= 0.0F) {
|
||||
return pp::foundation::Status::invalid_argument("input zoom must be finite and positive");
|
||||
}
|
||||
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<AppPointerDispatchPlan> plan_app_pointer_dispatch(
|
||||
float x,
|
||||
float y,
|
||||
float zoom,
|
||||
bool has_designer_layout,
|
||||
bool has_main_layout)
|
||||
{
|
||||
const auto zoom_status = validate_input_zoom(zoom);
|
||||
if (!zoom_status.ok()) {
|
||||
return pp::foundation::Result<AppPointerDispatchPlan>::failure(zoom_status);
|
||||
}
|
||||
|
||||
if (!std::isfinite(x) || !std::isfinite(y)) {
|
||||
return pp::foundation::Result<AppPointerDispatchPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("input coordinates must be finite"));
|
||||
}
|
||||
|
||||
return pp::foundation::Result<AppPointerDispatchPlan>::success(AppPointerDispatchPlan {
|
||||
.request_redraw = true,
|
||||
.dispatch_designer_first = has_designer_layout,
|
||||
.dispatch_main_if_not_consumed = has_main_layout,
|
||||
.normalized_x = x / zoom,
|
||||
.normalized_y = y / zoom,
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr AppPointerDispatchPlan plan_app_mouse_cancel_dispatch(
|
||||
bool has_designer_layout,
|
||||
bool has_main_layout) noexcept
|
||||
{
|
||||
return AppPointerDispatchPlan {
|
||||
.request_redraw = true,
|
||||
.dispatch_designer_first = has_designer_layout,
|
||||
.dispatch_main_if_not_consumed = has_main_layout,
|
||||
.normalized_x = 0.0F,
|
||||
.normalized_y = 0.0F,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr AppInputDispatchPlan plan_app_main_input_dispatch(bool has_main_layout) noexcept
|
||||
{
|
||||
return AppInputDispatchPlan {
|
||||
.request_redraw = true,
|
||||
.dispatch_main = has_main_layout,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<AppGestureDispatchPlan> plan_app_gesture_dispatch(
|
||||
float x0,
|
||||
float y0,
|
||||
float x1,
|
||||
float y1,
|
||||
float previous_x0,
|
||||
float previous_y0,
|
||||
float previous_x1,
|
||||
float previous_y1,
|
||||
float zoom,
|
||||
bool has_main_layout)
|
||||
{
|
||||
const auto zoom_status = validate_input_zoom(zoom);
|
||||
if (!zoom_status.ok()) {
|
||||
return pp::foundation::Result<AppGestureDispatchPlan>::failure(zoom_status);
|
||||
}
|
||||
|
||||
if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(x1) || !std::isfinite(y1)
|
||||
|| !std::isfinite(previous_x0) || !std::isfinite(previous_y0)
|
||||
|| !std::isfinite(previous_x1) || !std::isfinite(previous_y1)) {
|
||||
return pp::foundation::Result<AppGestureDispatchPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("gesture coordinates must be finite"));
|
||||
}
|
||||
|
||||
const float midpoint_x = (x0 + x1) * 0.5F;
|
||||
const float midpoint_y = (y0 + y1) * 0.5F;
|
||||
const float previous_midpoint_x = (previous_x0 + previous_x1) * 0.5F;
|
||||
const float previous_midpoint_y = (previous_y0 + previous_y1) * 0.5F;
|
||||
const float dx = x1 - x0;
|
||||
const float dy = y1 - y0;
|
||||
const float previous_dx = previous_x1 - previous_x0;
|
||||
const float previous_dy = previous_y1 - previous_y0;
|
||||
const float distance = std::sqrt(dx * dx + dy * dy);
|
||||
const float previous_distance = std::sqrt(previous_dx * previous_dx + previous_dy * previous_dy);
|
||||
|
||||
return pp::foundation::Result<AppGestureDispatchPlan>::success(AppGestureDispatchPlan {
|
||||
.request_redraw = true,
|
||||
.dispatch_main = has_main_layout,
|
||||
.normalized_x = midpoint_x / zoom,
|
||||
.normalized_y = midpoint_y / zoom,
|
||||
.distance = distance,
|
||||
.distance_delta = distance - previous_distance,
|
||||
.position_delta_x = midpoint_x - previous_midpoint_x,
|
||||
.position_delta_y = midpoint_y - previous_midpoint_y,
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr AppKeyDispatchPlan plan_app_key_down_dispatch(
|
||||
bool has_main_layout,
|
||||
bool is_spacebar,
|
||||
bool vr_active) noexcept
|
||||
{
|
||||
return AppKeyDispatchPlan {
|
||||
.request_redraw = true,
|
||||
.dispatch_main = has_main_layout,
|
||||
.set_key_down = true,
|
||||
.sync_vr_camera_rotation = is_spacebar && vr_active,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr AppKeyDispatchPlan plan_app_key_up_dispatch(bool has_main_layout) noexcept
|
||||
{
|
||||
return AppKeyDispatchPlan {
|
||||
.request_redraw = true,
|
||||
.dispatch_main = has_main_layout,
|
||||
.set_key_down = false,
|
||||
.sync_vr_camera_rotation = false,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace pp::app
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "app_core/app_frame.h"
|
||||
#include "app_core/app_input.h"
|
||||
#include "app_core/document_platform_io.h"
|
||||
#include "app_core/document_sharing.h"
|
||||
#include "platform_api/platform_services.h"
|
||||
@@ -411,164 +412,249 @@ void App::save_prepared_file(
|
||||
|
||||
bool App::mouse_down(int button, float x, float y, float pressure, kEventSource source, bool eraser)
|
||||
{
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_pointer_dispatch(
|
||||
x,
|
||||
y,
|
||||
zoom,
|
||||
layout_designer.get(main_id) != nullptr,
|
||||
layout.get(main_id) != nullptr);
|
||||
if (!plan) {
|
||||
LOG("Mouse down dispatch plan failed: %s", plan.status().message);
|
||||
return false;
|
||||
}
|
||||
|
||||
redraw = plan.value().request_redraw;
|
||||
MouseEvent e;
|
||||
e.m_type = button ? kEventType::MouseDownR : kEventType::MouseDownL;
|
||||
e.m_pos = { x / zoom, y / zoom };
|
||||
e.m_pos = { plan.value().normalized_x, plan.value().normalized_y };
|
||||
e.m_pressure = pressure;
|
||||
e.m_source = source;
|
||||
e.m_eraser = eraser;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout_designer[main_id])
|
||||
if (auto* main = layout_designer[main_id]; plan.value().dispatch_designer_first && main)
|
||||
return main->on_event(&e) == kEventResult::Consumed;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.value().dispatch_main_if_not_consumed && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::mouse_move(float x, float y, float pressure, kEventSource source, bool eraser)
|
||||
{
|
||||
cursor = { x / zoom, y / zoom };
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_pointer_dispatch(
|
||||
x,
|
||||
y,
|
||||
zoom,
|
||||
layout_designer.get(main_id) != nullptr,
|
||||
layout.get(main_id) != nullptr);
|
||||
if (!plan) {
|
||||
LOG("Mouse move dispatch plan failed: %s", plan.status().message);
|
||||
return false;
|
||||
}
|
||||
|
||||
cursor = { plan.value().normalized_x, plan.value().normalized_y };
|
||||
redraw = plan.value().request_redraw;
|
||||
MouseEvent e;
|
||||
e.m_type = kEventType::MouseMove;
|
||||
e.m_pos = { x / zoom, y / zoom };
|
||||
e.m_pos = { plan.value().normalized_x, plan.value().normalized_y };
|
||||
e.m_pressure = pressure;
|
||||
e.m_source = source;
|
||||
e.m_eraser = eraser;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout_designer[main_id])
|
||||
if (auto* main = layout_designer[main_id]; plan.value().dispatch_designer_first && main)
|
||||
return main->on_event(&e) == kEventResult::Consumed;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.value().dispatch_main_if_not_consumed && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::mouse_up(int button, float x, float y, kEventSource source, bool eraser)
|
||||
{
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_pointer_dispatch(
|
||||
x,
|
||||
y,
|
||||
zoom,
|
||||
layout_designer.get(main_id) != nullptr,
|
||||
layout.get(main_id) != nullptr);
|
||||
if (!plan) {
|
||||
LOG("Mouse up dispatch plan failed: %s", plan.status().message);
|
||||
return false;
|
||||
}
|
||||
|
||||
redraw = plan.value().request_redraw;
|
||||
MouseEvent e;
|
||||
e.m_type = button ? kEventType::MouseUpR : kEventType::MouseUpL;
|
||||
e.m_pos = { x / zoom, y / zoom };
|
||||
e.m_pos = { plan.value().normalized_x, plan.value().normalized_y };
|
||||
e.m_source = source;
|
||||
e.m_eraser = eraser;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout_designer[main_id])
|
||||
if (auto* main = layout_designer[main_id]; plan.value().dispatch_designer_first && main)
|
||||
return main->on_event(&e) == kEventResult::Consumed;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.value().dispatch_main_if_not_consumed && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::mouse_scroll(float x, float y, float delta)
|
||||
{
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_pointer_dispatch(
|
||||
x,
|
||||
y,
|
||||
zoom,
|
||||
layout_designer.get(main_id) != nullptr,
|
||||
layout.get(main_id) != nullptr);
|
||||
if (!plan) {
|
||||
LOG("Mouse scroll dispatch plan failed: %s", plan.status().message);
|
||||
return false;
|
||||
}
|
||||
|
||||
redraw = plan.value().request_redraw;
|
||||
MouseEvent e;
|
||||
e.m_type = kEventType::MouseScroll;
|
||||
e.m_pos = { x / zoom, y / zoom };
|
||||
e.m_pos = { plan.value().normalized_x, plan.value().normalized_y };
|
||||
e.m_scroll_delta = delta;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout_designer[main_id])
|
||||
if (auto* main = layout_designer[main_id]; plan.value().dispatch_designer_first && main)
|
||||
return main->on_event(&e) == kEventResult::Consumed;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.value().dispatch_main_if_not_consumed && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::mouse_cancel(int button)
|
||||
{
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_mouse_cancel_dispatch(
|
||||
layout_designer.get(main_id) != nullptr,
|
||||
layout.get(main_id) != nullptr);
|
||||
redraw = plan.request_redraw;
|
||||
MouseEvent e;
|
||||
e.m_type = kEventType::MouseCancel;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout_designer[main_id])
|
||||
if (auto* main = layout_designer[main_id]; plan.dispatch_designer_first && main)
|
||||
return main->on_event(&e) == kEventResult::Consumed;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.dispatch_main_if_not_consumed && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::gesture_start(const glm::vec2& p0, const glm::vec2& p1)
|
||||
{
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_gesture_dispatch(
|
||||
p0.x,
|
||||
p0.y,
|
||||
p1.x,
|
||||
p1.y,
|
||||
p0.x,
|
||||
p0.y,
|
||||
p1.x,
|
||||
p1.y,
|
||||
zoom,
|
||||
layout.get(main_id) != nullptr);
|
||||
if (!plan) {
|
||||
LOG("Gesture start dispatch plan failed: %s", plan.status().message);
|
||||
return false;
|
||||
}
|
||||
|
||||
redraw = plan.value().request_redraw;
|
||||
GestureEvent e;
|
||||
glm::vec2 p = glm::lerp(p0, p1, 0.5f);
|
||||
e.m_type = kEventType::GestureStart;
|
||||
e.m_pos = p / glm::vec2(zoom);
|
||||
e.m_distance = glm::distance(p0, p1);
|
||||
e.m_pos = { plan.value().normalized_x, plan.value().normalized_y };
|
||||
e.m_distance = plan.value().distance;
|
||||
gesture_p0 = p0;
|
||||
gesture_p1 = p1;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.value().dispatch_main && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::gesture_move(const glm::vec2& p0, const glm::vec2& p1)
|
||||
{
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_gesture_dispatch(
|
||||
p0.x,
|
||||
p0.y,
|
||||
p1.x,
|
||||
p1.y,
|
||||
gesture_p0.x,
|
||||
gesture_p0.y,
|
||||
gesture_p1.x,
|
||||
gesture_p1.y,
|
||||
zoom,
|
||||
layout.get(main_id) != nullptr);
|
||||
if (!plan) {
|
||||
LOG("Gesture move dispatch plan failed: %s", plan.status().message);
|
||||
return false;
|
||||
}
|
||||
|
||||
redraw = plan.value().request_redraw;
|
||||
GestureEvent e;
|
||||
glm::vec2 p = glm::lerp(p0, p1, 0.5f);
|
||||
e.m_type = kEventType::GestureMove;
|
||||
e.m_pos = p / glm::vec2(zoom);
|
||||
e.m_distance = glm::distance(p0, p1);
|
||||
e.m_distance_delta = e.m_distance - glm::distance(gesture_p0, gesture_p1);
|
||||
e.m_pos_delta = p - glm::lerp(gesture_p0, gesture_p1, 0.5f);
|
||||
e.m_pos = { plan.value().normalized_x, plan.value().normalized_y };
|
||||
e.m_distance = plan.value().distance;
|
||||
e.m_distance_delta = plan.value().distance_delta;
|
||||
e.m_pos_delta = { plan.value().position_delta_x, plan.value().position_delta_y };
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.value().dispatch_main && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::gesture_end()
|
||||
{
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_main_input_dispatch(layout.get(main_id) != nullptr);
|
||||
redraw = plan.request_redraw;
|
||||
GestureEvent e;
|
||||
e.m_type = kEventType::GestureEnd;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.dispatch_main && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::touch_tap(const glm::vec2& pos, int fingers, int tap_count)
|
||||
{
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_main_input_dispatch(layout.get(main_id) != nullptr);
|
||||
redraw = plan.request_redraw;
|
||||
TouchEvent e;
|
||||
e.m_type = kEventType::TouchTap;
|
||||
e.m_finger_count = fingers;
|
||||
e.m_tap_count = tap_count;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.dispatch_main && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::key_down(kKey key)
|
||||
{
|
||||
if (key == kKey::KeySpacebar && vr_active)
|
||||
const auto plan = pp::app::plan_app_key_down_dispatch(
|
||||
layout.get(main_id) != nullptr,
|
||||
key == kKey::KeySpacebar,
|
||||
vr_active);
|
||||
if (plan.sync_vr_camera_rotation)
|
||||
canvas->m_canvas->m_cam_rot = vr_rot;
|
||||
redraw = true;
|
||||
keys[(int)key] = true;
|
||||
redraw = plan.request_redraw;
|
||||
keys[(int)key] = plan.set_key_down;
|
||||
KeyEvent e;
|
||||
e.m_type = kEventType::KeyDown;
|
||||
e.m_key = key;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.dispatch_main && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::key_up(kKey key)
|
||||
{
|
||||
redraw = true;
|
||||
keys[(int)key] = false;
|
||||
const auto plan = pp::app::plan_app_key_up_dispatch(layout.get(main_id) != nullptr);
|
||||
redraw = plan.request_redraw;
|
||||
keys[(int)key] = plan.set_key_down;
|
||||
KeyEvent e;
|
||||
e.m_type = kEventType::KeyUp;
|
||||
e.m_key = key;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.dispatch_main && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::key_char(char key)
|
||||
{
|
||||
redraw = true;
|
||||
const auto plan = pp::app::plan_app_main_input_dispatch(layout.get(main_id) != nullptr);
|
||||
redraw = plan.request_redraw;
|
||||
KeyEvent e;
|
||||
e.m_type = kEventType::KeyChar;
|
||||
e.m_char = key;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; plan.dispatch_main && main)
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user