use super::state::{InputState, MouseButton, ModifierKeys};
use super::touch::TouchState;
use super::widget_state::WidgetInputState;
use crate::platform::PlatformEvent;
#[derive(Debug)]
pub struct EventProcessor {
modifiers: ModifierKeys,
touch_active: bool,
last_event_time: f64,
active_touches: std::collections::HashMap<u64, (f64, f64)>,
}
impl Default for EventProcessor {
fn default() -> Self {
Self::new()
}
}
impl EventProcessor {
pub fn new() -> Self {
Self {
modifiers: ModifierKeys::default(),
touch_active: false,
last_event_time: 0.0,
active_touches: std::collections::HashMap::new(),
}
}
pub fn process(&mut self, event: &PlatformEvent, input: &mut InputState, time: f64) -> bool {
self.last_event_time = time;
match event {
PlatformEvent::WindowCreated
| PlatformEvent::WindowMoved { .. }
| PlatformEvent::WindowDestroyed
| PlatformEvent::ClipboardPaste { .. }
| PlatformEvent::FileDropped { .. }
| PlatformEvent::FileHovered { .. }
| PlatformEvent::FileCancelled
| PlatformEvent::Ime(_)
| PlatformEvent::ThemeChanged { .. }
| PlatformEvent::ScaleFactorChanged { .. } => false,
PlatformEvent::PointerMoved { x, y } => {
input.pointer.prev_pos = input.pointer.pos;
input.pointer.pos = Some((*x, *y));
true
}
PlatformEvent::PointerDown { x, y, button } => {
input.pointer.pos = Some((*x, *y));
input.pointer.button_down = Some(*button);
true
}
PlatformEvent::PointerUp { x, y, button } => {
input.pointer.pos = Some((*x, *y));
if input.pointer.button_down == Some(*button) {
input.pointer.button_down = None;
input.pointer.clicked = Some(*button);
}
true
}
PlatformEvent::PointerEntered => {
true
}
PlatformEvent::PointerLeft => {
input.pointer.pos = None;
input.pointer.button_down = None;
true
}
PlatformEvent::TouchStart { id, x, y } => {
self.touch_active = true;
self.active_touches.insert(*id, (*x, *y));
if input.multi_touch.is_none() {
input.multi_touch = Some(TouchState::new());
}
if let Some(ref mut touch) = input.multi_touch {
touch.update_touch(*id, *x, *y, time, None);
}
if self.active_touches.len() == 1 {
input.pointer.pos = Some((*x, *y));
input.pointer.button_down = Some(MouseButton::Left);
}
true
}
PlatformEvent::TouchMove { id, x, y } => {
if let Some(prev_pos) = self.active_touches.get_mut(id) {
*prev_pos = (*x, *y);
}
if let Some(ref mut touch) = input.multi_touch {
touch.update_touch(*id, *x, *y, time, None);
}
if self.active_touches.len() == 1 && self.active_touches.contains_key(id) {
input.pointer.prev_pos = input.pointer.pos;
input.pointer.pos = Some((*x, *y));
}
true
}
PlatformEvent::TouchEnd { id, x, y } => {
let was_single_touch = self.active_touches.len() == 1;
self.active_touches.remove(id);
if let Some(ref mut touch) = input.multi_touch {
touch.remove_touch(*id);
}
if was_single_touch {
input.pointer.pos = Some((*x, *y));
input.pointer.button_down = None;
input.pointer.clicked = Some(MouseButton::Left);
}
if self.active_touches.is_empty() {
self.touch_active = false;
}
true
}
PlatformEvent::TouchCancel { id } => {
self.active_touches.remove(id);
if let Some(ref mut touch) = input.multi_touch {
touch.remove_touch(*id);
}
if self.active_touches.is_empty() {
self.touch_active = false;
input.pointer.button_down = None;
}
true
}
PlatformEvent::Scroll { dx, dy } => {
input.scroll_delta = (*dx, *dy);
true
}
PlatformEvent::KeyDown { modifiers, .. } => {
self.modifiers = *modifiers;
input.modifiers = *modifiers;
true
}
PlatformEvent::KeyUp { modifiers, .. } => {
self.modifiers = *modifiers;
input.modifiers = *modifiers;
true
}
PlatformEvent::ModifiersChanged { modifiers } => {
self.modifiers = *modifiers;
input.modifiers = *modifiers;
false }
PlatformEvent::TextInput { .. } => {
true
}
PlatformEvent::WindowResized { .. } => true,
PlatformEvent::RedrawRequested => true,
PlatformEvent::WindowFocused(focused) => {
if !focused {
input.pointer.button_down = None;
input.modifiers = ModifierKeys::default();
}
true
}
PlatformEvent::WindowCloseRequested => false,
}
}
pub fn process_widget(&mut self, event: &PlatformEvent, widget_state: &mut WidgetInputState, _time: f64) {
match event {
PlatformEvent::PointerMoved { x, y } => {
widget_state.update_mouse(*x, *y);
}
PlatformEvent::PointerDown { .. } => {
}
PlatformEvent::PointerUp { .. } => {
}
_ => {}
}
}
pub fn modifiers(&self) -> ModifierKeys {
self.modifiers
}
pub fn touch_count(&self) -> usize {
self.active_touches.len()
}
pub fn has_active_touches(&self) -> bool {
!self.active_touches.is_empty()
}
pub fn reset(&mut self) {
self.modifiers = ModifierKeys::default();
self.touch_active = false;
self.active_touches.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pointer_move() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
processor.process(&PlatformEvent::PointerMoved { x: 100.0, y: 200.0 }, &mut input, 0.0);
assert_eq!(input.pointer.pos, Some((100.0, 200.0)));
}
#[test]
fn test_pointer_click() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
processor.process(&PlatformEvent::PointerDown { x: 100.0, y: 200.0, button: MouseButton::Left }, &mut input, 0.0);
assert_eq!(input.pointer.button_down, Some(MouseButton::Left));
processor.process(&PlatformEvent::PointerUp { x: 100.0, y: 200.0, button: MouseButton::Left }, &mut input, 0.1);
assert_eq!(input.pointer.button_down, None);
assert_eq!(input.pointer.clicked, Some(MouseButton::Left));
}
#[test]
fn test_touch_to_pointer() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
processor.process(&PlatformEvent::TouchStart { id: 1, x: 100.0, y: 200.0 }, &mut input, 0.0);
assert_eq!(input.pointer.pos, Some((100.0, 200.0)));
assert_eq!(input.pointer.button_down, Some(MouseButton::Left));
assert_eq!(processor.touch_count(), 1);
assert!(input.multi_touch.is_some());
assert_eq!(input.touch_count(), 1);
}
#[test]
fn test_multi_touch_tracking() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
processor.process(&PlatformEvent::TouchStart { id: 1, x: 100.0, y: 200.0 }, &mut input, 0.0);
processor.process(&PlatformEvent::TouchStart { id: 2, x: 200.0, y: 200.0 }, &mut input, 0.0);
assert_eq!(processor.touch_count(), 2);
assert!(processor.has_active_touches());
assert_eq!(input.touch_count(), 2);
}
#[test]
fn test_touch_end_generates_click() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
processor.process(&PlatformEvent::TouchStart { id: 1, x: 100.0, y: 200.0 }, &mut input, 0.0);
processor.process(&PlatformEvent::TouchEnd { id: 1, x: 100.0, y: 200.0 }, &mut input, 0.1);
assert_eq!(input.pointer.clicked, Some(MouseButton::Left));
assert_eq!(processor.touch_count(), 0);
}
#[test]
fn test_scroll() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
processor.process(&PlatformEvent::Scroll { dx: 0.0, dy: 10.0 }, &mut input, 0.0);
assert_eq!(input.scroll_delta, (0.0, 10.0));
}
#[test]
fn test_modifiers() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
let mods = ModifierKeys { ctrl: true, ..Default::default() };
processor.process(&PlatformEvent::ModifiersChanged { modifiers: mods }, &mut input, 0.0);
assert!(input.modifiers.ctrl);
assert!(processor.modifiers().ctrl);
}
#[test]
fn test_window_focus_clears_state() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
input.pointer.button_down = Some(MouseButton::Left);
input.modifiers.ctrl = true;
processor.process(&PlatformEvent::WindowFocused(false), &mut input, 0.0);
assert_eq!(input.pointer.button_down, None);
assert!(!input.modifiers.ctrl);
}
#[test]
fn test_pointer_left_clears_position() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
input.pointer.pos = Some((100.0, 200.0));
processor.process(&PlatformEvent::PointerLeft, &mut input, 0.0);
assert_eq!(input.pointer.pos, None);
}
#[test]
fn test_touch_cancel() {
let mut processor = EventProcessor::new();
let mut input = InputState::new();
processor.process(&PlatformEvent::TouchStart { id: 1, x: 100.0, y: 200.0 }, &mut input, 0.0);
processor.process(&PlatformEvent::TouchCancel { id: 1 }, &mut input, 0.1);
assert_eq!(processor.touch_count(), 0);
assert!(!processor.has_active_touches());
}
#[test]
fn test_reset() {
let mut processor = EventProcessor::new();
processor.modifiers.ctrl = true;
processor.active_touches.insert(1, (100.0, 200.0));
processor.touch_active = true;
processor.reset();
assert!(!processor.modifiers.ctrl);
assert_eq!(processor.touch_count(), 0);
assert!(!processor.touch_active);
}
}