diff --git a/xilem_web/src/events.rs b/xilem_web/src/events.rs index 512b48fad..4b931b6ec 100644 --- a/xilem_web/src/events.rs +++ b/xilem_web/src/events.rs @@ -392,7 +392,7 @@ macro_rules! event_definitions { State: 'static, Action: 'static, V: DomView, - OA: OptionalAction, + OA: OptionalAction + 'static, Callback: Fn(&mut State, web_sys::$web_sys_ty) -> OA + 'static, { type ViewState = OnEventState; @@ -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), @@ -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), @@ -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), diff --git a/xilem_web/src/interfaces.rs b/xilem_web/src/interfaces.rs index 5fbf9b2ea..65ff985c7 100644 --- a/xilem_web/src/interfaces.rs +++ b/xilem_web/src/interfaces.rs @@ -37,10 +37,10 @@ macro_rules! event_handler_mixin { handler: Callback, ) -> events::$event_ty where - Self: Sized, - Self::Element: AsRef, - OA: OptionalAction, - Callback: Fn(&mut State, web_sys::$web_sys_event_type) -> OA, + State: 'static, + Action: 'static, + OA: OptionalAction + 'static, + Callback: Fn(&mut State, web_sys::$web_sys_event_type) -> OA + 'static, { events::$event_ty::new(self, handler) } @@ -116,15 +116,41 @@ pub trait Element: handler: Callback, ) -> events::OnEvent where - Self::Element: AsRef, - Event: JsCast + 'static, + State: 'static, + Action: 'static, OA: OptionalAction, - 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( self, handler: Callback, @@ -210,7 +236,7 @@ pub trait Element: (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), @@ -258,6 +284,35 @@ pub trait Element: (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), diff --git a/xilem_web/src/pointer.rs b/xilem_web/src/pointer.rs index 8975d4510..53d0ba366 100644 --- a/xilem_web/src/pointer.rs +++ b/xilem_web/src/pointer.rs @@ -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 { child: V, callback: F, phantom: PhantomData (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 { // reason: Closures are retained so they can be called by environment #[allow(unused)] @@ -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 { @@ -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::(); + let el = element.node.as_ref(); let [down_closure, move_closure, up_closure] = build_event_listeners(ctx, el); let state = PointerState { diff --git a/xilem_web/web_examples/counter/src/main.rs b/xilem_web/web_examples/counter/src/main.rs index 08d27aea5..79ff19042 100644 --- a/xilem_web/web_examples/counter/src/main.rs +++ b/xilem_web/web_examples/counter/src/main.rs @@ -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 { el::button(label).on_click(click_fn) }