Skip to content

Commit

Permalink
Implement DeviceEvent::Button
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda committed Jun 13, 2023
1 parent 876107e commit e5e322a
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 35 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- On Web, use `Window.requestIdleCallback()` for `ControlFlow::Poll` when available.
- On Web, respect `EventLoopWindowTarget::listen_device_events()` settings.
- On Web, fix `DeviceEvent::MouseMotion` only being emitted for each canvas instead of the whole window.
- On Web, add `DeviceEvent::Motion` and `DeviceEvent::MouseWheel` support.
- On Web, add `DeviceEvent::Motion`, `DeviceEvent::MouseWheel` and `DeviceEvent::Button` support.

# 0.28.6

Expand Down
90 changes: 82 additions & 8 deletions src/platform_impl/web/event_loop/runner.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::super::DeviceId;
use super::{super::ScaleChangeArgs, backend, state::State};
use crate::event::{DeviceEvent, DeviceId as RootDeviceId, Event, StartCause};
use crate::event::{DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, StartCause};
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::platform_impl::platform::backend::EventListenerHandle;
use crate::window::WindowId;
Expand Down Expand Up @@ -28,6 +28,8 @@ impl<T> Clone for Shared<T> {
}
}

type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;

pub struct Execution<T: 'static> {
runner: RefCell<RunnerEnum<T>>,
events: RefCell<VecDeque<Event<'static, T>>>,
Expand All @@ -39,10 +41,10 @@ pub struct Execution<T: 'static> {
scale_change_detector: RefCell<Option<backend::ScaleChangeDetector>>,
unload_event_handle: RefCell<Option<backend::UnloadEventHandle>>,
device_events: Cell<DeviceEvents>,
#[allow(clippy::type_complexity)]
on_mouse_move: RefCell<Option<EventListenerHandle<dyn FnMut(PointerEvent)>>>,
#[allow(clippy::type_complexity)]
on_wheel: RefCell<Option<EventListenerHandle<dyn FnMut(WheelEvent)>>>,
on_mouse_move: OnEventHandle<PointerEvent>,
on_wheel: OnEventHandle<WheelEvent>,
on_press: OnEventHandle<PointerEvent>,
on_release: OnEventHandle<PointerEvent>,
}

enum RunnerEnum<T: 'static> {
Expand Down Expand Up @@ -124,6 +126,8 @@ impl<T: 'static> Shared<T> {
device_events: Cell::default(),
on_mouse_move: RefCell::new(None),
on_wheel: RefCell::new(None),
on_press: RefCell::new(None),
on_release: RefCell::new(None),
}))
}

Expand Down Expand Up @@ -169,17 +173,39 @@ impl<T: 'static> Shared<T> {
return;
}

if event.pointer_type() != "mouse" {
let pointer_type = event.pointer_type();

if pointer_type != "mouse" {
return;
}

// chorded button event
if backend::event::mouse_button(&event).is_some() {
let device_id = RootDeviceId(DeviceId(event.pointer_id()));

if let Some(button) = backend::event::mouse_button(&event) {
debug_assert_eq!(
pointer_type, "mouse",
"expect pointer type of a chorded button event to be a mouse"
);

let state = if backend::event::mouse_buttons(&event).contains(button.into()) {
ElementState::Pressed
} else {
ElementState::Released
};

runner.send_event(Event::DeviceEvent {
device_id,
event: DeviceEvent::Button {
button: button.to_id(),
state,
},
});

return;
}

// pointer move event
let device_id = RootDeviceId(DeviceId(event.pointer_id()));
let mut delta = backend::event::MouseDelta::init(&window, &event);
runner.send_events(backend::event::pointer_move_event(event).flat_map(|event| {
let delta = delta
Expand Down Expand Up @@ -232,6 +258,52 @@ impl<T: 'static> Shared<T> {
}
}),
));
let runner = self.clone();
*self.0.on_press.borrow_mut() = Some(EventListenerHandle::new(
self.window(),
"pointerdown",
Closure::new(move |event: PointerEvent| {
if !runner.device_events() {
return;
}

if event.pointer_type() != "mouse" {
return;
}

let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
runner.send_event(Event::DeviceEvent {
device_id: RootDeviceId(DeviceId(event.pointer_id())),
event: DeviceEvent::Button {
button: button.to_id(),
state: ElementState::Pressed,
},
});
}),
));
let runner = self.clone();
*self.0.on_release.borrow_mut() = Some(EventListenerHandle::new(
self.window(),
"pointerup",
Closure::new(move |event: PointerEvent| {
if !runner.device_events() {
return;
}

if event.pointer_type() != "mouse" {
return;
}

let button = backend::event::mouse_button(&event).expect("no mouse button pressed");
runner.send_event(Event::DeviceEvent {
device_id: RootDeviceId(DeviceId(event.pointer_id())),
event: DeviceEvent::Button {
button: button.to_id(),
state: ElementState::Released,
},
});
}),
));
}

pub(crate) fn set_on_scale_change<F>(&self, handler: F)
Expand Down Expand Up @@ -573,6 +645,8 @@ impl<T: 'static> Shared<T> {
*self.0.unload_event_handle.borrow_mut() = None;
*self.0.on_mouse_move.borrow_mut() = None;
*self.0.on_wheel.borrow_mut() = None;
*self.0.on_press.borrow_mut() = None;
*self.0.on_release.borrow_mut() = None;
// Dropping the `Runner` drops the event handler closure, which will in
// turn drop all `Window`s moved into the closure.
*self.0.runner.borrow_mut() = RunnerEnum::Destroyed;
Expand Down
73 changes: 47 additions & 26 deletions src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,38 +384,41 @@ impl<T> EventLoopWindowTarget<T> {
}
});

let button_event = if buttons.contains(button.into()) {
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)),
state: ElementState::Pressed,
button,
},
}
let device_id = RootDeviceId(DeviceId(pointer_id));

let state = if buttons.contains(button.into()) {
ElementState::Pressed
} else {
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)),
state: ElementState::Released,
button,
},
}
ElementState::Released
};

let device_event = runner.device_events().then(|| Event::DeviceEvent {
device_id,
event: DeviceEvent::Button {
button: button.to_id(),
state,
},
});

// A chorded button event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position.
runner.send_events(modifiers.into_iter().chain([
runner.send_events(modifiers.into_iter().chain(device_event).chain([
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved {
device_id: RootDeviceId(DeviceId(pointer_id)),
device_id,
position,
},
},
button_event,
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id,
state,
button,
},
},
]));
}
},
Expand Down Expand Up @@ -450,21 +453,30 @@ impl<T> EventLoopWindowTarget<T> {
}
});

let device_id: RootDeviceId = RootDeviceId(DeviceId(pointer_id));
let device_event = runner.device_events().then(|| Event::DeviceEvent {
device_id,
event: DeviceEvent::Button {
button: button.to_id(),
state: ElementState::Pressed,
},
});

// A mouse down event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position.
runner.send_events(modifiers.into_iter().chain([
runner.send_events(modifiers.into_iter().chain(device_event).chain([
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved {
device_id: RootDeviceId(DeviceId(pointer_id)),
device_id,
position,
},
},
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)),
device_id,
state: ElementState::Pressed,
button,
},
Expand Down Expand Up @@ -534,21 +546,30 @@ impl<T> EventLoopWindowTarget<T> {
}
});

let device_id: RootDeviceId = RootDeviceId(DeviceId(pointer_id));
let device_event = runner.device_events().then(|| Event::DeviceEvent {
device_id,
event: DeviceEvent::Button {
button: button.to_id(),
state: ElementState::Pressed,
},
});

// A mouse up event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position.
runner.send_events(modifiers.into_iter().chain([
runner.send_events(modifiers.into_iter().chain(device_event).chain([
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved {
device_id: RootDeviceId(DeviceId(pointer_id)),
device_id,
position,
},
},
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)),
device_id,
state: ElementState::Released,
button,
},
Expand Down
11 changes: 11 additions & 0 deletions src/platform_impl/web/web_sys/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ pub fn mouse_button(event: &MouseEvent) -> Option<MouseButton> {
}
}

impl MouseButton {
pub fn to_id(self) -> u32 {
match self {
MouseButton::Left => 0,
MouseButton::Right => 1,
MouseButton::Middle => 2,
MouseButton::Other(value) => value.into(),
}
}
}

pub fn mouse_position(event: &MouseEvent) -> LogicalPosition<f64> {
LogicalPosition {
x: event.offset_x() as f64,
Expand Down

0 comments on commit e5e322a

Please sign in to comment.