#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UiKey {
Tab,
LeftArrow,
RightArrow,
UpArrow,
DownArrow,
PageUp,
PageDown,
Home,
End,
Insert,
Delete,
Backspace,
Space,
Enter,
Escape,
A,
C,
V,
X,
Y,
Z,
F1,
F5,
F10,
F11,
}
pub(crate) fn egui_key_for(key: UiKey) -> egui::Key {
match key {
UiKey::Tab => egui::Key::Tab,
UiKey::LeftArrow => egui::Key::ArrowLeft,
UiKey::RightArrow => egui::Key::ArrowRight,
UiKey::UpArrow => egui::Key::ArrowUp,
UiKey::DownArrow => egui::Key::ArrowDown,
UiKey::PageUp => egui::Key::PageUp,
UiKey::PageDown => egui::Key::PageDown,
UiKey::Home => egui::Key::Home,
UiKey::End => egui::Key::End,
UiKey::Insert => egui::Key::Insert,
UiKey::Delete => egui::Key::Delete,
UiKey::Backspace => egui::Key::Backspace,
UiKey::Space => egui::Key::Space,
UiKey::Enter => egui::Key::Enter,
UiKey::Escape => egui::Key::Escape,
UiKey::A => egui::Key::A,
UiKey::C => egui::Key::C,
UiKey::V => egui::Key::V,
UiKey::X => egui::Key::X,
UiKey::Y => egui::Key::Y,
UiKey::Z => egui::Key::Z,
UiKey::F1 => egui::Key::F1,
UiKey::F5 => egui::Key::F5,
UiKey::F10 => egui::Key::F10,
UiKey::F11 => egui::Key::F11,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseButton {
Left,
Right,
Middle,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct UiModifiers {
pub alt: bool,
pub ctrl: bool,
pub shift: bool,
pub mac_cmd: bool,
pub command: bool,
}
impl UiModifiers {
fn egui(self) -> egui::Modifiers {
egui::Modifiers {
alt: self.alt,
ctrl: self.ctrl,
shift: self.shift,
mac_cmd: self.mac_cmd,
command: self.command,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum InputEvent {
MouseMotion { x: f32, y: f32 },
MouseButton { button: MouseButton, pressed: bool },
MouseWheel { x: f32, y: f32 },
TextInput(String),
Key { key: UiKey, down: bool },
ModifiersChanged(UiModifiers),
}
#[derive(Debug, Default)]
pub(crate) struct EguiInputState {
events: Vec<egui::Event>,
pointer_pos: Option<egui::Pos2>,
modifiers: egui::Modifiers,
}
impl EguiInputState {
pub(crate) fn apply_input(&mut self, event: &InputEvent) {
match event {
InputEvent::ModifiersChanged(modifiers) => {
self.modifiers = modifiers.egui();
}
InputEvent::MouseMotion { x, y } => {
let pos = egui::pos2(*x, *y);
self.pointer_pos = Some(pos);
self.events.push(egui::Event::PointerMoved(pos));
}
InputEvent::MouseButton { button, pressed } => {
if let Some(pos) = self.pointer_pos {
self.events.push(egui::Event::PointerButton {
pos,
button: egui_pointer_button_for(*button),
pressed: *pressed,
modifiers: self.modifiers,
});
}
}
InputEvent::MouseWheel { x, y } => {
self.events.push(egui::Event::MouseWheel {
unit: egui::MouseWheelUnit::Point,
delta: egui::vec2(*x, *y),
phase: egui::TouchPhase::Move,
modifiers: self.modifiers,
});
}
InputEvent::TextInput(text) => {
self.events.push(egui::Event::Text(text.clone()));
}
InputEvent::Key { key, down } => {
let key = egui_key_for(*key);
self.events.push(egui::Event::Key {
key,
physical_key: Some(key),
pressed: *down,
repeat: false,
modifiers: self.modifiers,
});
}
}
}
pub(crate) fn take_events(&mut self) -> Vec<egui::Event> {
std::mem::take(&mut self.events)
}
}
fn egui_pointer_button_for(button: MouseButton) -> egui::PointerButton {
match button {
MouseButton::Left => egui::PointerButton::Primary,
MouseButton::Right => egui::PointerButton::Secondary,
MouseButton::Middle => egui::PointerButton::Middle,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ui_key_escape_maps_to_egui_escape() {
let key = UiKey::Escape;
let egui_key = egui_key_for(key);
assert_eq!(egui_key, egui::Key::Escape);
}
#[test]
fn egui_input_state_records_text_and_key_events() {
let mut state = EguiInputState::default();
state.apply_input(&InputEvent::TextInput("A".to_string()));
state.apply_input(&InputEvent::Key {
key: UiKey::Enter,
down: true,
});
assert_eq!(
state.take_events(),
vec![
egui::Event::Text("A".to_string()),
egui::Event::Key {
key: egui::Key::Enter,
physical_key: Some(egui::Key::Enter),
pressed: true,
repeat: false,
modifiers: egui::Modifiers::default(),
},
]
);
}
#[test]
fn egui_input_state_records_pointer_button_at_last_position() {
let mut state = EguiInputState::default();
state.apply_input(&InputEvent::MouseMotion { x: 12.0, y: 34.0 });
state.apply_input(&InputEvent::MouseButton {
button: MouseButton::Left,
pressed: true,
});
assert_eq!(
state.take_events(),
vec![
egui::Event::PointerMoved(egui::pos2(12.0, 34.0)),
egui::Event::PointerButton {
pos: egui::pos2(12.0, 34.0),
button: egui::PointerButton::Primary,
pressed: true,
modifiers: egui::Modifiers::default(),
},
]
);
}
#[test]
fn egui_input_state_applies_current_modifiers_to_key_events() {
let mut state = EguiInputState::default();
let modifiers = UiModifiers {
ctrl: true,
shift: true,
command: true,
..Default::default()
};
state.apply_input(&InputEvent::ModifiersChanged(modifiers));
state.apply_input(&InputEvent::Key {
key: UiKey::C,
down: true,
});
assert_eq!(
state.take_events(),
vec![egui::Event::Key {
key: egui::Key::C,
physical_key: Some(egui::Key::C),
pressed: true,
repeat: false,
modifiers: egui::Modifiers {
ctrl: true,
shift: true,
command: true,
..Default::default()
},
}]
);
}
#[test]
fn egui_input_state_applies_current_modifiers_to_pointer_and_wheel_events() {
let mut state = EguiInputState::default();
state.apply_input(&InputEvent::MouseMotion { x: 12.0, y: 34.0 });
state.apply_input(&InputEvent::ModifiersChanged(UiModifiers {
alt: true,
..Default::default()
}));
state.apply_input(&InputEvent::MouseButton {
button: MouseButton::Left,
pressed: true,
});
state.apply_input(&InputEvent::MouseWheel { x: 1.0, y: -2.0 });
assert_eq!(
state.take_events(),
vec![
egui::Event::PointerMoved(egui::pos2(12.0, 34.0)),
egui::Event::PointerButton {
pos: egui::pos2(12.0, 34.0),
button: egui::PointerButton::Primary,
pressed: true,
modifiers: egui::Modifiers {
alt: true,
..Default::default()
},
},
egui::Event::MouseWheel {
unit: egui::MouseWheelUnit::Point,
delta: egui::vec2(1.0, -2.0),
phase: egui::TouchPhase::Move,
modifiers: egui::Modifiers {
alt: true,
..Default::default()
},
},
]
);
}
}