Skip to main content

smudge_core/
input.rs

1//! The platform-independent input events an application receives.
2//!
3//! [`App::handle_event`](crate::app::App::handle_event) takes these events
4//! between frames. The windowing layer translates the events of the operating
5//! system into them, and tests inject them directly, so widget and application
6//! code never sees a windowing library. Positions are logical pixels, with the
7//! origin at the top left of the window.
8
9use crate::geometry::Point;
10
11/// One input event, translated from the windowing system or injected by a
12/// test.
13#[derive(Clone, Debug, PartialEq)]
14pub enum InputEvent {
15    /// The pointer moved to a new position in logical pixels.
16    PointerMoved {
17        /// The position of the pointer in logical pixels.
18        position: Point,
19    },
20    /// The pointer left the window.
21    PointerLeft,
22    /// A pointer button was pressed.
23    PointerDown {
24        /// The position of the pointer in logical pixels.
25        position: Point,
26        /// The button that was pressed.
27        button: PointerButton,
28    },
29    /// A pointer button was released.
30    PointerUp {
31        /// The position of the pointer in logical pixels.
32        position: Point,
33        /// The button that was released.
34        button: PointerButton,
35    },
36    /// The mouse wheel or touchpad scrolled.
37    ///
38    /// The delta is in logical pixels, positive for scrolling right and down.
39    Wheel {
40        /// How far the content scrolls, in logical pixels.
41        delta: Point,
42    },
43    /// A keyboard key was pressed or released.
44    Key(KeyEvent),
45    /// The input method changed its composing text.
46    ///
47    /// `cursor` is the byte range of the cursor or selection inside `text`,
48    /// or `None` when no cursor is shown.
49    ImePreedit {
50        /// The composing text.
51        text: String,
52        /// The byte range of the cursor inside the composing text.
53        cursor: Option<(usize, usize)>,
54    },
55    /// The input method committed text, which the editor inserts.
56    ImeCommit {
57        /// The committed text.
58        text: String,
59    },
60    /// The window gained or lost the input focus of the operating system.
61    WindowFocus(
62        /// Whether the window is focused.
63        bool,
64    ),
65    /// The keyboard modifiers changed.
66    ///
67    /// Some platforms report a change only after the key event that caused
68    /// it, so the modifiers a key event carries can lag behind. This event
69    /// keeps the current state for whatever reads it next, such as a click.
70    ModifiersChanged(
71        /// The modifiers held now.
72        Modifiers,
73    ),
74}
75
76/// One of the pointer buttons an event can refer to.
77#[derive(Copy, Clone, Debug, PartialEq, Eq)]
78pub enum PointerButton {
79    /// The main button, the left button on a right-handed mouse.
80    Primary,
81    /// The context menu button, the right button on a right-handed mouse.
82    Secondary,
83    /// The middle button.
84    Middle,
85}
86
87/// A keyboard key that was pressed or released.
88#[derive(Clone, Debug, PartialEq, Eq)]
89pub struct KeyEvent {
90    /// Which key the event refers to.
91    pub key: Key,
92    /// Whether the key is being pressed, as opposed to released.
93    pub pressed: bool,
94    /// Whether this event repeats a key the user holds down.
95    pub repeat: bool,
96    /// The keyboard modifiers held when the event happened.
97    pub modifiers: Modifiers,
98    /// The text the keypress produces, when it produces any.
99    pub text: Option<String>,
100}
101
102/// A key on the keyboard, by meaning rather than by position.
103///
104/// A printable key arrives as [`Key::Character`]. Everything the platform
105/// reports that has no variant here arrives as [`Key::Other`].
106#[derive(Clone, Debug, PartialEq, Eq)]
107pub enum Key {
108    /// A character key, already affected by the keyboard layout, such as `a`
109    /// or `?`.
110    Character(String),
111    /// The Enter or Return key.
112    Enter,
113    /// The Tab key.
114    Tab,
115    /// The Escape key.
116    Escape,
117    /// The Backspace key.
118    Backspace,
119    /// The Delete key.
120    Delete,
121    /// The left arrow key.
122    ArrowLeft,
123    /// The right arrow key.
124    ArrowRight,
125    /// The up arrow key.
126    ArrowUp,
127    /// The down arrow key.
128    ArrowDown,
129    /// The Home key.
130    Home,
131    /// The End key.
132    End,
133    /// The Page Up key.
134    PageUp,
135    /// The Page Down key.
136    PageDown,
137    /// The space bar.
138    Space,
139    /// A key with no variant of its own.
140    Other,
141}
142
143/// Whether a key event was taken by a handler.
144///
145/// A key handler returns this to decide whether the event keeps bubbling to
146/// the ancestors of the node it reached.
147#[derive(Copy, Clone, Debug, PartialEq, Eq)]
148pub enum Handled {
149    /// A handler acted on the event, so it stops bubbling.
150    Yes,
151    /// The handler ignored the event, so it bubbles to the parent.
152    No,
153}
154
155/// The keyboard modifiers held during an input event.
156#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
157pub struct Modifiers {
158    /// Whether Shift is held.
159    pub shift: bool,
160    /// Whether Control is held.
161    pub control: bool,
162    /// Whether Alt is held.
163    pub alt: bool,
164    /// Whether the Meta key is held, the Command key on macOS and the Super
165    /// key elsewhere.
166    pub meta: bool,
167}
168
169impl Modifiers {
170    /// Returns whether the platform's command modifier is held.
171    ///
172    /// This is `meta` on macOS and `control` everywhere else, so a shortcut
173    /// reads the same on every platform.
174    ///
175    /// # Examples
176    ///
177    /// ```
178    /// use smudge_core::input::Modifiers;
179    ///
180    /// let modifiers = Modifiers {
181    ///     control: true,
182    ///     ..Modifiers::default()
183    /// };
184    /// #[cfg(not(target_os = "macos"))]
185    /// assert!(modifiers.command());
186    /// ```
187    pub fn command(&self) -> bool {
188        #[cfg(target_os = "macos")]
189        {
190            self.meta
191        }
192        #[cfg(not(target_os = "macos"))]
193        {
194            self.control
195        }
196    }
197}
198
199#[cfg(test)]
200#[path = "input_tests.rs"]
201mod tests;