#pragma once #include "foundation/result.h" #include #include 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; }; struct AppUiVisibilityTogglePlan { bool next_ui_visible = true; std::size_t first_panel_child_index = 1; std::size_t panel_child_count = 0; }; struct AppStylusAttachPlan { bool set_has_stylus = true; bool enable_canvas_touch_lock = 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 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::failure(zoom_status); } if (!std::isfinite(x) || !std::isfinite(y)) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("input coordinates must be finite")); } return pp::foundation::Result::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 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::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::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::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, }; } [[nodiscard]] inline pp::foundation::Result plan_app_ui_visibility_toggle( bool current_ui_visible, bool has_main_layout, std::size_t main_child_count, std::size_t panel_child_count) { if (!has_main_layout) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("UI toggle requires a main layout")); } if (main_child_count <= 1U) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("UI toggle requires a panel container child")); } return pp::foundation::Result::success(AppUiVisibilityTogglePlan { .next_ui_visible = !current_ui_visible, .first_panel_child_index = 1U, .panel_child_count = panel_child_count, }); } [[nodiscard]] constexpr AppStylusAttachPlan plan_app_stylus_attach(bool has_canvas) noexcept { return AppStylusAttachPlan { .set_has_stylus = true, .enable_canvas_touch_lock = has_canvas, }; } } // namespace pp::app