volren_core/interaction/events.rs
1//! Abstract input event types.
2//!
3//! The library is windowing-system agnostic. Consumers bridge their platform
4//! events (winit, SDL2, egui, etc.) to the abstract types defined here.
5
6// ── Mouse ─────────────────────────────────────────────────────────────────────
7
8/// Mouse button identifier.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10#[non_exhaustive]
11pub enum MouseButton {
12 /// Left (primary) button.
13 Left,
14 /// Right (secondary) button.
15 Right,
16 /// Middle (scroll wheel) button.
17 Middle,
18}
19
20/// The specific mouse action that occurred.
21#[derive(Debug, Clone, Copy, PartialEq)]
22#[non_exhaustive]
23pub enum MouseEventKind {
24 /// A button was pressed.
25 Press(MouseButton),
26 /// A button was released.
27 Release(MouseButton),
28 /// The cursor moved.
29 Move,
30 /// Mouse wheel scrolled. Positive = scroll up (zoom in convention).
31 Scroll(f64),
32}
33
34/// Abstract mouse event.
35///
36/// All positions are in **logical pixels** relative to the viewport origin
37/// (top-left). Sub-pixel precision is preserved as `f64`.
38#[derive(Debug, Clone, Copy, PartialEq)]
39pub struct MouseEvent {
40 /// Cursor position in logical pixels.
41 pub position: (f64, f64),
42 /// What kind of mouse action occurred.
43 pub kind: MouseEventKind,
44 /// Modifier keys held during this event.
45 pub modifiers: Modifiers,
46}
47
48// ── Keyboard ──────────────────────────────────────────────────────────────────
49
50/// Platform-independent key identifier.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
52#[non_exhaustive]
53pub enum Key {
54 /// Reset.
55 R,
56 /// X axis.
57 X,
58 /// Y axis.
59 Y,
60 /// Z axis.
61 Z,
62 /// Arrow up.
63 Up,
64 /// Arrow down.
65 Down,
66 /// Arrow left.
67 Left,
68 /// Arrow right.
69 Right,
70 /// Zoom in / increase.
71 Plus,
72 /// Zoom out / decrease.
73 Minus,
74 /// Cancel / exit.
75 Escape,
76 /// Any other printable character.
77 Char(char),
78}
79
80/// Abstract keyboard event.
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82#[non_exhaustive]
83pub enum KeyEvent {
84 /// A key was pressed.
85 Pressed {
86 /// Which key.
87 key: Key,
88 },
89 /// A key was released.
90 Released {
91 /// Which key.
92 key: Key,
93 },
94}
95
96/// Modifier keys held during an event.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
98pub struct Modifiers {
99 /// Shift key held.
100 pub shift: bool,
101 /// Control key held (Cmd on macOS).
102 pub ctrl: bool,
103 /// Alt/Option key held.
104 pub alt: bool,
105}
106
107// ── Context ───────────────────────────────────────────────────────────────────
108
109/// Viewport and volume context passed alongside every event.
110#[derive(Debug, Clone, Copy)]
111pub struct InteractionContext {
112 /// Viewport width in logical pixels.
113 pub viewport_width: f64,
114 /// Viewport height in logical pixels.
115 pub viewport_height: f64,
116 /// Volume bounding box, if a volume is loaded.
117 pub volume_bounds: Option<crate::math::Aabb>,
118}
119
120impl InteractionContext {
121 /// Aspect ratio (width / height).
122 #[must_use]
123 pub fn aspect(&self) -> f64 {
124 if self.viewport_height > 0.0 {
125 self.viewport_width / self.viewport_height
126 } else {
127 1.0
128 }
129 }
130}
131
132// ── Result ────────────────────────────────────────────────────────────────────
133
134/// Result of processing an interaction event.
135///
136/// Tells the consumer what changed so it can decide whether to re-render.
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
138pub struct InteractionResult {
139 /// The camera was modified.
140 pub camera_changed: bool,
141 /// The window/level was modified (for 2D slice styles).
142 pub window_level_changed: bool,
143 /// The slice plane was modified.
144 pub slice_changed: bool,
145 /// Convenience: true if any of the above changed.
146 pub needs_redraw: bool,
147}
148
149impl InteractionResult {
150 /// Convenience: only the camera changed.
151 #[must_use]
152 pub fn camera_only() -> Self {
153 Self {
154 camera_changed: true,
155 needs_redraw: true,
156 ..Self::default()
157 }
158 }
159
160 /// Convenience: nothing changed.
161 #[must_use]
162 pub fn nothing() -> Self {
163 Self::default()
164 }
165
166 /// Convenience: window/level changed.
167 #[must_use]
168 pub fn window_level_only() -> Self {
169 Self {
170 window_level_changed: true,
171 needs_redraw: true,
172 ..Self::default()
173 }
174 }
175
176 /// Convenience: slice changed.
177 #[must_use]
178 pub fn slice_only() -> Self {
179 Self {
180 slice_changed: true,
181 needs_redraw: true,
182 ..Self::default()
183 }
184 }
185}