use crate::input::{Devices, PointerDelta, Switch, WheelDelta};
use crate::math::Vec2;
#[cfg(feature = "ui")]
use crate::platform::WHEEL_RATE;
pub(crate) enum Driven {
Switched(Switch, bool),
Pointed(Vec2),
Moved(PointerDelta, f32),
Turned(WheelDelta, f32),
#[cfg(feature = "ui")]
Typed(String),
}
impl Driven {
pub(crate) fn drive(&self, devices: &mut Devices) {
match *self {
Self::Switched(control, down) => devices.press(control, down),
Self::Pointed(at) => devices.point_at(at),
Self::Moved(lane, pixels) => devices.move_pointer(lane.moving(pixels)),
Self::Turned(lane, notches) => devices.turn_wheel(lane.turning(notches)),
#[cfg(feature = "ui")]
Self::Typed(_) => {}
}
}
#[cfg(feature = "ui")]
pub(crate) fn ui_event(self, devices: &Devices, pixels_per_point: f32) -> Option<egui::Event> {
let modifiers = devices.modifiers();
let point =
|pixels: Vec2| egui::pos2(pixels.x / pixels_per_point, pixels.y / pixels_per_point);
match self {
Self::Switched(Switch::Key(key), pressed) => {
let key = key.ui_key()?;
match (pressed && modifiers.command, key) {
(true, egui::Key::C) => Some(egui::Event::Copy),
(true, egui::Key::X) => Some(egui::Event::Cut),
_ => Some(egui::Event::Key {
key,
physical_key: Some(key),
pressed,
repeat: false,
modifiers,
}),
}
}
Self::Switched(Switch::Mouse(button), pressed) => Some(egui::Event::PointerButton {
pos: point(devices.pointing_at()?),
button: button.ui_button(),
pressed,
modifiers,
}),
Self::Pointed(at) => Some(egui::Event::PointerMoved(point(at))),
Self::Turned(lane, notches) => {
let lines = WHEEL_RATE.lines(lane.turning(notches));
Some(egui::Event::MouseWheel {
unit: egui::MouseWheelUnit::Line,
delta: egui::vec2(lines.x, lines.y),
phase: egui::TouchPhase::Move,
modifiers,
})
}
Self::Moved(..) => None,
Self::Typed(text) => Some(egui::Event::Text(text)),
}
}
}
#[cfg(all(test, feature = "ui"))]
mod tests {
use super::*;
use crate::input::{Key, MouseButton, Pads};
fn controls() -> Devices {
Devices::new(Pads::silent(), crate::platform::DOUBLE_CLICK_INTERVAL)
}
fn ui_event(devices: &mut Devices, driven: Driven) -> Option<egui::Event> {
driven.drive(devices);
driven.ui_event(devices, 1.0)
}
#[test]
fn a_key_under_the_command_key_copies_and_cuts_where_a_window_would() {
let input = &mut controls();
assert!(
matches!(
ui_event(input, Driven::Switched(Switch::Key(Key::C), true)),
Some(egui::Event::Key { .. })
),
"a key on its own is the key"
);
ui_event(input, Driven::Switched(Switch::Key(Key::LeftControl), true));
assert_eq!(
ui_event(input, Driven::Switched(Switch::Key(Key::C), true)),
Some(egui::Event::Copy)
);
assert_eq!(
ui_event(input, Driven::Switched(Switch::Key(Key::X), true)),
Some(egui::Event::Cut)
);
assert!(
matches!(
ui_event(input, Driven::Switched(Switch::Key(Key::C), false)),
Some(egui::Event::Key { pressed: false, .. })
),
"and a release is the key either way"
);
}
#[test]
fn a_press_before_the_pointer_is_placed_reaches_the_ui_as_nothing() {
let input = &mut controls();
let press = || Driven::Switched(Switch::Mouse(MouseButton::Left), true);
assert_eq!(ui_event(input, press()), None, "the pointer is nowhere yet");
ui_event(input, Driven::Pointed(Vec2::new(12.0, 20.0)));
assert_eq!(
ui_event(input, press()),
Some(egui::Event::PointerButton {
pos: egui::pos2(12.0, 20.0),
button: egui::PointerButton::Primary,
pressed: true,
modifiers: egui::Modifiers::NONE,
}),
"and it lands where the pointer was placed"
);
}
#[test]
fn a_pointer_that_moves_reaches_the_ui_as_nothing() {
let input = &mut controls();
assert_eq!(
ui_event(input, Driven::Moved(PointerDelta::Sideways, 4.0)),
None
);
assert_eq!(ui_event(input, Driven::Moved(PointerDelta::Up, 4.0)), None);
}
fn scrolled(x: f32, y: f32) -> Option<egui::Event> {
Some(egui::Event::MouseWheel {
unit: egui::MouseWheelUnit::Line,
delta: egui::vec2(x, y),
phase: egui::TouchPhase::Move,
modifiers: egui::Modifiers::NONE,
})
}
#[test]
fn one_notch_reaches_the_ui_as_the_lines_a_desktop_window_reports_for_it() {
let input = &mut controls();
assert_eq!(
ui_event(input, Driven::Turned(WheelDelta::Up, 1.0)),
scrolled(0.0, 1.0),
"a roll away scrolls the way a window's own roll away does"
);
assert_eq!(
ui_event(input, Driven::Turned(WheelDelta::Sideways, 1.0)),
scrolled(-1.0, 0.0),
"and a tilt to the right the way a window's own tilt does"
);
}
}