use crate::{math::*, movement_tracker::MovementTracker};
const MAX_CLICK_DIST: f32 = 6.0;
const MAX_CLICK_DELAY: f64 = 0.3;
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "with_serde", serde(default))]
pub struct RawInput {
pub mouse_down: bool,
pub mouse_pos: Option<Pos2>,
pub scroll_delta: Vec2,
pub screen_size: Vec2,
pub pixels_per_point: Option<f32>,
pub time: f64,
pub seconds_since_midnight: Option<f64>,
pub events: Vec<Event>,
}
#[derive(Clone, Debug, Default)]
pub struct InputState {
pub raw: RawInput,
pub mouse: MouseInput,
pub scroll_delta: Vec2,
pub screen_size: Vec2,
pub pixels_per_point: f32,
pub time: f64,
pub dt: f32,
pub seconds_since_midnight: Option<f64>,
pub events: Vec<Event>,
}
#[derive(Clone, Debug)]
pub struct MouseInput {
pub down: bool,
pub pressed: bool,
pub released: bool,
pub could_be_click: bool,
pub click: bool,
pub double_click: bool,
pub last_click_time: f64,
pub pos: Option<Pos2>,
pub press_origin: Option<Pos2>,
pub delta: Vec2,
pub velocity: Vec2,
pub pos_tracker: MovementTracker<Pos2>,
}
impl Default for MouseInput {
fn default() -> Self {
Self {
down: false,
pressed: false,
released: false,
could_be_click: false,
click: false,
double_click: false,
last_click_time: std::f64::NEG_INFINITY,
pos: None,
press_origin: None,
delta: Vec2::zero(),
velocity: Vec2::zero(),
pos_tracker: MovementTracker::new(1000, 0.1),
}
}
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "with_serde", serde(rename_all = "snake_case"))]
pub enum Event {
Copy,
Cut,
Text(String),
Key {
key: Key,
pressed: bool,
},
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "with_serde", serde(rename_all = "snake_case"))]
pub enum Key {
Alt,
Backspace,
Control,
Delete,
Down,
End,
Escape,
Home,
Insert,
Left,
Logo,
PageDown,
PageUp,
Return,
Right,
Shift,
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(),
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 released = self.down && !new.mouse_down;
let click = released && self.could_be_click;
let double_click = click && (new.time - self.last_click_time) < MAX_CLICK_DELAY;
let mut press_origin = self.press_origin;
let mut could_be_click = self.could_be_click;
let mut last_click_time = self.last_click_time;
if click {
last_click_time = new.time
}
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) {
could_be_click &= press_origin.distance(mouse_pos) < MAX_CLICK_DIST;
} else {
could_be_click = false;
}
if self.pressed {
self.pos_tracker.clear();
}
if let Some(mouse_pos) = new.mouse_pos {
self.pos_tracker.add(new.time, mouse_pos);
} else {
}
self.pos_tracker.flush(new.time);
let velocity = if self.pos_tracker.len() >= 3 && self.pos_tracker.dt() > 0.01 {
self.pos_tracker.velocity().unwrap_or_default()
} else {
Vec2::default()
};
MouseInput {
down: new.mouse_down && new.mouse_pos.is_some(),
pressed,
released,
could_be_click,
click,
double_click,
last_click_time,
pos: new.mouse_pos,
press_origin,
delta,
velocity,
pos_tracker: self.pos_tracker,
}
}
}
impl RawInput {
pub fn ui(&self, ui: &mut crate::Ui) {
use crate::label;
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!("could_be_click: {}", self.could_be_click));
ui.add(label!("click: {}", self.click));
ui.add(label!("double_click: {}", self.double_click));
ui.add(label!("last_click_time: {:.3}", self.last_click_time));
ui.add(label!("pos: {:?}", self.pos));
ui.add(label!("press_origin: {:?}", self.press_origin));
ui.add(label!("delta: {:?}", self.delta));
ui.add(label!(
"velocity: [{:3.0} {:3.0}] points/sec",
self.velocity.x,
self.velocity.y
));
}
}