Skip to content

Commit

Permalink
xilem_web: Add pointer events and fix bounds on event listeners and c…
Browse files Browse the repository at this point in the history
…orrect event type for mouse events. (#733)

As [this Firefox
issue](https://bugzilla.mozilla.org/show_bug.cgi?id=1675847) is finally
fixed, we can be more close to the spec with the
`click/auxclick/contextmenu` events by using a `PointerEvent` instead of
a `MouseEvent`.

I've also noticed that the DOM interface bounds/types were out of sync
with the event types (bounds), this was corrected as well.

Additionally all the `pointer...` events were added.
And a simple `PointerMsg::position() -> Point` accessor.
  • Loading branch information
Philipp-M authored Nov 10, 2024
1 parent eb36f1e commit 6258856
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 18 deletions.
19 changes: 13 additions & 6 deletions xilem_web/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ macro_rules! event_definitions {
State: 'static,
Action: 'static,
V: DomView<State, Action>,
OA: OptionalAction<Action>,
OA: OptionalAction<Action> + 'static,
Callback: Fn(&mut State, web_sys::$web_sys_ty) -> OA + 'static,
{
type ViewState = OnEventState<V::ViewState>;
Expand Down Expand Up @@ -453,11 +453,9 @@ macro_rules! event_definitions {
};
}

// click/auxclick/contextmenu are still mouse events in either Safari as well as Firefox,
// see: https://stackoverflow.com/questions/70626381/why-chrome-emits-pointerevents-and-firefox-mouseevents-and-which-type-definition/76900433#76900433
event_definitions!(
(OnAbort, "abort", Event),
(OnAuxClick, "auxclick", MouseEvent),
(OnAuxClick, "auxclick", PointerEvent),
(OnBeforeInput, "beforeinput", InputEvent),
(OnBeforeMatch, "beforematch", Event),
(OnBeforeToggle, "beforetoggle", Event),
Expand All @@ -466,10 +464,10 @@ event_definitions!(
(OnCanPlay, "canplay", Event),
(OnCanPlayThrough, "canplaythrough", Event),
(OnChange, "change", Event),
(OnClick, "click", MouseEvent),
(OnClick, "click", PointerEvent),
(OnClose, "close", Event),
(OnContextLost, "contextlost", Event),
(OnContextMenu, "contextmenu", MouseEvent),
(OnContextMenu, "contextmenu", PointerEvent),
(OnContextRestored, "contextrestored", Event),
(OnCopy, "copy", Event),
(OnCueChange, "cuechange", Event),
Expand Down Expand Up @@ -509,6 +507,15 @@ event_definitions!(
(OnPause, "pause", Event),
(OnPlay, "play", Event),
(OnPlaying, "playing", Event),
(OnPointerCancel, "pointercancel", PointerEvent),
(OnPointerDown, "pointerdown", PointerEvent),
(OnPointerEnter, "pointerenter", PointerEvent),
(OnPointerLeave, "pointerleave", PointerEvent),
(OnPointerMove, "pointermove", PointerEvent),
(OnPointerOut, "pointerout", PointerEvent),
(OnPointerOver, "pointerover", PointerEvent),
(OnPointerRawUpdate, "pointerrawupdate", PointerEvent),
(OnPointerUp, "pointerup", PointerEvent),
(OnProgress, "progress", Event),
(OnRateChange, "ratechange", Event),
(OnReset, "reset", Event),
Expand Down
73 changes: 64 additions & 9 deletions xilem_web/src/interfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ macro_rules! event_handler_mixin {
handler: Callback,
) -> events::$event_ty<Self, State, Action, Callback>
where
Self: Sized,
Self::Element: AsRef<web_sys::Element>,
OA: OptionalAction<Action>,
Callback: Fn(&mut State, web_sys::$web_sys_event_type) -> OA,
State: 'static,
Action: 'static,
OA: OptionalAction<Action> + 'static,
Callback: Fn(&mut State, web_sys::$web_sys_event_type) -> OA + 'static,
{
events::$event_ty::new(self, handler)
}
Expand Down Expand Up @@ -116,15 +116,41 @@ pub trait Element<State, Action = ()>:
handler: Callback,
) -> events::OnEvent<Self, State, Action, Event, Callback>
where
Self::Element: AsRef<web_sys::Element>,
Event: JsCast + 'static,
State: 'static,
Action: 'static,
OA: OptionalAction<Action>,
Callback: Fn(&mut State, Event) -> OA,
Self: Sized,
Callback: Fn(&mut State, Event) -> OA + 'static,
Event: JsCast + 'static + crate::Message,
{
events::OnEvent::new(self, event, handler)
}

/// Add a stateful pointer event (down/move/up) listener to this [`Element`].
///
/// The pointer ids are captured from the underlying element.
///
/// # Examples
///
/// ```
/// use xilem_web::{interfaces::Element, elements::html::div, PointerDetails, PointerMsg};
/// use web_sys::console::log_1;
///
/// # fn component() -> impl Element<()> {
/// div(()).pointer(|_, pointer_msg| {
/// match pointer_msg {
/// PointerMsg::Down(PointerDetails { position, button, id }) => {
/// log_1(&format!("Down({id}) at {position} and button: {button}").into());
/// }
/// PointerMsg::Move(PointerDetails { position, button, id }) => {
/// log_1(&format!("Move({id}) at {position} and button: {button}").into());
/// }
/// PointerMsg::Up(PointerDetails { position, button, id }) => {
/// log_1(&format!("Up({id}) at {position} and button: {button}").into());
/// }
/// };
/// })
/// # }
/// ```
fn pointer<Callback: Fn(&mut State, PointerMsg)>(
self,
handler: Callback,
Expand Down Expand Up @@ -210,7 +236,7 @@ pub trait Element<State, Action = ()>:
(OnCanPlay, on_canplay, "canplay", Event),
(OnCanPlayThrough, on_canplaythrough, "canplaythrough", Event),
(OnChange, on_change, "change", Event),
(OnClick, on_click, "click", MouseEvent),
(OnClick, on_click, "click", PointerEvent),
(OnClose, on_close, "close", Event),
(OnContextLost, on_contextlost, "contextlost", Event),
(OnContextMenu, on_contextmenu, "contextmenu", PointerEvent),
Expand Down Expand Up @@ -258,6 +284,35 @@ pub trait Element<State, Action = ()>:
(OnPause, on_pause, "pause", Event),
(OnPlay, on_play, "play", Event),
(OnPlaying, on_playing, "playing", Event),
(
OnPointerCancel,
on_pointercancel,
"pointercancel",
PointerEvent
),
(OnPointerDown, on_pointerdown, "pointerdown", PointerEvent),
(
OnPointerEnter,
on_pointerenter,
"pointerenter",
PointerEvent
),
(
OnPointerLeave,
on_pointerleave,
"pointerleave",
PointerEvent
),
(OnPointerMove, on_pointermove, "pointermove", PointerEvent),
(OnPointerOut, on_pointerout, "pointerout", PointerEvent),
(OnPointerOver, on_pointerover, "pointerover", PointerEvent),
(
OnPointerRawUpdate,
on_pointerrawupdate,
"pointerrawupdate",
PointerEvent
),
(OnPointerUp, on_pointerup, "pointerup", PointerEvent),
(OnProgress, on_progress, "progress", Event),
(OnRateChange, on_ratechange, "ratechange", Event),
(OnReset, on_reset, "reset", Event),
Expand Down
29 changes: 27 additions & 2 deletions xilem_web/src/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@ use web_sys::PointerEvent;
const POINTER_VIEW_ID: ViewId = ViewId::new(0x1234_5014);

/// A view that allows stateful handling of [`PointerEvent`]s with [`PointerMsg`]
///
/// See [`Element::pointer`] for more details how to use this view.
pub struct Pointer<V, T, A, F> {
child: V,
callback: F,
phantom: PhantomData<fn() -> (T, A)>,
}

#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
#[allow(
unnameable_types,
reason = "Implementation detail, public because of trait visibility rules"
)]
pub struct PointerState<S> {
// reason: Closures are retained so they can be called by environment
#[allow(unused)]
Expand All @@ -44,6 +49,26 @@ pub enum PointerMsg {
Up(PointerDetails),
}

impl PointerMsg {
pub fn position(&self) -> Point {
match self {
PointerMsg::Down(p) | PointerMsg::Move(p) | PointerMsg::Up(p) => p.position,
}
}

pub fn button(&self) -> i16 {
match self {
PointerMsg::Down(p) | PointerMsg::Move(p) | PointerMsg::Up(p) => p.button,
}
}

pub fn id(&self) -> i32 {
match self {
PointerMsg::Down(p) | PointerMsg::Move(p) | PointerMsg::Up(p) => p.id,
}
}
}

#[derive(Debug)]
/// Details of a pointer event.
pub struct PointerDetails {
Expand Down Expand Up @@ -125,7 +150,7 @@ where
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
ctx.with_id(POINTER_VIEW_ID, |ctx| {
let (element, child_state) = self.child.build(ctx);
let el = element.as_ref().unchecked_ref::<web_sys::Element>();
let el = element.node.as_ref();

let [down_closure, move_closure, up_closure] = build_event_listeners(ctx, el);
let state = PointerState {
Expand Down
2 changes: 1 addition & 1 deletion xilem_web/web_examples/counter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl AppState {
/// You can create functions that generate views.
fn btn(
label: &'static str,
click_fn: impl Fn(&mut AppState, web_sys::MouseEvent) + 'static,
click_fn: impl Fn(&mut AppState, web_sys::PointerEvent) + 'static,
) -> impl HtmlButtonElement<AppState> {
el::button(label).on_click(click_fn)
}
Expand Down

0 comments on commit 6258856

Please sign in to comment.