use egui::TouchPhase as EguiTouchPhase;
use egui::{Event as EguiEvent, MouseWheelUnit, Pos2, TouchDeviceId, TouchId};
use egui::{ImeEvent as EguiImeEvent, PointerButton as EguiPointerButton};
use crate::input::{
ImeEvent, ImeKind, InputEvent, Key, KeyEvent, Modifiers, MouseButton, MouseEvent,
PointerAction, PointerEvent, PointerKind, TouchEvent, TouchPhase, WheelUnit,
};
pub fn to_egui_event(event: &InputEvent, pixels_per_point: f32) -> Vec<EguiEvent> {
assert!(
pixels_per_point > 0.0,
"a pixel density must be positive to convert physical pixels into points"
);
match event {
InputEvent::Key(event) => key_events(*event),
InputEvent::Mouse(event) => mouse_events(event, pixels_per_point),
InputEvent::Pointer(event) => pointer_events(event, pixels_per_point),
InputEvent::Touch(event) => touch_events(*event, pixels_per_point),
InputEvent::Text(text) => vec![EguiEvent::Text(text.0.clone())],
InputEvent::Ime(event) => ime_events(event),
InputEvent::ModifiersChanged(modifiers) => {
vec![EguiEvent::ModifiersChanged(to_modifiers(*modifiers))]
}
InputEvent::FocusChanged(focused) => vec![EguiEvent::WindowFocused(*focused)],
InputEvent::Resized { .. } | InputEvent::ScaleFactorChanged { .. } => Vec::new(),
}
}
pub fn to_egui_events_into<'a>(
events: impl IntoIterator<Item = &'a InputEvent>,
pixels_per_point: f32,
out: &mut Vec<EguiEvent>,
) {
out.extend(
events
.into_iter()
.flat_map(|event| to_egui_event(event, pixels_per_point)),
);
}
fn key_events(event: KeyEvent) -> Vec<EguiEvent> {
let Some(key) = to_key(event.key) else {
return Vec::new();
};
vec![EguiEvent::Key {
key,
physical_key: Some(key),
pressed: event.pressed,
repeat: event.repeat,
modifiers: to_modifiers(event.modifiers),
}]
}
fn mouse_events(event: &MouseEvent, pixels_per_point: f32) -> Vec<EguiEvent> {
match event {
MouseEvent::Moved { position } => {
vec![EguiEvent::PointerMoved(to_pos(*position, pixels_per_point))]
}
MouseEvent::Button {
position,
button,
pressed,
modifiers,
} => {
let Some(button) = to_pointer_button(*button) else {
return Vec::new();
};
vec![EguiEvent::PointerButton {
pos: to_pos(*position, pixels_per_point),
button,
pressed: *pressed,
modifiers: to_modifiers(*modifiers),
}]
}
MouseEvent::Left => vec![EguiEvent::PointerGone],
MouseEvent::Wheel {
delta,
unit,
phase,
modifiers,
} => vec![EguiEvent::MouseWheel {
unit: to_wheel_unit(*unit),
delta: match unit {
WheelUnit::Pixel => egui::Vec2::new(delta[0], delta[1]) / pixels_per_point,
WheelUnit::Line | WheelUnit::Page => egui::Vec2::new(delta[0], delta[1]),
},
phase: to_touch_phase(*phase),
modifiers: to_modifiers(*modifiers),
}],
}
}
fn pointer_events(event: &PointerEvent, pixels_per_point: f32) -> Vec<EguiEvent> {
match event.action {
PointerAction::Zoom(delta) => return vec![EguiEvent::Zoom(delta)],
PointerAction::Rotate(delta) => return vec![EguiEvent::Rotate(delta)],
PointerAction::Moved
| PointerAction::Pressed
| PointerAction::Released { .. }
| PointerAction::Left => {}
}
let Some(position) = pointer_position(event, pixels_per_point) else {
return Vec::new();
};
match event.action {
PointerAction::Moved => vec![EguiEvent::PointerMoved(position)],
PointerAction::Pressed => vec![EguiEvent::PointerButton {
pos: position,
button: EguiPointerButton::Primary,
pressed: true,
modifiers: to_modifiers(event.modifiers),
}],
PointerAction::Released { .. } => vec![
EguiEvent::PointerButton {
pos: position,
button: EguiPointerButton::Primary,
pressed: false,
modifiers: to_modifiers(event.modifiers),
},
EguiEvent::PointerGone,
],
PointerAction::Left => vec![EguiEvent::PointerGone],
PointerAction::Zoom(_) | PointerAction::Rotate(_) => unreachable!("handled above"),
}
}
fn pointer_position(event: &PointerEvent, pixels_per_point: f32) -> Option<Pos2> {
match event.kind {
PointerKind::Mouse | PointerKind::Touch | PointerKind::Trackpad => return None,
PointerKind::Pen | PointerKind::Other => {}
}
Some(
event
.position
.map_or(Pos2::ZERO, |position| to_pos(position, pixels_per_point)),
)
}
fn touch_events(event: TouchEvent, pixels_per_point: f32) -> Vec<EguiEvent> {
let pos = to_pos(event.position, pixels_per_point);
let mut events = vec![EguiEvent::Touch {
device_id: TouchDeviceId(0),
id: TouchId(event.id),
phase: to_touch_phase(event.phase),
pos,
force: event.force,
}];
match event.phase {
TouchPhase::Started => {
events.push(EguiEvent::PointerMoved(pos));
events.push(EguiEvent::PointerButton {
pos,
button: EguiPointerButton::Primary,
pressed: true,
modifiers: Modifiers::default().into(),
});
}
TouchPhase::Moved => events.push(EguiEvent::PointerMoved(pos)),
TouchPhase::Ended | TouchPhase::Cancelled => {
events.push(EguiEvent::PointerButton {
pos,
button: EguiPointerButton::Primary,
pressed: false,
modifiers: Modifiers::default().into(),
});
events.push(EguiEvent::PointerGone);
}
}
events
}
fn ime_events(event: &ImeEvent) -> Vec<EguiEvent> {
let inner = match &event.kind {
ImeKind::Preedit { text, active_range } => EguiImeEvent::Preedit {
text: text.clone(),
active_range_chars: active_range
.clone()
.map(|range| bytes_to_chars(text, range)),
},
ImeKind::Commit(text) => EguiImeEvent::Commit(text.clone()),
ImeKind::DeleteSurrounding { before, after } => {
EguiImeEvent::DeleteSurrounding {
before_chars: *before,
after_chars: *after,
}
}
ImeKind::Enabled | ImeKind::Disabled => return Vec::new(),
};
vec![EguiEvent::Ime(inner)]
}
fn bytes_to_chars(text: &str, range: core::ops::Range<usize>) -> core::ops::Range<usize> {
let start = text
.char_indices()
.position(|(offset, _)| offset >= range.start)
.unwrap_or_else(|| text.chars().count());
let end = text
.char_indices()
.position(|(offset, _)| offset >= range.end)
.unwrap_or_else(|| text.chars().count());
start..end
}
fn to_pos(position: [f32; 2], pixels_per_point: f32) -> Pos2 {
Pos2::new(
position[0] / pixels_per_point,
position[1] / pixels_per_point,
)
}
fn to_modifiers(modifiers: Modifiers) -> egui::Modifiers {
modifiers.into()
}
impl From<Modifiers> for egui::Modifiers {
fn from(modifiers: Modifiers) -> Self {
Self {
alt: modifiers.alt,
ctrl: modifiers.ctrl,
shift: modifiers.shift,
mac_cmd: modifiers.mac_cmd,
command: modifiers.command,
}
}
}
fn to_wheel_unit(unit: WheelUnit) -> MouseWheelUnit {
match unit {
WheelUnit::Line => MouseWheelUnit::Line,
WheelUnit::Page => MouseWheelUnit::Page,
WheelUnit::Pixel => MouseWheelUnit::Point,
}
}
fn to_pointer_button(button: MouseButton) -> Option<EguiPointerButton> {
Some(match button {
MouseButton::Primary => EguiPointerButton::Primary,
MouseButton::Secondary => EguiPointerButton::Secondary,
MouseButton::Middle => EguiPointerButton::Middle,
MouseButton::Back => EguiPointerButton::Extra1,
MouseButton::Forward => EguiPointerButton::Extra2,
MouseButton::Other(_) => return None,
})
}
fn to_touch_phase(phase: TouchPhase) -> EguiTouchPhase {
match phase {
TouchPhase::Started => EguiTouchPhase::Start,
TouchPhase::Moved => EguiTouchPhase::Move,
TouchPhase::Ended => EguiTouchPhase::End,
TouchPhase::Cancelled => EguiTouchPhase::Cancel,
}
}
fn to_key(key: Key) -> Option<egui::Key> {
Some(match key {
Key::Escape => egui::Key::Escape,
Key::Space => egui::Key::Space,
Key::Enter => egui::Key::Enter,
Key::Tab => egui::Key::Tab,
Key::Backspace => egui::Key::Backspace,
Key::Delete => egui::Key::Delete,
Key::ArrowUp => egui::Key::ArrowUp,
Key::ArrowDown => egui::Key::ArrowDown,
Key::ArrowLeft => egui::Key::ArrowLeft,
Key::ArrowRight => egui::Key::ArrowRight,
Key::Home => egui::Key::Home,
Key::End => egui::Key::End,
Key::PageUp => egui::Key::PageUp,
Key::PageDown => egui::Key::PageDown,
Key::A => egui::Key::A,
Key::B => egui::Key::B,
Key::C => egui::Key::C,
Key::D => egui::Key::D,
Key::E => egui::Key::E,
Key::F => egui::Key::F,
Key::G => egui::Key::G,
Key::H => egui::Key::H,
Key::I => egui::Key::I,
Key::J => egui::Key::J,
Key::K => egui::Key::K,
Key::L => egui::Key::L,
Key::M => egui::Key::M,
Key::N => egui::Key::N,
Key::O => egui::Key::O,
Key::P => egui::Key::P,
Key::Q => egui::Key::Q,
Key::R => egui::Key::R,
Key::S => egui::Key::S,
Key::T => egui::Key::T,
Key::U => egui::Key::U,
Key::V => egui::Key::V,
Key::W => egui::Key::W,
Key::X => egui::Key::X,
Key::Y => egui::Key::Y,
Key::Z => egui::Key::Z,
Key::Num0 => egui::Key::Num0,
Key::Num1 => egui::Key::Num1,
Key::Num2 => egui::Key::Num2,
Key::Num3 => egui::Key::Num3,
Key::Num4 => egui::Key::Num4,
Key::Num5 => egui::Key::Num5,
Key::Num6 => egui::Key::Num6,
Key::Num7 => egui::Key::Num7,
Key::Num8 => egui::Key::Num8,
Key::Num9 => egui::Key::Num9,
Key::F1 => egui::Key::F1,
Key::F2 => egui::Key::F2,
Key::F3 => egui::Key::F3,
Key::F4 => egui::Key::F4,
Key::F5 => egui::Key::F5,
Key::F6 => egui::Key::F6,
Key::F7 => egui::Key::F7,
Key::F8 => egui::Key::F8,
Key::F9 => egui::Key::F9,
Key::F10 => egui::Key::F10,
Key::F11 => egui::Key::F11,
Key::F12 => egui::Key::F12,
Key::F13 => egui::Key::F13,
Key::F14 => egui::Key::F14,
Key::F15 => egui::Key::F15,
Key::F16 => egui::Key::F16,
Key::F17 => egui::Key::F17,
Key::F18 => egui::Key::F18,
Key::F19 => egui::Key::F19,
Key::F20 => egui::Key::F20,
Key::F21 => egui::Key::F21,
Key::F22 => egui::Key::F22,
Key::F23 => egui::Key::F23,
Key::F24 => egui::Key::F24,
Key::F25 => egui::Key::F25,
Key::F26 => egui::Key::F26,
Key::F27 => egui::Key::F27,
Key::F28 => egui::Key::F28,
Key::F29 => egui::Key::F29,
Key::F30 => egui::Key::F30,
Key::F31 => egui::Key::F31,
Key::F32 => egui::Key::F32,
Key::F33 => egui::Key::F33,
Key::F34 => egui::Key::F34,
Key::F35 => egui::Key::F35,
Key::Other(_) => return None,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::input::TextEvent;
fn at_one(event: InputEvent) -> Vec<EguiEvent> {
to_egui_event(&event, 1.0)
}
fn key(key: Key, pressed: bool) -> InputEvent {
InputEvent::Key(KeyEvent {
key,
pressed,
repeat: false,
modifiers: Modifiers::default(),
})
}
#[test]
fn a_key_press_carries_both_readings_of_the_key() {
let event = at_one(key(Key::W, true));
assert_eq!(
event,
vec![EguiEvent::Key {
key: egui::Key::W,
physical_key: Some(egui::Key::W),
pressed: true,
repeat: false,
modifiers: egui::Modifiers::default(),
}],
"egui's shortcuts read the logical key and a game control the \
physical one, so both are filled from the crate's physical key"
);
}
#[test]
fn a_key_release_and_repeat_survive_the_translation() {
let release = at_one(InputEvent::Key(KeyEvent {
key: Key::Space,
pressed: false,
repeat: true,
modifiers: Modifiers::default(),
}));
let EguiEvent::Key {
pressed, repeat, ..
} = release[0]
else {
panic!("a key event should translate to one egui key event");
};
assert!(!pressed && repeat, "both flags belong to egui's own event");
}
#[test]
fn every_named_key_has_a_counterpart() {
for key in every_named_key() {
assert!(to_key(key).is_some(), "{key:?} has no egui counterpart");
}
}
#[test]
fn an_unnamed_key_is_dropped_rather_than_misreported() {
assert!(at_one(key(Key::Other(0x58), true)).is_empty());
}
#[test]
fn an_unnamed_key_is_not_any_named_one() {
assert!(to_key(Key::Other(4)).is_none());
}
#[test]
fn a_mouse_position_is_converted_to_points() {
let events = to_egui_event(
&InputEvent::Mouse(MouseEvent::Moved {
position: [40.0, 20.0],
}),
2.0,
);
assert_eq!(
events,
vec![EguiEvent::PointerMoved(Pos2::new(20.0, 10.0))],
"the crate reports physical pixels and egui lays out in points"
);
}
#[test]
fn a_mouse_button_keeps_its_position_button_and_state() {
let events = to_egui_event(
&InputEvent::Mouse(MouseEvent::Button {
position: [10.0, 30.0],
button: MouseButton::Secondary,
pressed: true,
modifiers: Modifiers {
shift: true,
..Modifiers::default()
},
}),
1.0,
);
assert_eq!(
events,
vec![EguiEvent::PointerButton {
pos: Pos2::new(10.0, 30.0),
button: EguiPointerButton::Secondary,
pressed: true,
modifiers: egui::Modifiers {
shift: true,
..egui::Modifiers::default()
},
}]
);
}
#[test]
fn every_mouse_button_maps_to_its_own_egui_button() {
let buttons = [
(MouseButton::Primary, EguiPointerButton::Primary),
(MouseButton::Secondary, EguiPointerButton::Secondary),
(MouseButton::Middle, EguiPointerButton::Middle),
(MouseButton::Back, EguiPointerButton::Extra1),
(MouseButton::Forward, EguiPointerButton::Extra2),
];
for (ours, theirs) in buttons {
assert_eq!(to_pointer_button(ours), Some(theirs));
}
assert_ne!(
to_pointer_button(MouseButton::Back),
to_pointer_button(MouseButton::Forward)
);
assert_eq!(to_pointer_button(MouseButton::Other(7)), None);
}
#[test]
fn a_mouse_leaving_the_window_becomes_pointer_gone() {
assert_eq!(
at_one(InputEvent::Mouse(MouseEvent::Left)),
vec![EguiEvent::PointerGone]
);
}
#[test]
fn a_mouse_a_touch_and_a_trackpad_pointer_event_is_dropped_as_a_duplicate() {
for kind in [
PointerKind::Mouse,
PointerKind::Touch,
PointerKind::Trackpad,
] {
let event = InputEvent::Pointer(PointerEvent {
kind,
id: 0,
action: PointerAction::Moved,
position: Some([12.0, 24.0]),
modifiers: Modifiers::default(),
});
assert!(
at_one(event).is_empty(),
"a {kind:?} pointer event duplicates the event that already feeds egui"
);
}
}
#[test]
fn a_pen_pointer_event_reaches_egui() {
assert_eq!(
at_one(InputEvent::Pointer(PointerEvent {
kind: PointerKind::Pen,
id: 1,
action: PointerAction::Moved,
position: Some([12.0, 24.0]),
modifiers: Modifiers::default(),
})),
vec![EguiEvent::PointerMoved(Pos2::new(12.0, 24.0))]
);
assert_eq!(
at_one(InputEvent::Pointer(PointerEvent {
kind: PointerKind::Pen,
id: 1,
action: PointerAction::Pressed,
position: Some([1.0, 2.0]),
modifiers: Modifiers::default(),
})),
vec![EguiEvent::PointerButton {
pos: Pos2::new(1.0, 2.0),
button: EguiPointerButton::Primary,
pressed: true,
modifiers: egui::Modifiers::default(),
}]
);
assert_eq!(
at_one(InputEvent::Pointer(PointerEvent {
kind: PointerKind::Pen,
id: 1,
action: PointerAction::Released { cancelled: false },
position: Some([1.0, 2.0]),
modifiers: Modifiers::default(),
})),
vec![
EguiEvent::PointerButton {
pos: Pos2::new(1.0, 2.0),
button: EguiPointerButton::Primary,
pressed: false,
modifiers: egui::Modifiers::default(),
},
EguiEvent::PointerGone,
]
);
}
#[test]
fn a_pen_leaving_the_window_becomes_pointer_gone() {
assert_eq!(
at_one(InputEvent::Pointer(PointerEvent {
kind: PointerKind::Pen,
id: 1,
action: PointerAction::Left,
position: None,
modifiers: Modifiers::default(),
})),
vec![EguiEvent::PointerGone]
);
}
#[test]
fn a_wheel_line_delta_passes_through_unscaled() {
let events = at_one(InputEvent::Mouse(MouseEvent::Wheel {
delta: [1.0, -3.0],
unit: WheelUnit::Line,
phase: TouchPhase::Moved,
modifiers: Modifiers::default(),
}));
assert_eq!(
events,
vec![EguiEvent::MouseWheel {
unit: MouseWheelUnit::Line,
delta: egui::Vec2::new(1.0, -3.0),
phase: EguiTouchPhase::Move,
modifiers: egui::Modifiers::default(),
}]
);
}
#[test]
fn a_wheel_pixel_delta_is_converted_to_points() {
let events = to_egui_event(
&InputEvent::Mouse(MouseEvent::Wheel {
delta: [8.0, -16.0],
unit: WheelUnit::Pixel,
phase: TouchPhase::Started,
modifiers: Modifiers::default(),
}),
2.0,
);
assert_eq!(
events,
vec![EguiEvent::MouseWheel {
unit: MouseWheelUnit::Point,
delta: egui::Vec2::new(4.0, -8.0),
phase: EguiTouchPhase::Start,
modifiers: egui::Modifiers::default(),
}],
"egui measures a point-unit delta in points, so a pixel delta is scaled"
);
}
#[test]
fn zoom_and_rotate_pass_their_amount_through() {
assert_eq!(
at_one(InputEvent::Pointer(PointerEvent {
kind: PointerKind::Touch,
id: 1,
action: PointerAction::Zoom(1.5),
position: None,
modifiers: Modifiers::default(),
})),
vec![EguiEvent::Zoom(1.5)]
);
assert_eq!(
at_one(InputEvent::Pointer(PointerEvent {
kind: PointerKind::Touch,
id: 1,
action: PointerAction::Rotate(0.5),
position: None,
modifiers: Modifiers::default(),
})),
vec![EguiEvent::Rotate(0.5)]
);
}
#[test]
fn text_is_forwarded_as_its_own_event() {
assert_eq!(
at_one(InputEvent::Text(TextEvent("héllo".to_string()))),
vec![EguiEvent::Text("héllo".to_string())]
);
}
#[test]
fn modifiers_are_forwarded_with_command_intact() {
let modifiers = Modifiers {
ctrl: true,
command: true,
..Modifiers::default()
};
assert_eq!(
at_one(InputEvent::ModifiersChanged(modifiers)),
vec![EguiEvent::ModifiersChanged(egui::Modifiers {
ctrl: true,
command: true,
..egui::Modifiers::default()
})]
);
}
#[test]
fn a_touch_reports_itself_and_the_pointer() {
let start = at_one(InputEvent::Touch(TouchEvent {
id: 7,
phase: TouchPhase::Started,
position: [12.0, 24.0],
force: Some(0.5),
}));
assert_eq!(
start,
vec![
EguiEvent::Touch {
device_id: TouchDeviceId(0),
id: TouchId(7),
phase: EguiTouchPhase::Start,
pos: Pos2::new(12.0, 24.0),
force: Some(0.5),
},
EguiEvent::PointerMoved(Pos2::new(12.0, 24.0)),
EguiEvent::PointerButton {
pos: Pos2::new(12.0, 24.0),
button: EguiPointerButton::Primary,
pressed: true,
modifiers: egui::Modifiers::default(),
},
],
"a finger down must also press the pointer, or a tap hits nothing"
);
let end = at_one(InputEvent::Touch(TouchEvent {
id: 7,
phase: TouchPhase::Ended,
position: [12.0, 24.0],
force: None,
}));
assert_eq!(
end,
vec![
EguiEvent::Touch {
device_id: TouchDeviceId(0),
id: TouchId(7),
phase: EguiTouchPhase::End,
pos: Pos2::new(12.0, 24.0),
force: None,
},
EguiEvent::PointerButton {
pos: Pos2::new(12.0, 24.0),
button: EguiPointerButton::Primary,
pressed: false,
modifiers: egui::Modifiers::default(),
},
EguiEvent::PointerGone,
],
"the release must come before the pointer leaves"
);
}
#[test]
fn a_moved_touch_only_moves_the_pointer() {
let events = at_one(InputEvent::Touch(TouchEvent {
id: 1,
phase: TouchPhase::Moved,
position: [4.0, 5.0],
force: None,
}));
assert_eq!(events.len(), 2, "the touch and one pointer move");
assert!(matches!(events[1], EguiEvent::PointerMoved(_)));
}
#[test]
fn a_cancelled_touch_also_releases_the_pointer() {
let events = at_one(InputEvent::Touch(TouchEvent {
id: 1,
phase: TouchPhase::Cancelled,
position: [1.0, 1.0],
force: None,
}));
assert!(
matches!(events.last(), Some(EguiEvent::PointerGone)),
"a cancelled touch is not a hover"
);
}
#[test]
fn a_preedit_range_is_remeasured_in_characters() {
let text = "héllo";
let events = at_one(InputEvent::Ime(ImeEvent {
kind: ImeKind::Preedit {
text: text.to_string(),
active_range: Some(1..4),
},
}));
assert_eq!(
events,
vec![EguiEvent::Ime(EguiImeEvent::Preedit {
text: text.to_string(),
active_range_chars: Some(1..3),
})],
"egui counts characters where the crate counts bytes"
);
}
#[test]
fn a_preedit_without_a_range_keeps_it_absent() {
let events = at_one(InputEvent::Ime(ImeEvent {
kind: ImeKind::Preedit {
text: "ab".to_string(),
active_range: None,
},
}));
assert_eq!(
events,
vec![EguiEvent::Ime(EguiImeEvent::Preedit {
text: "ab".to_string(),
active_range_chars: None,
})]
);
}
#[test]
fn a_commit_and_a_delete_surrounding_reach_egui() {
assert_eq!(
at_one(InputEvent::Ime(ImeEvent {
kind: ImeKind::Commit("done".to_string()),
})),
vec![EguiEvent::Ime(EguiImeEvent::Commit("done".to_string()))]
);
assert_eq!(
at_one(InputEvent::Ime(ImeEvent {
kind: ImeKind::DeleteSurrounding {
before: 2,
after: 3,
},
})),
vec![EguiEvent::Ime(EguiImeEvent::DeleteSurrounding {
before_chars: 2,
after_chars: 3,
})]
);
}
#[test]
fn enabling_and_disabling_the_input_method_produce_nothing() {
assert!(
at_one(InputEvent::Ime(ImeEvent {
kind: ImeKind::Enabled
}))
.is_empty()
);
assert!(
at_one(InputEvent::Ime(ImeEvent {
kind: ImeKind::Disabled
}))
.is_empty()
);
}
#[test]
fn focus_changes_reach_egui() {
assert_eq!(
at_one(InputEvent::FocusChanged(false)),
vec![EguiEvent::WindowFocused(false)]
);
}
#[test]
fn a_frame_keeps_its_event_order() {
let events = vec![
InputEvent::Mouse(MouseEvent::Moved {
position: [1.0, 1.0],
}),
InputEvent::Mouse(MouseEvent::Button {
position: [1.0, 1.0],
button: MouseButton::Primary,
pressed: true,
modifiers: Modifiers::default(),
}),
InputEvent::Mouse(MouseEvent::Left),
];
let mut converted = Vec::new();
to_egui_events_into(&events, 1.0, &mut converted);
assert_eq!(
converted,
vec![
EguiEvent::PointerMoved(Pos2::new(1.0, 1.0)),
EguiEvent::PointerButton {
pos: Pos2::new(1.0, 1.0),
button: EguiPointerButton::Primary,
pressed: true,
modifiers: egui::Modifiers::default(),
},
EguiEvent::PointerGone,
]
);
}
#[test]
fn an_empty_frame_converts_to_nothing() {
let mut converted = Vec::new();
to_egui_events_into([], 1.0, &mut converted);
assert!(converted.is_empty());
}
#[test]
#[should_panic(expected = "must be positive")]
fn a_zero_density_is_rejected() {
let _ = to_egui_event(
&InputEvent::Mouse(MouseEvent::Moved {
position: [0.0, 0.0],
}),
0.0,
);
}
fn every_named_key() -> Vec<Key> {
let mut keys = vec![
Key::Escape,
Key::Space,
Key::Enter,
Key::Tab,
Key::Backspace,
Key::Delete,
Key::ArrowUp,
Key::ArrowDown,
Key::ArrowLeft,
Key::ArrowRight,
Key::Home,
Key::End,
Key::PageUp,
Key::PageDown,
];
keys.extend([
Key::A,
Key::B,
Key::C,
Key::D,
Key::E,
Key::F,
Key::G,
Key::H,
Key::I,
Key::J,
Key::K,
Key::L,
Key::M,
Key::N,
Key::O,
Key::P,
Key::Q,
Key::R,
Key::S,
Key::T,
Key::U,
Key::V,
Key::W,
Key::X,
Key::Y,
Key::Z,
]);
keys.extend([
Key::Num0,
Key::Num1,
Key::Num2,
Key::Num3,
Key::Num4,
Key::Num5,
Key::Num6,
Key::Num7,
Key::Num8,
Key::Num9,
]);
keys.extend([
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::F9,
Key::F10,
Key::F11,
Key::F12,
Key::F13,
Key::F14,
Key::F15,
Key::F16,
Key::F17,
Key::F18,
Key::F19,
Key::F20,
Key::F21,
Key::F22,
Key::F23,
Key::F24,
Key::F25,
Key::F26,
Key::F27,
Key::F28,
Key::F29,
Key::F30,
Key::F31,
Key::F32,
Key::F33,
Key::F34,
Key::F35,
]);
keys
}
}