On Web, implement DeviceEvents (#2871)

This commit is contained in:
daxpedda 2023-06-14 10:26:26 +02:00 committed by GitHub
parent 9a9c9b15ba
commit 6300cf915e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 405 additions and 85 deletions

View file

@ -81,6 +81,10 @@ 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, use `Window.requestIdleCallback()` for `ControlFlow::Poll` when available.
- **Breaking:** On Web, the canvas size is not controlled by Winit anymore and external changes to - **Breaking:** On Web, the canvas size is not controlled by Winit anymore and external changes to
the canvas size will be reported through `WindowEvent::Resized`. the canvas size will be reported through `WindowEvent::Resized`.
- 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`, `DeviceEvent::MouseWheel`, `DeviceEvent::Button` and
`DeviceEvent::Key` support.
# 0.28.6 # 0.28.6

View file

@ -356,11 +356,11 @@ impl<T> EventLoopWindowTarget<T> {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Wayland / macOS / iOS / Android / Web / Orbital:** Unsupported. /// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
/// ///
/// [`DeviceEvent`]: crate::event::DeviceEvent /// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn listen_device_events(&self, _allowed: DeviceEvents) { pub fn listen_device_events(&self, _allowed: DeviceEvents) {
#[cfg(any(x11_platform, wayland_platform, windows))] #[cfg(any(x11_platform, wasm_platform, wayland_platform, windows))]
self.p.listen_device_events(_allowed); self.p.listen_device_events(_allowed);
} }
} }

View file

@ -1,17 +1,24 @@
use super::super::DeviceId;
use super::{backend, state::State}; use super::{backend, state::State};
use crate::dpi::PhysicalSize; use crate::dpi::PhysicalSize;
use crate::event::{Event, StartCause}; use crate::event::{
use crate::event_loop::ControlFlow; DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, RawKeyEvent, StartCause,
};
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::platform_impl::platform::backend::EventListenerHandle;
use crate::window::WindowId; use crate::window::WindowId;
use std::sync::atomic::Ordering;
use std::{ use std::{
cell::RefCell, cell::{Cell, RefCell},
clone::Clone, clone::Clone,
collections::{HashSet, VecDeque}, collections::{HashSet, VecDeque},
iter, iter,
ops::Deref, ops::Deref,
rc::{Rc, Weak}, rc::{Rc, Weak},
}; };
use wasm_bindgen::prelude::Closure;
use web_sys::{KeyboardEvent, PointerEvent, WheelEvent};
use web_time::{Duration, Instant}; use web_time::{Duration, Instant};
pub struct Shared<T: 'static>(Rc<Execution<T>>); pub struct Shared<T: 'static>(Rc<Execution<T>>);
@ -24,6 +31,8 @@ impl<T> Clone for Shared<T> {
} }
} }
type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
pub struct Execution<T: 'static> { pub struct Execution<T: 'static> {
runner: RefCell<RunnerEnum<T>>, runner: RefCell<RunnerEnum<T>>,
events: RefCell<VecDeque<EventWrapper<T>>>, events: RefCell<VecDeque<EventWrapper<T>>>,
@ -33,6 +42,13 @@ pub struct Execution<T: 'static> {
redraw_pending: RefCell<HashSet<WindowId>>, redraw_pending: RefCell<HashSet<WindowId>>,
destroy_pending: RefCell<VecDeque<WindowId>>, destroy_pending: RefCell<VecDeque<WindowId>>,
unload_event_handle: RefCell<Option<backend::UnloadEventHandle>>, unload_event_handle: RefCell<Option<backend::UnloadEventHandle>>,
device_events: Cell<DeviceEvents>,
on_mouse_move: OnEventHandle<PointerEvent>,
on_wheel: OnEventHandle<WheelEvent>,
on_mouse_press: OnEventHandle<PointerEvent>,
on_mouse_release: OnEventHandle<PointerEvent>,
on_key_press: OnEventHandle<KeyboardEvent>,
on_key_release: OnEventHandle<KeyboardEvent>,
} }
enum RunnerEnum<T: 'static> { enum RunnerEnum<T: 'static> {
@ -131,6 +147,13 @@ impl<T: 'static> Shared<T> {
redraw_pending: RefCell::new(HashSet::new()), redraw_pending: RefCell::new(HashSet::new()),
destroy_pending: RefCell::new(VecDeque::new()), destroy_pending: RefCell::new(VecDeque::new()),
unload_event_handle: RefCell::new(None), unload_event_handle: RefCell::new(None),
device_events: Cell::default(),
on_mouse_move: RefCell::new(None),
on_wheel: RefCell::new(None),
on_mouse_press: RefCell::new(None),
on_mouse_release: RefCell::new(None),
on_key_press: RefCell::new(None),
on_key_release: RefCell::new(None),
})) }))
} }
@ -165,6 +188,184 @@ impl<T: 'static> Shared<T> {
Some(backend::on_unload(self.window(), move || { Some(backend::on_unload(self.window(), move || {
close_instance.handle_unload() close_instance.handle_unload()
})); }));
let runner = self.clone();
let window = self.window().clone();
*self.0.on_mouse_move.borrow_mut() = Some(EventListenerHandle::new(
self.window(),
"pointermove",
Closure::new(move |event: PointerEvent| {
if !runner.device_events() {
return;
}
let pointer_type = event.pointer_type();
if pointer_type != "mouse" {
return;
}
// chorded button event
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 mut delta = backend::event::MouseDelta::init(&window, &event);
runner.send_events(backend::event::pointer_move_event(event).flat_map(|event| {
let delta = delta
.delta(&event)
.to_physical(backend::scale_factor(&window));
let x_motion = (delta.x != 0.0).then_some(Event::DeviceEvent {
device_id,
event: DeviceEvent::Motion {
axis: 0,
value: delta.x,
},
});
let y_motion = (delta.y != 0.0).then_some(Event::DeviceEvent {
device_id,
event: DeviceEvent::Motion {
axis: 1,
value: delta.y,
},
});
x_motion
.into_iter()
.chain(y_motion)
.chain(iter::once(Event::DeviceEvent {
device_id,
event: DeviceEvent::MouseMotion {
delta: (delta.x, delta.y),
},
}))
}));
}),
));
let runner = self.clone();
let window = self.window().clone();
*self.0.on_wheel.borrow_mut() = Some(EventListenerHandle::new(
self.window(),
"wheel",
Closure::new(move |event: WheelEvent| {
if !runner.device_events() {
return;
}
if let Some(delta) = backend::event::mouse_scroll_delta(&window, &event) {
runner.send_event(Event::DeviceEvent {
device_id: RootDeviceId(DeviceId(0)),
event: DeviceEvent::MouseWheel { delta },
});
}
}),
));
let runner = self.clone();
*self.0.on_mouse_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_mouse_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,
},
});
}),
));
let runner = self.clone();
*self.0.on_key_press.borrow_mut() = Some(EventListenerHandle::new(
self.window(),
"keydown",
Closure::new(move |event: KeyboardEvent| {
if !runner.device_events() {
return;
}
runner.send_event(Event::DeviceEvent {
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
event: DeviceEvent::Key(RawKeyEvent {
physical_key: backend::event::key_code(&event),
state: ElementState::Pressed,
}),
});
}),
));
let runner = self.clone();
*self.0.on_key_release.borrow_mut() = Some(EventListenerHandle::new(
self.window(),
"keyup",
Closure::new(move |event: KeyboardEvent| {
if !runner.device_events() {
return;
}
runner.send_event(Event::DeviceEvent {
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
event: DeviceEvent::Key(RawKeyEvent {
physical_key: backend::event::key_code(&event),
state: ElementState::Released,
}),
});
}),
));
} }
// Generate a strictly increasing ID // Generate a strictly increasing ID
@ -405,6 +606,12 @@ impl<T: 'static> Shared<T> {
self.handle_event(Event::LoopDestroyed, control); self.handle_event(Event::LoopDestroyed, control);
let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut()); let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut());
*self.0.unload_event_handle.borrow_mut() = None; *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_mouse_press.borrow_mut() = None;
*self.0.on_mouse_release.borrow_mut() = None;
*self.0.on_key_press.borrow_mut() = None;
*self.0.on_key_release.borrow_mut() = None;
// Dropping the `Runner` drops the event handler closure, which will in // Dropping the `Runner` drops the event handler closure, which will in
// turn drop all `Window`s moved into the closure. // turn drop all `Window`s moved into the closure.
*self.0.runner.borrow_mut() = RunnerEnum::Destroyed; *self.0.runner.borrow_mut() = RunnerEnum::Destroyed;
@ -450,6 +657,24 @@ impl<T: 'static> Shared<T> {
RunnerEnum::Destroyed => ControlFlow::Exit, RunnerEnum::Destroyed => ControlFlow::Exit,
} }
} }
pub fn listen_device_events(&self, allowed: DeviceEvents) {
self.0.device_events.set(allowed)
}
pub fn device_events(&self) -> bool {
match self.0.device_events.get() {
DeviceEvents::Always => true,
DeviceEvents::WhenFocused => self.0.all_canvases.borrow().iter().any(|(_, canvas)| {
if let Some(canvas) = canvas.upgrade() {
canvas.borrow().has_focus.load(Ordering::Relaxed)
} else {
false
}
}),
DeviceEvents::Never => false,
}
}
} }
pub(crate) enum EventWrapper<T: 'static> { pub(crate) enum EventWrapper<T: 'static> {

View file

@ -3,8 +3,7 @@ use std::clone::Clone;
use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque}; use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque};
use std::iter; use std::iter;
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::Ordering;
use std::sync::Arc;
use raw_window_handle::{RawDisplayHandle, WebDisplayHandle}; use raw_window_handle::{RawDisplayHandle, WebDisplayHandle};
@ -18,9 +17,10 @@ use super::{
window::WindowId, window::WindowId,
}; };
use crate::event::{ use crate::event::{
DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase, DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, KeyEvent, RawKeyEvent, Touch,
WindowEvent, TouchPhase, WindowEvent,
}; };
use crate::event_loop::DeviceEvents;
use crate::keyboard::ModifiersState; use crate::keyboard::ModifiersState;
use crate::window::{Theme, WindowId as RootWindowId}; use crate::window::{Theme, WindowId as RootWindowId};
@ -82,7 +82,6 @@ impl<T> EventLoopWindowTarget<T> {
canvas: &Rc<RefCell<backend::Canvas>>, canvas: &Rc<RefCell<backend::Canvas>>,
id: WindowId, id: WindowId,
prevent_default: bool, prevent_default: bool,
has_focus: Arc<AtomicBool>,
) { ) {
self.runner.add_canvas(RootWindowId(id), canvas); self.runner.add_canvas(RootWindowId(id), canvas);
let canvas_clone = canvas.clone(); let canvas_clone = canvas.clone();
@ -92,10 +91,10 @@ impl<T> EventLoopWindowTarget<T> {
canvas.on_touch_start(prevent_default); canvas.on_touch_start(prevent_default);
let runner = self.runner.clone(); let runner = self.runner.clone();
let has_focus_clone = has_focus.clone(); let has_focus = canvas.has_focus.clone();
let modifiers = self.modifiers.clone(); let modifiers = self.modifiers.clone();
canvas.on_blur(move || { canvas.on_blur(move || {
has_focus_clone.store(false, Ordering::Relaxed); has_focus.store(false, Ordering::Relaxed);
let clear_modifiers = (!modifiers.get().is_empty()).then(|| { let clear_modifiers = (!modifiers.get().is_empty()).then(|| {
modifiers.set(ModifiersState::empty()); modifiers.set(ModifiersState::empty());
@ -116,9 +115,9 @@ impl<T> EventLoopWindowTarget<T> {
}); });
let runner = self.runner.clone(); let runner = self.runner.clone();
let has_focus_clone = has_focus.clone(); let has_focus = canvas.has_focus.clone();
canvas.on_focus(move || { canvas.on_focus(move || {
if !has_focus_clone.swap(true, Ordering::Relaxed) { if !has_focus.swap(true, Ordering::Relaxed) {
runner.send_event(Event::WindowEvent { runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::Focused(true), event: WindowEvent::Focused(true),
@ -138,24 +137,36 @@ impl<T> EventLoopWindowTarget<T> {
} }
}); });
let device_id = RootDeviceId(unsafe { DeviceId::dummy() });
let device_event = runner.device_events().then_some(Event::DeviceEvent {
device_id,
event: DeviceEvent::Key(RawKeyEvent {
physical_key,
state: ElementState::Pressed,
}),
});
runner.send_events( runner.send_events(
iter::once(Event::WindowEvent { device_event
window_id: RootWindowId(id), .into_iter()
event: WindowEvent::KeyboardInput { .chain(iter::once(Event::WindowEvent {
device_id: RootDeviceId(unsafe { DeviceId::dummy() }), window_id: RootWindowId(id),
event: KeyEvent { event: WindowEvent::KeyboardInput {
physical_key, device_id,
logical_key, event: KeyEvent {
text, physical_key,
location, logical_key,
state: ElementState::Pressed, text,
repeat, location,
platform_specific: KeyEventExtra, state: ElementState::Pressed,
repeat,
platform_specific: KeyEventExtra,
},
is_synthetic: false,
}, },
is_synthetic: false, }))
}, .chain(modifiers_changed),
})
.chain(modifiers_changed),
); );
}, },
prevent_default, prevent_default,
@ -173,29 +184,42 @@ impl<T> EventLoopWindowTarget<T> {
} }
}); });
let device_id = RootDeviceId(unsafe { DeviceId::dummy() });
let device_event = runner.device_events().then_some(Event::DeviceEvent {
device_id,
event: DeviceEvent::Key(RawKeyEvent {
physical_key,
state: ElementState::Pressed,
}),
});
runner.send_events( runner.send_events(
iter::once(Event::WindowEvent { device_event
window_id: RootWindowId(id), .into_iter()
event: WindowEvent::KeyboardInput { .chain(iter::once(Event::WindowEvent {
device_id: RootDeviceId(unsafe { DeviceId::dummy() }), window_id: RootWindowId(id),
event: KeyEvent { event: WindowEvent::KeyboardInput {
physical_key, device_id,
logical_key, event: KeyEvent {
text, physical_key,
location, logical_key,
state: ElementState::Released, text,
repeat, location,
platform_specific: KeyEventExtra, state: ElementState::Released,
repeat,
platform_specific: KeyEventExtra,
},
is_synthetic: false,
}, },
is_synthetic: false, }))
}, .chain(modifiers_changed),
})
.chain(modifiers_changed),
) )
}, },
prevent_default, prevent_default,
); );
let has_focus = canvas.has_focus.clone();
canvas.on_cursor_leave({ canvas.on_cursor_leave({
let runner = self.runner.clone(); let runner = self.runner.clone();
let has_focus = has_focus.clone(); let has_focus = has_focus.clone();
@ -290,13 +314,34 @@ impl<T> EventLoopWindowTarget<T> {
|(position, delta)| { |(position, delta)| {
let device_id = RootDeviceId(DeviceId(pointer_id)); let device_id = RootDeviceId(DeviceId(pointer_id));
[ let device_events = runner.device_events().then(|| {
Event::DeviceEvent { let x_motion = (delta.x != 0.0).then_some(Event::DeviceEvent {
device_id, device_id,
event: DeviceEvent::MouseMotion { event: DeviceEvent::Motion {
delta: (delta.x, delta.y), axis: 0,
value: delta.x,
}, },
}, });
let y_motion = (delta.y != 0.0).then_some(Event::DeviceEvent {
device_id,
event: DeviceEvent::Motion {
axis: 1,
value: delta.y,
},
});
x_motion.into_iter().chain(y_motion).chain(iter::once(
Event::DeviceEvent {
device_id,
event: DeviceEvent::MouseMotion {
delta: (delta.x, delta.y),
},
},
))
});
device_events.into_iter().flatten().chain(iter::once(
Event::WindowEvent { Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { event: WindowEvent::CursorMoved {
@ -304,7 +349,7 @@ impl<T> EventLoopWindowTarget<T> {
position, position,
}, },
}, },
] ))
}, },
))); )));
} }
@ -359,38 +404,41 @@ impl<T> EventLoopWindowTarget<T> {
} }
}); });
let button_event = if buttons.contains(button.into()) { let device_id = RootDeviceId(DeviceId(pointer_id));
Event::WindowEvent {
window_id: RootWindowId(id), let state = if buttons.contains(button.into()) {
event: WindowEvent::MouseInput { ElementState::Pressed
device_id: RootDeviceId(DeviceId(pointer_id)),
state: ElementState::Pressed,
button,
},
}
} else { } else {
Event::WindowEvent { ElementState::Released
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)),
state: ElementState::Released,
button,
},
}
}; };
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, // A chorded button event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the // therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position. // 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 { Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { event: WindowEvent::CursorMoved {
device_id: RootDeviceId(DeviceId(pointer_id)), device_id,
position, position,
}, },
}, },
button_event, Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id,
state,
button,
},
},
])); ]));
} }
}, },
@ -425,21 +473,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, // A mouse down event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the // therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position. // 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 { Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { event: WindowEvent::CursorMoved {
device_id: RootDeviceId(DeviceId(pointer_id)), device_id,
position, position,
}, },
}, },
Event::WindowEvent { Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)), device_id,
state: ElementState::Pressed, state: ElementState::Pressed,
button, button,
}, },
@ -509,21 +566,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, // A mouse up event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the // therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position. // 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 { Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { event: WindowEvent::CursorMoved {
device_id: RootDeviceId(DeviceId(pointer_id)), device_id,
position, position,
}, },
}, },
Event::WindowEvent { Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)), device_id,
state: ElementState::Released, state: ElementState::Released,
button, button,
}, },
@ -577,16 +643,21 @@ impl<T> EventLoopWindowTarget<T> {
} }
}); });
runner.send_events(modifiers_changed.into_iter().chain(iter::once( let device_event = runner.device_events().then_some(Event::DeviceEvent {
Event::WindowEvent { device_id: RootDeviceId(DeviceId(pointer_id)),
event: DeviceEvent::MouseWheel { delta },
});
runner.send_events(modifiers_changed.into_iter().chain(device_event).chain(
iter::once(Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::MouseWheel { event: WindowEvent::MouseWheel {
device_id: RootDeviceId(DeviceId(pointer_id)), device_id: RootDeviceId(DeviceId(pointer_id)),
delta, delta,
phase: TouchPhase::Moved, phase: TouchPhase::Moved,
}, },
}, }),
))); ));
}, },
prevent_default, prevent_default,
); );
@ -661,4 +732,8 @@ impl<T> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle { pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Web(WebDisplayHandle::empty()) RawDisplayHandle::Web(WebDisplayHandle::empty())
} }
pub fn listen_device_events(&self, allowed: DeviceEvents) {
self.runner.listen_device_events(allowed)
}
} }

View file

@ -12,6 +12,8 @@ use crate::window::{WindowAttributes, WindowId as RootWindowId};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use js_sys::Promise; use js_sys::Promise;
use smol_str::SmolStr; use smol_str::SmolStr;
@ -24,6 +26,7 @@ use web_sys::{Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, WheelEvent};
pub struct Canvas { pub struct Canvas {
common: Common, common: Common,
id: WindowId, id: WindowId,
pub has_focus: Arc<AtomicBool>,
on_touch_start: Option<EventListenerHandle<dyn FnMut(Event)>>, on_touch_start: Option<EventListenerHandle<dyn FnMut(Event)>>,
on_touch_end: Option<EventListenerHandle<dyn FnMut(Event)>>, on_touch_end: Option<EventListenerHandle<dyn FnMut(Event)>>,
on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>, on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
@ -91,6 +94,7 @@ impl Canvas {
wants_fullscreen: Rc::new(RefCell::new(false)), wants_fullscreen: Rc::new(RefCell::new(false)),
}, },
id, id,
has_focus: Arc::new(AtomicBool::new(false)),
on_touch_start: None, on_touch_start: None,
on_touch_end: None, on_touch_end: None,
on_blur: None, on_blur: None,

View file

@ -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> { pub fn mouse_position(event: &MouseEvent) -> LogicalPosition<f64> {
LogicalPosition { LogicalPosition {
x: event.offset_x() as f64, x: event.offset_x() as f64,

View file

@ -1,5 +1,5 @@
mod canvas; mod canvas;
mod event; pub mod event;
mod event_handle; mod event_handle;
mod media_query_handle; mod media_query_handle;
mod pointer; mod pointer;
@ -8,6 +8,7 @@ mod timeout;
pub use self::canvas::Canvas; pub use self::canvas::Canvas;
pub use self::event::ButtonsState; pub use self::event::ButtonsState;
pub use self::event_handle::EventListenerHandle;
pub use self::resize_scaling::ResizeScaleHandle; pub use self::resize_scaling::ResizeScaleHandle;
pub use self::timeout::{IdleCallback, Timeout}; pub use self::timeout::{IdleCallback, Timeout};

View file

@ -51,12 +51,12 @@ impl Window {
let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id))); let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id)));
let has_focus = Arc::new(AtomicBool::new(false)); target.register(&canvas, id, prevent_default);
target.register(&canvas, id, prevent_default, has_focus.clone());
let runner = target.runner.clone(); let runner = target.runner.clone();
let destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id))); let destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id)));
let has_focus = canvas.borrow().has_focus.clone();
let window = Window { let window = Window {
id, id,
has_focus, has_focus,