feat(input): host-agnostic events and pan/zoom/cursor controller
This commit is contained in:
1 parent
606e40a9f1
commit
5ab854104a
6 files changed
+718
No files matched your search
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <plot/core/rect.hpp>
|
||||
#include <plot/core/types.hpp>
|
||||
#include <plot/input/events.hpp>
|
||||
|
||||
namespace plot {
|
||||
class Plot;
|
||||
}
|
||||
|
||||
namespace plot::input {
|
||||
|
||||
/// What a drag in progress is doing.
|
||||
enum class DragMode {
|
||||
None,
|
||||
Pan, ///< translate every range (left or right drag)
|
||||
BoxZoom, ///< a shift+left selection is open; the ranges change on release
|
||||
MoveCursor, ///< ctrl+left drag is moving `hovered_cursor()`
|
||||
AxisZoomX, ///< middle drag inside the x label strip; zooms x only
|
||||
AxisZoomY, ///< middle drag inside the y label strip; zooms y only
|
||||
};
|
||||
|
||||
/// Turns host events into plot gestures, knowing nothing about Qt, GTK, GLFW or
|
||||
/// ImGui -- Tasks 8-11 translate their native events into the value types above.
|
||||
///
|
||||
/// It is pure CPU and needs no GL context and no font: everything it reads comes
|
||||
/// from `Plot`'s layout, ranges and transforms. The controller owns no viewport
|
||||
/// of its own: set_viewport() forwards to `Plot::set_viewport` (and refreshes the
|
||||
/// layout) so the two can never disagree, and every gesture runs through
|
||||
/// `Plot`'s range setters, which clear `auto_scale` in one place.
|
||||
///
|
||||
/// Gesture table (grab-and-drag: the data follows the cursor):
|
||||
/// | gesture | mode | effect |
|
||||
/// |---|---|---|
|
||||
/// | left drag | `Pan` | translate both ranges |
|
||||
/// | right drag | `Pan` | same, for hosts that reserve left |
|
||||
/// | shift + left drag | `BoxZoom` | both ranges from the selection rect |
|
||||
/// | ctrl + left drag near a cursor | `MoveCursor` | move that cursor |
|
||||
/// | middle drag on the x label strip | `AxisZoomX` | x-only zoom |
|
||||
/// | middle drag on the y label strip | `AxisZoomY` | y-only zoom |
|
||||
/// | wheel (shift: x only, ctrl: y only) | -- | zoom about the pointer |
|
||||
/// | `+` / `-` | -- | zoom about the plot centre |
|
||||
/// | `f` | -- | auto-fit every axis |
|
||||
///
|
||||
/// Both axis-zoom drags read the vertical `dy` with the wheel's sign convention
|
||||
/// (dragging down zooms in), anchored at the pointer's data coordinate on the
|
||||
/// axis they zoom.
|
||||
///
|
||||
/// A drag that changes no range is harmless: a box-zoom selection under 4 px on
|
||||
/// either side is a click, and leaves the ranges alone.
|
||||
class Controller {
|
||||
public:
|
||||
explicit Controller(Plot& plot) noexcept : plot_(&plot) {}
|
||||
|
||||
/// Sets the plot's viewport (the controller keeps none of its own) and
|
||||
/// refreshes the layout, so gesture geometry and the host agree.
|
||||
void set_viewport(Rect px);
|
||||
|
||||
void on_pointer(const PointerEvent& e);
|
||||
void on_wheel(const WheelEvent& e);
|
||||
void on_key(const KeyEvent& e);
|
||||
|
||||
DragMode drag_mode() const noexcept { return mode_; }
|
||||
/// The current box-zoom selection in pixels; meaningful while BoxZoom.
|
||||
Rect selection_rect() const noexcept { return selection_; }
|
||||
/// The cursor under the pointer, or `kInvalid` when the pointer is outside the
|
||||
/// plot area or no cursor is within a few pixels.
|
||||
u64 hovered_cursor() const;
|
||||
|
||||
private:
|
||||
void begin_drag(const PointerEvent& e);
|
||||
void update_drag(const PointerEvent& e);
|
||||
void end_drag();
|
||||
|
||||
void pan(const PointerEvent& e);
|
||||
void box_zoom_update(const PointerEvent& e);
|
||||
void apply_box_zoom();
|
||||
void move_cursor(const PointerEvent& e);
|
||||
void axis_zoom(const PointerEvent& e);
|
||||
|
||||
/// The nearest visible cursor within the hit radius of (px, py), or kInvalid.
|
||||
/// kInvalid when (px, py) is outside the plot axes.
|
||||
u64 cursor_at(f32 px, f32 py) const;
|
||||
|
||||
/// Zoom one axis about `anchor` (a data coordinate) by `factor`, recomputing
|
||||
/// the range so the anchor keeps its screen position. Leaves `auto_scale` false.
|
||||
void zoom_x_axis(u32 axis, f64 anchor, f64 factor);
|
||||
void zoom_y_axis(u32 axis, f64 anchor, f64 factor);
|
||||
|
||||
Plot* plot_ = nullptr;
|
||||
DragMode mode_ = DragMode::None;
|
||||
Rect selection_{};
|
||||
f32 origin_x_ = 0.0f;
|
||||
f32 origin_y_ = 0.0f;
|
||||
u64 drag_cursor_ = kInvalid;
|
||||
bool has_pointer_ = false;
|
||||
f32 pointer_x_ = 0.0f;
|
||||
f32 pointer_y_ = 0.0f;
|
||||
};
|
||||
|
||||
} // namespace plot::input
|
||||
Reference in new issue
Block a user