From 7b318887ee81fe655b2fad6f5f9838cbaf6e4880 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sat, 16 Jan 2021 23:18:51 +0100 Subject: [PATCH] Put input/output data types into mod egui::data --- egui/src/context.rs | 1 + egui/src/data/input.rs | 243 +++++++++++++++++++++++++ egui/src/data/mod.rs | 4 + egui/src/data/output.rs | 47 +++++ egui/src/{input.rs => input_state.rs} | 252 +------------------------- egui/src/lib.rs | 6 +- egui/src/memory.rs | 12 +- egui/src/types.rs | 48 ----- 8 files changed, 311 insertions(+), 302 deletions(-) create mode 100644 egui/src/data/input.rs create mode 100644 egui/src/data/mod.rs create mode 100644 egui/src/data/output.rs rename egui/src/{input.rs => input_state.rs} (60%) diff --git a/egui/src/context.rs b/egui/src/context.rs index 3cea398b..2e0d0103 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -7,6 +7,7 @@ use std::sync::{ use crate::{ animation_manager::AnimationManager, + data::output::Output, mutex::{Mutex, MutexGuard}, paint::{stats::*, text::Fonts, *}, *, diff --git a/egui/src/data/input.rs b/egui/src/data/input.rs new file mode 100644 index 00000000..0edddeba --- /dev/null +++ b/egui/src/data/input.rs @@ -0,0 +1,243 @@ +//! The input needed by Egui. + +use crate::math::*; + +/// What the integrations provides to Egui at the start of each frame. +/// +/// Set the values that make sense, leave the rest at their `Default::default()`. +/// +/// All coordinates are in points (logical pixels) with origin (0, 0) in the top left corner. +#[derive(Clone, Debug)] +pub struct RawInput { + /// Is the button currently down? + /// NOTE: Egui currently only supports the primary mouse button. + pub mouse_down: bool, + + /// Current position of the mouse in points. + pub mouse_pos: Option, + + /// How many points (logical pixels) the user scrolled + pub scroll_delta: Vec2, + + #[deprecated = "Use screen_rect instead: `Some(Rect::from_pos_size(Default::default(), vec2(window_width, window_height)))`"] + pub screen_size: Vec2, + + /// Position and size of the area that Egui should use. + /// Usually you would set this to + /// + /// `Some(Rect::from_pos_size(Default::default(), vec2(window_width, window_height)))`. + /// + /// but you could also constrain Egui to some smaller portion of your window if you like. + /// + /// `None` will be treated as "same as last frame", with the default being a very big area. + pub screen_rect: Option, + + /// Also known as device pixel ratio, > 1 for HDPI screens. + /// If text looks blurry on high resolution screens, you probably forgot to set this. + pub pixels_per_point: Option, + + /// Monotonically increasing time, in seconds. Relative to whatever. Used for animations. + /// If `None` is provided, Egui will assume a time delta of `predicted_dt` (default 1/60 seconds). + pub time: Option, + + /// Should be set to the expected time between frames when painting at vsync speeds. + /// The default for this is 1/60. + /// Can safely be left at its default value. + pub predicted_dt: f32, + + /// Which modifier keys are down at the start of the frame? + pub modifiers: Modifiers, + + /// In-order events received this frame + pub events: Vec, +} + +impl Default for RawInput { + fn default() -> Self { + #![allow(deprecated)] // for screen_size + Self { + mouse_down: false, + mouse_pos: None, + scroll_delta: Vec2::zero(), + screen_size: Default::default(), + screen_rect: None, + pixels_per_point: None, + time: None, + predicted_dt: 1.0 / 60.0, + modifiers: Modifiers::default(), + events: vec![], + } + } +} + +impl RawInput { + /// Helper: move volatile (deltas and events), clone the rest + pub fn take(&mut self) -> RawInput { + #![allow(deprecated)] // for screen_size + RawInput { + mouse_down: self.mouse_down, + mouse_pos: self.mouse_pos, + scroll_delta: std::mem::take(&mut self.scroll_delta), + screen_size: self.screen_size, + screen_rect: self.screen_rect, + pixels_per_point: self.pixels_per_point, + time: self.time, + predicted_dt: self.predicted_dt, + modifiers: self.modifiers, + events: std::mem::take(&mut self.events), + } + } +} + +/// An input event generated by the integration. +/// +/// This only covers events that Egui cares about. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Event { + /// The integration detected a "copy" event (e.g. Cmd+C). + Copy, + /// The integration detected a "cut" event (e.g. Cmd+X). + Cut, + /// Text input, e.g. via keyboard or paste action. + /// + /// When the user presses enter/return, do not send a `Text` (just [`Key::Enter`]). + Text(String), + Key { + key: Key, + pressed: bool, + modifiers: Modifiers, + }, +} + +/// State of the modifier keys. These must be fed to Egui. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct Modifiers { + /// Either of the alt keys are down (option ⌥ on Mac). + pub alt: bool, + /// Either of the control keys are down. + /// When checking for keyboard shortcuts, consider using [`Self::command`] instead. + pub ctrl: bool, + /// Either of the shift keys are down. + pub shift: bool, + /// The Mac ⌘ Command key. Should always be set to `false` on other platforms. + pub mac_cmd: bool, + /// On Windows and Linux, set this to the same value as `ctrl`. + /// On Mac, this should be set whenever one of the ⌘ Command keys are down (same as `mac_cmd`). + /// This is so that Egui can, for instance, select all text by checking for `command + A` + /// and it will work on both Mac and Windows. + pub command: bool, +} + +/// Keyboard keys. +/// +/// Includes all keys Egui is interested in (such as `Home` and `End`) +/// plus a few that are useful for detecting keyboard shortcuts. +/// +/// Many keys are omitted because they are not always physical keys (depending on keyboard language), e.g. `;` and `§`, +/// and are therefor unsuitable as keyboard shortcuts if you want your app to be portable. +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub enum Key { + ArrowDown, + ArrowLeft, + ArrowRight, + ArrowUp, + + Escape, + Tab, + Backspace, + Enter, + Space, + + Insert, + Delete, + Home, + End, + PageUp, + PageDown, + + /// Either from the main row or from the numpad. + Num0, + /// Either from the main row or from the numpad. + Num1, + /// Either from the main row or from the numpad. + Num2, + /// Either from the main row or from the numpad. + Num3, + /// Either from the main row or from the numpad. + Num4, + /// Either from the main row or from the numpad. + Num5, + /// Either from the main row or from the numpad. + Num6, + /// Either from the main row or from the numpad. + Num7, + /// Either from the main row or from the numpad. + Num8, + /// Either from the main row or from the numpad. + Num9, + + A, // Used for cmd+A (select All) + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, // Used for ctrl+K (delete text after cursor) + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, // Used for ctrl+U (delete text before cursor) + V, + W, // Used for ctrl+W (delete previous word) + X, + Y, + Z, // Used for cmd+Z (undo) +} + +impl RawInput { + pub fn ui(&self, ui: &mut crate::Ui) { + #![allow(deprecated)] // for screen_size + let Self { + mouse_down, + mouse_pos, + scroll_delta, + screen_size: _, + screen_rect, + pixels_per_point, + time, + predicted_dt, + modifiers, + events, + } = self; + + // TODO: simpler way to show values, e.g. `ui.value("Mouse Pos:", self.mouse_pos); + // TODO: `ui.style_mut().text_style = TextStyle::Monospace`; + ui.label(format!("mouse_down: {}", mouse_down)); + ui.label(format!("mouse_pos: {:.1?}", mouse_pos)); + ui.label(format!("scroll_delta: {:?} points", scroll_delta)); + ui.label(format!("screen_rect: {:?} points", screen_rect)); + ui.label(format!("pixels_per_point: {:?}", pixels_per_point)) + .on_hover_text( + "Also called HDPI factor.\nNumber of physical pixels per each logical pixel.", + ); + if let Some(time) = time { + ui.label(format!("time: {:.3} s", time)); + } else { + ui.label("time: None"); + } + ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt)); + ui.label(format!("modifiers: {:#?}", modifiers)); + ui.label(format!("events: {:?}", events)) + .on_hover_text("key presses etc"); + } +} diff --git a/egui/src/data/mod.rs b/egui/src/data/mod.rs new file mode 100644 index 00000000..5a4b3f1d --- /dev/null +++ b/egui/src/data/mod.rs @@ -0,0 +1,4 @@ +//! All the data sent between egui and the backend + +pub mod input; +pub mod output; diff --git a/egui/src/data/output.rs b/egui/src/data/output.rs new file mode 100644 index 00000000..ca3d5455 --- /dev/null +++ b/egui/src/data/output.rs @@ -0,0 +1,47 @@ +//! All the data egui returns to the backend at the end of each frame. + +/// What Egui emits each frame. +/// The backend should use this. +#[derive(Clone, Default)] +pub struct Output { + /// Set the cursor to this icon. + pub cursor_icon: CursorIcon, + + /// If set, open this url. + pub open_url: Option, + + /// Response to [`Event::Copy`] or [`Event::Cut`]. Ignore if empty. + pub copied_text: String, + + /// If `true`, Egui is requesting immediate repaint (i.e. on the next frame). + /// + /// This happens for instance when there is an animation, or if a user has called `Context::request_repaint()`. + /// + /// As an Egui user: don't set this value directly. + /// Call `Context::request_repaint()` instead and it will do so for you. + pub needs_repaint: bool, +} + +/// A mouse cursor icon. +/// +/// Egui emits a [`CursorIcon`] in [`Output`] each frame as a request to the integration. +#[derive(Clone, Copy)] +pub enum CursorIcon { + Default, + /// Pointing hand, used for e.g. web links + PointingHand, + ResizeHorizontal, + ResizeNeSw, + ResizeNwSe, + ResizeVertical, + Text, + /// Used when moving + Grab, + Grabbing, +} + +impl Default for CursorIcon { + fn default() -> Self { + Self::Default + } +} diff --git a/egui/src/input.rs b/egui/src/input_state.rs similarity index 60% rename from egui/src/input.rs rename to egui/src/input_state.rs index 9180c672..04966f15 100644 --- a/egui/src/input.rs +++ b/egui/src/input_state.rs @@ -1,100 +1,14 @@ -//! The input needed by Egui. - +use crate::{math::*, util::History}; use std::collections::HashSet; -use crate::{math::*, util::History}; +use crate::data::input::*; + +pub use crate::data::input::Key; /// If mouse moves more than this, it is no longer a click (but maybe a drag) -const MAX_CLICK_DIST: f32 = 6.0; +const MAX_CLICK_DIST: f32 = 6.0; // TODO: move to settings /// The new mouse press must come within this many seconds from previous mouse release -const MAX_CLICK_DELAY: f64 = 0.3; - -/// What the integrations provides to Egui at the start of each frame. -/// -/// Set the values that make sense, leave the rest at their `Default::default()`. -/// -/// All coordinates are in points (logical pixels) with origin (0, 0) in the top left corner. -#[derive(Clone, Debug)] -pub struct RawInput { - /// Is the button currently down? - /// NOTE: Egui currently only supports the primary mouse button. - pub mouse_down: bool, - - /// Current position of the mouse in points. - pub mouse_pos: Option, - - /// How many points (logical pixels) the user scrolled - pub scroll_delta: Vec2, - - #[deprecated = "Use screen_rect instead: `Some(Rect::from_pos_size(Default::default(), vec2(window_width, window_height)))`"] - pub screen_size: Vec2, - - /// Position and size of the area that Egui should use. - /// Usually you would set this to - /// - /// `Some(Rect::from_pos_size(Default::default(), vec2(window_width, window_height)))`. - /// - /// but you could also constrain Egui to some smaller portion of your window if you like. - /// - /// `None` will be treated as "same as last frame", with the default being a very big area. - pub screen_rect: Option, - - /// Also known as device pixel ratio, > 1 for HDPI screens. - /// If text looks blurry on high resolution screens, you probably forgot to set this. - pub pixels_per_point: Option, - - /// Monotonically increasing time, in seconds. Relative to whatever. Used for animations. - /// If `None` is provided, Egui will assume a time delta of `predicted_dt` (default 1/60 seconds). - pub time: Option, - - /// Should be set to the expected time between frames when painting at vsync speeds. - /// The default for this is 1/60. - /// Can safely be left at its default value. - pub predicted_dt: f32, - - /// Which modifier keys are down at the start of the frame? - pub modifiers: Modifiers, - - /// In-order events received this frame - pub events: Vec, -} - -impl Default for RawInput { - fn default() -> Self { - #![allow(deprecated)] // for screen_size - Self { - mouse_down: false, - mouse_pos: None, - scroll_delta: Vec2::zero(), - screen_size: Default::default(), - screen_rect: None, - pixels_per_point: None, - time: None, - predicted_dt: 1.0 / 60.0, - modifiers: Modifiers::default(), - events: vec![], - } - } -} - -impl RawInput { - /// Helper: move volatile (deltas and events), clone the rest - pub fn take(&mut self) -> RawInput { - #![allow(deprecated)] // for screen_size - RawInput { - mouse_down: self.mouse_down, - mouse_pos: self.mouse_pos, - scroll_delta: std::mem::take(&mut self.scroll_delta), - screen_size: self.screen_size, - screen_rect: self.screen_rect, - pixels_per_point: self.pixels_per_point, - time: self.time, - predicted_dt: self.predicted_dt, - modifiers: self.modifiers, - events: std::mem::take(&mut self.events), - } - } -} +const MAX_CLICK_DELAY: f64 = 0.3; // TODO: move to settings /// Input state that Egui updates each frame. #[derive(Clone, Debug)] @@ -217,122 +131,6 @@ impl Default for MouseInput { } } } - -/// An input event generated by the integration. -/// -/// This only covers events that Egui cares about. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Event { - /// The integration detected a "copy" event (e.g. Cmd+C). - Copy, - /// The integration detected a "cut" event (e.g. Cmd+X). - Cut, - /// Text input, e.g. via keyboard or paste action. - /// - /// When the user presses enter/return, do not send a `Text` (just [`Key::Enter`]). - Text(String), - Key { - key: Key, - pressed: bool, - modifiers: Modifiers, - }, -} - -/// State of the modifier keys. These must be fed to Egui. -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct Modifiers { - /// Either of the alt keys are down (option ⌥ on Mac). - pub alt: bool, - /// Either of the control keys are down. - /// When checking for keyboard shortcuts, consider using [`Self::command`] instead. - pub ctrl: bool, - /// Either of the shift keys are down. - pub shift: bool, - /// The Mac ⌘ Command key. Should always be set to `false` on other platforms. - pub mac_cmd: bool, - /// On Windows and Linux, set this to the same value as `ctrl`. - /// On Mac, this should be set whenever one of the ⌘ Command keys are down (same as `mac_cmd`). - /// This is so that Egui can, for instance, select all text by checking for `command + A` - /// and it will work on both Mac and Windows. - pub command: bool, -} - -/// Keyboard keys. -/// -/// Includes all keys Egui is interested in (such as `Home` and `End`) -/// plus a few that are useful for detecting keyboard shortcuts. -/// -/// Many keys are omitted because they are not always physical keys (depending on keyboard language), e.g. `;` and `§`, -/// and are therefor unsuitable as keyboard shortcuts if you want your app to be portable. -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] -pub enum Key { - ArrowDown, - ArrowLeft, - ArrowRight, - ArrowUp, - - Escape, - Tab, - Backspace, - Enter, - Space, - - Insert, - Delete, - Home, - End, - PageUp, - PageDown, - - /// Either from the main row or from the numpad. - Num0, - /// Either from the main row or from the numpad. - Num1, - /// Either from the main row or from the numpad. - Num2, - /// Either from the main row or from the numpad. - Num3, - /// Either from the main row or from the numpad. - Num4, - /// Either from the main row or from the numpad. - Num5, - /// Either from the main row or from the numpad. - Num6, - /// Either from the main row or from the numpad. - Num7, - /// Either from the main row or from the numpad. - Num8, - /// Either from the main row or from the numpad. - Num9, - - A, // Used for cmd+A (select All) - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, // Used for ctrl+K (delete text after cursor) - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, // Used for ctrl+U (delete text before cursor) - V, - W, // Used for ctrl+W (delete previous word) - X, - Y, - Z, // Used for cmd+Z (undo) -} - impl InputState { #[must_use] pub fn begin_frame(self, new: RawInput) -> InputState { @@ -512,44 +310,6 @@ impl MouseInput { } } -impl RawInput { - pub fn ui(&self, ui: &mut crate::Ui) { - #![allow(deprecated)] // for screen_size - let Self { - mouse_down, - mouse_pos, - scroll_delta, - screen_size: _, - screen_rect, - pixels_per_point, - time, - predicted_dt, - modifiers, - events, - } = self; - - // TODO: simpler way to show values, e.g. `ui.value("Mouse Pos:", self.mouse_pos); - // TODO: `ui.style_mut().text_style = TextStyle::Monospace`; - ui.label(format!("mouse_down: {}", mouse_down)); - ui.label(format!("mouse_pos: {:.1?}", mouse_pos)); - ui.label(format!("scroll_delta: {:?} points", scroll_delta)); - ui.label(format!("screen_rect: {:?} points", screen_rect)); - ui.label(format!("pixels_per_point: {:?}", pixels_per_point)) - .on_hover_text( - "Also called HDPI factor.\nNumber of physical pixels per each logical pixel.", - ); - if let Some(time) = time { - ui.label(format!("time: {:.3} s", time)); - } else { - ui.label("time: None"); - } - ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt)); - ui.label(format!("modifiers: {:#?}", modifiers)); - ui.label(format!("events: {:?}", events)) - .on_hover_text("key presses etc"); - } -} - impl InputState { pub fn ui(&self, ui: &mut crate::Ui) { let Self { diff --git a/egui/src/lib.rs b/egui/src/lib.rs index 89d79e4b..aa733119 100644 --- a/egui/src/lib.rs +++ b/egui/src/lib.rs @@ -81,9 +81,10 @@ mod animation_manager; pub mod containers; mod context; +mod data; pub(crate) mod grid; mod id; -mod input; +mod input_state; mod introspection; mod layers; mod layout; @@ -113,9 +114,10 @@ pub use epaint::{ pub use { containers::*, context::{Context, CtxRef}, + data::{input::*, output::*}, grid::Grid, id::Id, - input::*, + input_state::*, layers::*, layout::*, memory::Memory, diff --git a/egui/src/memory.rs b/egui/src/memory.rs index 3ec9b934..21e6c63a 100644 --- a/egui/src/memory.rs +++ b/egui/src/memory.rs @@ -130,8 +130,8 @@ impl Interaction { fn begin_frame( &mut self, - prev_input: &crate::input::InputState, - new_input: &crate::input::RawInput, + prev_input: &crate::input_state::InputState, + new_input: &crate::data::input::RawInput, ) { self.kb_focus_id_previous_frame = self.kb_focus_id; self.click_interest = false; @@ -150,8 +150,8 @@ impl Interaction { self.pressed_tab = false; self.pressed_shift_tab = false; for event in &new_input.events { - if let crate::input::Event::Key { - key: crate::input::Key::Tab, + if let crate::Event::Key { + key: crate::Key::Tab, pressed: true, modifiers, } = event @@ -169,8 +169,8 @@ impl Interaction { impl Memory { pub(crate) fn begin_frame( &mut self, - prev_input: &crate::input::InputState, - new_input: &crate::input::RawInput, + prev_input: &crate::input_state::InputState, + new_input: &crate::data::input::RawInput, ) { self.interaction.begin_frame(prev_input, new_input); diff --git a/egui/src/types.rs b/egui/src/types.rs index e6415a7f..f919c710 100644 --- a/egui/src/types.rs +++ b/egui/src/types.rs @@ -2,54 +2,6 @@ use crate::{lerp, math::Rect, Align, CtxRef, Id, LayerId, Ui}; // ---------------------------------------------------------------------------- -/// What Egui emits each frame. -/// The backend should use this. -#[derive(Clone, Default)] -// #[cfg_attr(feature = "persistence", derive(serde::Serialize))] -pub struct Output { - /// Set the cursor to this icon. - pub cursor_icon: CursorIcon, - - /// If set, open this url. - pub open_url: Option, - - /// Response to Event::Copy or Event::Cut. Ignore if empty. - pub copied_text: String, - - /// If `true`, Egui or a user is indicating that the UI needs immediate repaint (e.g. on the next frame). - /// This happens for instance when there is an animation, or if a user has called `Context::request_repaint()`. - /// Don't set this manually, but call `Context::request_repaint()` instead. - pub needs_repaint: bool, -} - -/// A mouse cursor icon. -/// -/// Egui emits a `CursorIcond` in [`Output`] each frame as a request to the integration. -#[derive(Clone, Copy)] -// #[cfg_attr(feature = "persistence", derive(serde::Serialize))] -// #[cfg_attr(feature = "persistence", serde(rename_all = "snake_case"))] -pub enum CursorIcon { - Default, - /// Pointing hand, used for e.g. web links - PointingHand, - ResizeHorizontal, - ResizeNeSw, - ResizeNwSe, - ResizeVertical, - Text, - /// Used when moving - Grab, - Grabbing, -} - -impl Default for CursorIcon { - fn default() -> Self { - Self::Default - } -} - -// ---------------------------------------------------------------------------- - /// The result of adding a widget to a [`Ui`]. /// /// This lets you know whether or not a widget has been clicked this frame.