use serde_derive::Deserialize; use crate::{math::*, movement_tracker::MovementTracker}; /// What the integration gives to the gui. /// All coordinates in emigui is in point/logical coordinates. #[derive(Clone, Debug, Default, Deserialize)] #[serde(default)] pub struct RawInput { /// Is the button currently down? pub mouse_down: bool, /// Current position of the mouse in points. pub mouse_pos: Option, /// How many pixels the user scrolled pub scroll_delta: Vec2, /// Size of the screen in points. /// TODO: this should be screen_rect for easy sandboxing. pub screen_size: Vec2, /// Also known as device pixel ratio, > 1 for HDPI screens. pub pixels_per_point: Option, /// Time in seconds. Relative to whatever. Used for animation. pub time: f64, /// Local time. Only used for the clock in the example app. pub seconds_since_midnight: Option, /// In-order events received this frame pub events: Vec, } /// What emigui maintains #[derive(Clone, Debug, Default)] pub struct InputState { /// The raw input we got this fraem pub raw: RawInput, pub mouse: MouseInput, /// How many pixels the user scrolled pub scroll_delta: Vec2, /// Size of the screen in points. pub screen_size: Vec2, /// Also known as device pixel ratio, > 1 for HDPI screens. pub pixels_per_point: f32, /// Time in seconds. Relative to whatever. Used for animation. pub time: f64, /// Time since last frame, in seconds. pub dt: f32, /// Local time. Only used for the clock in the example app. pub seconds_since_midnight: Option, /// In-order events received this frame pub events: Vec, } /// What emigui maintains #[derive(Clone, Debug)] pub struct MouseInput { /// Is the button currently down? /// true the frame when it is pressed, /// false the frame it is released. pub down: bool, /// The mouse went from !down to down pub pressed: bool, /// The mouse went from down to !down pub released: bool, /// Current position of the mouse in points. /// None for touch screens when finger is not down. pub pos: Option, /// Where did the current click/drag originate? pub press_origin: Option, /// If the mouse is down, will it register as a click when released? /// Set to true on mouse down, set to false when mouse moves too much. pub could_be_click: bool, /// How much the mouse moved compared to last frame, in points. pub delta: Vec2, /// Current velocity of mouse cursor. pub velocity: Vec2, /// Recent movement of the mouse. /// Used for calculating velocity of mouse pointer. pub pos_tracker: MovementTracker, } impl Default for MouseInput { fn default() -> Self { Self { down: false, pressed: false, released: false, pos: None, press_origin: None, could_be_click: false, delta: Vec2::zero(), velocity: Vec2::zero(), pos_tracker: MovementTracker::new(1000, 0.1), } } } #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Event { Copy, Cut, /// Text input, e.g. via keyboard or paste action Text(String), Key { key: Key, pressed: bool, }, } #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Key { Alt, Backspace, Control, Delete, Down, End, Escape, Home, Insert, Left, /// Windows key or Mac Command key Logo, PageDown, PageUp, Return, Right, Shift, // Space, Tab, Up, } impl InputState { #[must_use] pub fn begin_frame(self, new: RawInput) -> InputState { let mouse = self.mouse.begin_frame(&new); let dt = (new.time - self.raw.time) as f32; InputState { mouse, scroll_delta: new.scroll_delta, screen_size: new.screen_size, pixels_per_point: new.pixels_per_point.unwrap_or(1.0), time: new.time, dt, seconds_since_midnight: new.seconds_since_midnight, events: new.events.clone(), // TODO: remove clone() and use raw.events raw: new, } } } impl MouseInput { #[must_use] pub fn begin_frame(mut self, new: &RawInput) -> MouseInput { let delta = new .mouse_pos .and_then(|new| self.pos.map(|last| new - last)) .unwrap_or_default(); let pressed = !self.down && new.mouse_down; let mut press_origin = self.press_origin; let mut could_be_click = self.could_be_click; if pressed { press_origin = new.mouse_pos; could_be_click = true; } else if !self.down || self.pos.is_none() { press_origin = None; } if let (Some(press_origin), Some(mouse_pos)) = (new.mouse_pos, press_origin) { // If mouse moves more than this, it is no longer a click (but maybe a drag) const MAX_CLICK_DIST: f32 = 6.0; could_be_click &= press_origin.distance(mouse_pos) < MAX_CLICK_DIST; } else { could_be_click = false; } if let Some(mouse_pos) = new.mouse_pos { self.pos_tracker.add(new.time, mouse_pos); } else { // we do not clear the `mouse_tracker` here, because it is exactly when a finger has // released from the touch screen that we may want to assign a velocity to whatever // the user tried to throw } let velocity = self.pos_tracker.velocity_noew(new.time).unwrap_or_default(); MouseInput { down: new.mouse_down && new.mouse_pos.is_some(), pressed, released: self.down && !new.mouse_down, pos: new.mouse_pos, press_origin, could_be_click, delta, velocity, pos_tracker: self.pos_tracker, } } } impl RawInput { pub fn ui(&self, ui: &mut crate::Ui) { use crate::label; // TODO: simpler way to show values, e.g. `ui.value("Mouse Pos:", self.mouse_pos); // TODO: easily change default font! ui.add(label!("mouse_down: {}", self.mouse_down)); ui.add(label!("mouse_pos: {:.1?}", self.mouse_pos)); ui.add(label!("scroll_delta: {:?} points", self.scroll_delta)); ui.add(label!("screen_size: {:?} points", self.screen_size)); ui.add(label!("pixels_per_point: {:?}", self.pixels_per_point)) .tooltip_text( "Also called hdpi factor.\nNumber of physical pixels per each logical pixel.", ); ui.add(label!("time: {:.3} s", self.time)); ui.add(label!( "seconds_since_midnight: {:?} s", self.seconds_since_midnight )); ui.add(label!("events: {:?}", self.events)) .tooltip_text("key presses etc"); } } impl InputState { pub fn ui(&self, ui: &mut crate::Ui) { use crate::label; ui.collapsing("Raw Input", |ui| self.raw.ui(ui)); crate::containers::CollapsingHeader::new("mouse") .default_open(true) .show(ui, |ui| { self.mouse.ui(ui); }); ui.add(label!("scroll_delta: {:?} points", self.scroll_delta)); ui.add(label!("screen_size: {:?} points", self.screen_size)); ui.add(label!( "{} points for each physical pixel (hdpi factor)", self.pixels_per_point )); ui.add(label!("time: {:.3} s", self.time)); ui.add(label!("dt: {:.3} s", self.dt)); ui.add(label!( "seconds_since_midnight: {:?} s", self.seconds_since_midnight )); ui.add(label!("events: {:?}", self.events)) .tooltip_text("key presses etc"); } } impl MouseInput { pub fn ui(&self, ui: &mut crate::Ui) { use crate::label; ui.add(label!("down: {}", self.down)); ui.add(label!("pressed: {}", self.pressed)); ui.add(label!("released: {}", self.released)); ui.add(label!("pos: {:?}", self.pos)); ui.add(label!("press_origin: {:?}", self.press_origin)); ui.add(label!("could_be_click: {}", self.could_be_click)); ui.add(label!("delta: {:?}", self.delta)); ui.add(label!( "velocity: [{:3.0} {:3.0}] points/sec", self.velocity.x, self.velocity.y )); } }