Skip to main content

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}