From cc3f60dd2ffb472e877bc4345c8b27c4ac32ccdd Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Tue, 22 Aug 2023 23:29:24 -0400 Subject: [PATCH 01/56] Adding PointInteraction trait, etc. --- packages/html/Cargo.toml | 2 +- packages/html/src/events/mouse.rs | 196 +---------------------- packages/html/src/input_data.rs | 3 +- packages/html/src/lib.rs | 1 + packages/html/src/point_interaction.rs | 95 +++++++++++ packages/html/src/web_sys_bind/events.rs | 24 +-- 6 files changed, 120 insertions(+), 201 deletions(-) create mode 100644 packages/html/src/point_interaction.rs diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 5644b440c..83706d3bc 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -16,7 +16,7 @@ serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } wasm-bindgen = { workspace = true, optional = true } euclid = "0.22.7" -enumset = "1.0.11" +enumset = { version = "1.0.11", features = ["serde"] } keyboard-types = "0.7" async-trait = "0.1.58" serde-value = "0.7.0" diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index 7899393ff..51bca7d48 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -1,9 +1,5 @@ -use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; -use crate::input_data::{ - decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet, -}; +use crate::point_interaction::{PointData, PointInteraction}; use dioxus_core::Event; -use keyboard_types::Modifiers; use std::fmt::{Debug, Formatter}; pub type MouseEvent = Event; @@ -12,80 +8,8 @@ pub type MouseEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Default, PartialEq, Eq)] /// Data associated with a mouse event -/// -/// Do not use the deprecated fields; they may change or become private in the future. pub struct MouseData { - /// True if the alt key was down when the mouse event was fired. - #[deprecated(since = "0.3.0", note = "use modifiers() instead")] - pub alt_key: bool, - - /// The button number that was pressed (if applicable) when the mouse event was fired. - #[deprecated(since = "0.3.0", note = "use trigger_button() instead")] - pub button: i16, - - /// Indicates which buttons are pressed on the mouse (or other input device) when a mouse event is triggered. - /// - /// Each button that can be pressed is represented by a given number (see below). If more than one button is pressed, the button values are added together to produce a new number. For example, if the secondary (2) and auxiliary (4) buttons are pressed simultaneously, the value is 6 (i.e., 2 + 4). - /// - /// - 1: Primary button (usually the left button) - /// - 2: Secondary button (usually the right button) - /// - 4: Auxiliary button (usually the mouse wheel button or middle button) - /// - 8: 4th button (typically the "Browser Back" button) - /// - 16 : 5th button (typically the "Browser Forward" button) - #[deprecated(since = "0.3.0", note = "use held_buttons() instead")] - pub buttons: u16, - - /// The horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). - /// - /// For example, clicking on the left edge of the viewport will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally. - #[deprecated(since = "0.3.0", note = "use client_coordinates() instead")] - pub client_x: i32, - - /// The vertical coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). - /// - /// For example, clicking on the top edge of the viewport will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically. - #[deprecated(since = "0.3.0", note = "use client_coordinates() instead")] - pub client_y: i32, - - /// True if the control key was down when the mouse event was fired. - #[deprecated(since = "0.3.0", note = "use modifiers() instead")] - pub ctrl_key: bool, - - /// True if the meta key was down when the mouse event was fired. - #[deprecated(since = "0.3.0", note = "use modifiers() instead")] - pub meta_key: bool, - - /// The offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node. - #[deprecated(since = "0.3.0", note = "use element_coordinates() instead")] - pub offset_x: i32, - - /// The offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node. - #[deprecated(since = "0.3.0", note = "use element_coordinates() instead")] - pub offset_y: i32, - - /// The X (horizontal) coordinate (in pixels) of the mouse, relative to the left edge of the entire document. This includes any portion of the document not currently visible. - /// - /// Being based on the edge of the document as it is, this property takes into account any horizontal scrolling of the page. For example, if the page is scrolled such that 200 pixels of the left side of the document are scrolled out of view, and the mouse is clicked 100 pixels inward from the left edge of the view, the value returned by pageX will be 300. - #[deprecated(since = "0.3.0", note = "use page_coordinates() instead")] - pub page_x: i32, - - /// The Y (vertical) coordinate in pixels of the event relative to the whole document. - /// - /// See `page_x`. - #[deprecated(since = "0.3.0", note = "use page_coordinates() instead")] - pub page_y: i32, - - /// The X coordinate of the mouse pointer in global (screen) coordinates. - #[deprecated(since = "0.3.0", note = "use screen_coordinates() instead")] - pub screen_x: i32, - - /// The Y coordinate of the mouse pointer in global (screen) coordinates. - #[deprecated(since = "0.3.0", note = "use screen_coordinates() instead")] - pub screen_y: i32, - - /// True if the shift key was down when the mouse event was fired. - #[deprecated(since = "0.3.0", note = "use modifiers() instead")] - pub shift_key: bool, + point_data: PointData, } impl_event! { @@ -153,118 +77,14 @@ impl MouseData { /// Construct MouseData with the specified properties /// /// Note: the current implementation truncates coordinates. In the future, when we change the internal representation, it may also support a fractional part. - pub fn new( - coordinates: Coordinates, - trigger_button: Option, - held_buttons: MouseButtonSet, - modifiers: Modifiers, - ) -> Self { - let alt_key = modifiers.contains(Modifiers::ALT); - let ctrl_key = modifiers.contains(Modifiers::CONTROL); - let meta_key = modifiers.contains(Modifiers::META); - let shift_key = modifiers.contains(Modifiers::SHIFT); - - let [client_x, client_y]: [i32; 2] = coordinates.client().cast().into(); - let [offset_x, offset_y]: [i32; 2] = coordinates.element().cast().into(); - let [page_x, page_y]: [i32; 2] = coordinates.page().cast().into(); - let [screen_x, screen_y]: [i32; 2] = coordinates.screen().cast().into(); - - #[allow(deprecated)] - Self { - alt_key, - ctrl_key, - meta_key, - shift_key, - - button: trigger_button.map_or(0, |b| b.into_web_code()), - buttons: encode_mouse_button_set(held_buttons), - - client_x, - client_y, - offset_x, - offset_y, - page_x, - page_y, - screen_x, - screen_y, - } + pub fn new(point_data: PointData) -> Self { + Self { point_data } } +} - /// The event's coordinates relative to the application's viewport (as opposed to the coordinate within the page). - /// - /// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally. - pub fn client_coordinates(&self) -> ClientPoint { - #[allow(deprecated)] - ClientPoint::new(self.client_x.into(), self.client_y.into()) - } - - /// The event's coordinates relative to the padding edge of the target element - /// - /// For example, clicking in the top left corner of an element will result in element coordinates (0., 0.) - pub fn element_coordinates(&self) -> ElementPoint { - #[allow(deprecated)] - ElementPoint::new(self.offset_x.into(), self.offset_y.into()) - } - - /// The event's coordinates relative to the entire document. This includes any portion of the document not currently visible. - /// - /// For example, if the page is scrolled 200 pixels to the right and 300 pixels down, clicking in the top left corner of the viewport would result in page coordinates (200., 300.) - pub fn page_coordinates(&self) -> PagePoint { - #[allow(deprecated)] - PagePoint::new(self.page_x.into(), self.page_y.into()) - } - - /// The event's coordinates relative to the entire screen. This takes into account the window's offset. - pub fn screen_coordinates(&self) -> ScreenPoint { - #[allow(deprecated)] - ScreenPoint::new(self.screen_x.into(), self.screen_y.into()) - } - - pub fn coordinates(&self) -> Coordinates { - Coordinates::new( - self.screen_coordinates(), - self.client_coordinates(), - self.element_coordinates(), - self.page_coordinates(), - ) - } - - /// The set of modifier keys which were pressed when the event occurred - pub fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - #[allow(deprecated)] - { - if self.alt_key { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key { - modifiers.insert(Modifiers::META); - } - if self.shift_key { - modifiers.insert(Modifiers::SHIFT); - } - } - - modifiers - } - - /// The set of mouse buttons which were held when the event occurred. - pub fn held_buttons(&self) -> MouseButtonSet { - #[allow(deprecated)] - decode_mouse_button_set(self.buttons) - } - - /// The mouse button that triggered the event - /// - // todo the following is kind of bad; should we just return None when the trigger_button is unreliable (and frankly irrelevant)? i guess we would need the event_type here - /// This is only guaranteed to indicate which button was pressed during events caused by pressing or releasing a button. As such, it is not reliable for events such as mouseenter, mouseleave, mouseover, mouseout, or mousemove. For example, a value of MouseButton::Primary may also indicate that no button was pressed. - pub fn trigger_button(&self) -> Option { - #[allow(deprecated)] - Some(MouseButton::from_web_code(self.button)) +impl PointInteraction for MouseData { + fn get_point_data(&self) -> PointData { + self.point_data } } diff --git a/packages/html/src/input_data.rs b/packages/html/src/input_data.rs index 28d182fdc..d354c78a9 100644 --- a/packages/html/src/input_data.rs +++ b/packages/html/src/input_data.rs @@ -8,9 +8,10 @@ use keyboard_types::Location; /// A mouse button type (such as Primary/Secondary) // note: EnumSetType also derives Copy and Clone for some reason #[allow(clippy::unused_unit)] -#[derive(EnumSetType, Debug)] +#[derive(EnumSetType, Debug, Default)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum MouseButton { + #[default] /// Primary button (typically the left button) Primary, /// Secondary button (typically the right button) diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 2fc1baeac..d555d798a 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -22,6 +22,7 @@ mod global_attributes; pub mod input_data; #[cfg(feature = "native-bind")] pub mod native_bind; +mod point_interaction; mod render_template; #[cfg(feature = "wasm-bind")] mod web_sys_bind; diff --git a/packages/html/src/point_interaction.rs b/packages/html/src/point_interaction.rs new file mode 100644 index 000000000..9692420e8 --- /dev/null +++ b/packages/html/src/point_interaction.rs @@ -0,0 +1,95 @@ +use keyboard_types::Modifiers; + +use crate::{ + geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}, + input_data::{MouseButton, MouseButtonSet}, +}; + +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[derive(Copy, Clone, Default, PartialEq, Eq)] +pub struct PointData { + pub trigger_button: MouseButton, + pub client_x: i32, + pub client_y: i32, + pub screen_x: i32, + pub screen_y: i32, + pub element_x: i32, + pub element_y: i32, + pub page_x: i32, + pub page_y: i32, + pub held_buttons: MouseButtonSet, + pub modifiers: Modifiers, +} + +impl PointData { + pub fn new( + trigger_button: Option, + coordinates: Coordinates, + held_buttons: MouseButtonSet, + modifiers: Modifiers, + ) -> Self { + let [client_x, client_y]: [i32; 2] = coordinates.client().cast().into(); + let [element_x, element_y]: [i32; 2] = coordinates.element().cast().into(); + let [page_x, page_y]: [i32; 2] = coordinates.page().cast().into(); + let [screen_x, screen_y]: [i32; 2] = coordinates.screen().cast().into(); + Self { + trigger_button: trigger_button.map_or(MouseButton::default(), |b| b), + client_x, + client_y, + screen_x, + screen_y, + element_x, + element_y, + page_x, + page_y, + held_buttons, + modifiers, + } + } +} + +pub trait PointInteraction { + fn get_point_data(&self) -> PointData; + + fn coordinates(&self) -> Coordinates { + let point_data = self.get_point_data(); + Coordinates::new( + ScreenPoint::new(point_data.screen_x.into(), point_data.screen_y.into()), + ClientPoint::new(point_data.client_x.into(), point_data.client_y.into()), + ElementPoint::new(point_data.element_x.into(), point_data.element_y.into()), + PagePoint::new(point_data.page_x.into(), point_data.page_y.into()), + ) + } + + fn client_coordinates(&self) -> ClientPoint { + let point_data = self.get_point_data(); + ClientPoint::new(point_data.client_x.into(), point_data.client_y.into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + let point_data = self.get_point_data(); + ScreenPoint::new(point_data.screen_x.into(), point_data.screen_y.into()) + } + + fn element_coordinates(&self) -> ElementPoint { + let point_data = self.get_point_data(); + ElementPoint::new(point_data.element_x.into(), point_data.element_y.into()) + } + + fn page_coordinates(&self) -> PagePoint { + let point_data = self.get_point_data(); + PagePoint::new(point_data.page_x.into(), point_data.page_y.into()) + } + + fn modifiers(&self) -> Modifiers { + self.get_point_data().modifiers + } + + fn held_buttons(&self) -> MouseButtonSet { + self.get_point_data().held_buttons + } + + fn trigger_button(&self) -> MouseButton { + self.get_point_data().trigger_button + } +} diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index e5a6c0d78..4b87fcf41 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -2,8 +2,8 @@ use crate::events::{ AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData, TransitionData, WheelData, }; -use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; +use crate::point_interaction::PointData; use crate::{DragData, MountedData}; use keyboard_types::{Code, Key, Modifiers}; use std::convert::TryInto; @@ -105,17 +105,19 @@ impl From<&MouseEvent> for MouseData { modifiers.insert(Modifiers::SHIFT); } - MouseData::new( - Coordinates::new( - ScreenPoint::new(e.screen_x().into(), e.screen_y().into()), - ClientPoint::new(e.client_x().into(), e.client_y().into()), - ElementPoint::new(e.offset_x().into(), e.offset_y().into()), - PagePoint::new(e.page_x().into(), e.page_y().into()), - ), - Some(MouseButton::from_web_code(e.button())), - decode_mouse_button_set(e.buttons()), + MouseData::new(PointData { + trigger_button: MouseButton::from_web_code(e.button()), + client_x: e.client_x(), + client_y: e.client_y(), + screen_x: e.screen_x(), + screen_y: e.screen_y(), + element_x: e.offset_x(), + element_y: e.offset_y(), + page_x: e.page_x(), + page_y: e.page_y(), + held_buttons: decode_mouse_button_set(e.buttons()), modifiers, - ) + }) } } From 0e5600f2f42d6a11a7851e904721676f2bfbd8fc Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 12:47:37 -0400 Subject: [PATCH 02/56] add impl for pointer --- packages/html/src/events/mouse.rs | 2 + packages/html/src/events/pointer.rs | 80 +++++++++++++---- packages/html/src/point_interaction.rs | 109 +++++++++++++++++++---- packages/html/src/web_sys_bind/events.rs | 85 ++++++++---------- 4 files changed, 195 insertions(+), 81 deletions(-) diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index 51bca7d48..6f49f70f7 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -9,6 +9,8 @@ pub type MouseEvent = Event; #[derive(Clone, Default, PartialEq, Eq)] /// Data associated with a mouse event pub struct MouseData { + /// Common data for all pointer/mouse events + #[cfg_attr(feature = "serialize", serde(flatten))] point_data: PointData, } diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 4134ac852..0ba2b76b1 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -1,22 +1,16 @@ +use std::fmt::{Debug, Formatter}; + use dioxus_core::Event; +use crate::point_interaction::{PointData, PointInteraction}; + pub type PointerEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub struct PointerData { - // Mouse only - pub alt_key: bool, - pub button: i16, - pub buttons: u16, - pub client_x: i32, - pub client_y: i32, - pub ctrl_key: bool, - pub meta_key: bool, - pub page_x: i32, - pub page_y: i32, - pub screen_x: i32, - pub screen_y: i32, - pub shift_key: bool, + /// Common data for all pointer/mouse events + #[cfg_attr(feature = "serialize", serde(flatten))] + point_data: PointData, pub pointer_id: i32, pub width: i32, pub height: i32, @@ -27,7 +21,6 @@ pub struct PointerData { pub twist: i32, pub pointer_type: String, pub is_primary: bool, - // pub get_modifier_state: bool, } impl_event![ @@ -62,3 +55,60 @@ impl_event![ /// pointerout onpointerout ]; + +impl PointInteraction for PointerData { + fn get_point_data(&self) -> PointData { + self.point_data + } +} + +impl PointerData { + pub fn new( + point_data: PointData, + pointer_id: i32, + width: i32, + height: i32, + pressure: f32, + tangential_pressure: f32, + tilt_x: i32, + tilt_y: i32, + twist: i32, + pointer_type: String, + is_primary: bool, + ) -> Self { + Self { + point_data, + pointer_id, + width, + height, + pressure, + tangential_pressure, + tilt_x, + tilt_y, + twist, + pointer_type, + is_primary, + } + } +} + +impl Debug for PointerData { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PointerData") + .field("coordinates", &self.coordinates()) + .field("modifiers", &self.modifiers()) + .field("held_buttons", &self.held_buttons()) + .field("trigger_button", &self.trigger_button()) + .field("pointer_id", &self.pointer_id) + .field("width", &self.width) + .field("height", &self.height) + .field("pressure", &self.pressure) + .field("tangential_pressure", &self.tangential_pressure) + .field("tilt_x", &self.tilt_x) + .field("tilt_y", &self.tilt_y) + .field("twist", &self.twist) + .field("pointer_type", &self.pointer_type) + .field("is_primary", &self.is_primary) + .finish() + } +} diff --git a/packages/html/src/point_interaction.rs b/packages/html/src/point_interaction.rs index 9692420e8..29ff997d6 100644 --- a/packages/html/src/point_interaction.rs +++ b/packages/html/src/point_interaction.rs @@ -2,23 +2,68 @@ use keyboard_types::Modifiers; use crate::{ geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}, - input_data::{MouseButton, MouseButtonSet}, + input_data::{decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet}, }; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Copy, Clone, Default, PartialEq, Eq)] pub struct PointData { - pub trigger_button: MouseButton, + pub alt_key: bool, + + /// The button number that was pressed (if applicable) when the mouse event was fired. + pub button: i16, + + /// Indicates which buttons are pressed on the mouse (or other input device) when a mouse event is triggered. + /// + /// Each button that can be pressed is represented by a given number (see below). If more than one button is pressed, the button values are added together to produce a new number. For example, if the secondary (2) and auxiliary (4) buttons are pressed simultaneously, the value is 6 (i.e., 2 + 4). + /// + /// - 1: Primary button (usually the left button) + /// - 2: Secondary button (usually the right button) + /// - 4: Auxiliary button (usually the mouse wheel button or middle button) + /// - 8: 4th button (typically the "Browser Back" button) + /// - 16 : 5th button (typically the "Browser Forward" button) + pub buttons: u16, + + /// The horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). + /// + /// For example, clicking on the left edge of the viewport will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally. pub client_x: i32, + + /// The vertical coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). + /// + /// For example, clicking on the top edge of the viewport will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically. pub client_y: i32, - pub screen_x: i32, - pub screen_y: i32, - pub element_x: i32, - pub element_y: i32, + + /// True if the control key was down when the mouse event was fired. + pub ctrl_key: bool, + + /// True if the meta key was down when the mouse event was fired. + pub meta_key: bool, + + /// The offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node. + pub offset_x: i32, + + /// The offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node. + pub offset_y: i32, + + /// The X (horizontal) coordinate (in pixels) of the mouse, relative to the left edge of the entire document. This includes any portion of the document not currently visible. + /// + /// Being based on the edge of the document as it is, this property takes into account any horizontal scrolling of the page. For example, if the page is scrolled such that 200 pixels of the left side of the document are scrolled out of view, and the mouse is clicked 100 pixels inward from the left edge of the view, the value returned by pageX will be 300. pub page_x: i32, + + /// The Y (vertical) coordinate in pixels of the event relative to the whole document. + /// + /// See `page_x`. pub page_y: i32, - pub held_buttons: MouseButtonSet, - pub modifiers: Modifiers, + + /// The X coordinate of the mouse pointer in global (screen) coordinates. + pub screen_x: i32, + + /// The Y coordinate of the mouse pointer in global (screen) coordinates. + pub screen_y: i32, + + /// True if the shift key was down when the mouse event was fired. + pub shift_key: bool, } impl PointData { @@ -28,22 +73,32 @@ impl PointData { held_buttons: MouseButtonSet, modifiers: Modifiers, ) -> Self { + let alt_key = modifiers.contains(Modifiers::ALT); + let ctrl_key = modifiers.contains(Modifiers::CONTROL); + let meta_key = modifiers.contains(Modifiers::META); + let shift_key = modifiers.contains(Modifiers::SHIFT); + let [client_x, client_y]: [i32; 2] = coordinates.client().cast().into(); - let [element_x, element_y]: [i32; 2] = coordinates.element().cast().into(); + let [offset_x, offset_y]: [i32; 2] = coordinates.element().cast().into(); let [page_x, page_y]: [i32; 2] = coordinates.page().cast().into(); let [screen_x, screen_y]: [i32; 2] = coordinates.screen().cast().into(); Self { - trigger_button: trigger_button.map_or(MouseButton::default(), |b| b), + button: trigger_button + .map_or(MouseButton::default(), |b| b) + .into_web_code(), + buttons: encode_mouse_button_set(held_buttons), + meta_key, + ctrl_key, + shift_key, + alt_key, client_x, client_y, screen_x, screen_y, - element_x, - element_y, + offset_x, + offset_y, page_x, page_y, - held_buttons, - modifiers, } } } @@ -56,7 +111,7 @@ pub trait PointInteraction { Coordinates::new( ScreenPoint::new(point_data.screen_x.into(), point_data.screen_y.into()), ClientPoint::new(point_data.client_x.into(), point_data.client_y.into()), - ElementPoint::new(point_data.element_x.into(), point_data.element_y.into()), + ElementPoint::new(point_data.offset_x.into(), point_data.offset_y.into()), PagePoint::new(point_data.page_x.into(), point_data.page_y.into()), ) } @@ -73,7 +128,7 @@ pub trait PointInteraction { fn element_coordinates(&self) -> ElementPoint { let point_data = self.get_point_data(); - ElementPoint::new(point_data.element_x.into(), point_data.element_y.into()) + ElementPoint::new(point_data.offset_x.into(), point_data.offset_y.into()) } fn page_coordinates(&self) -> PagePoint { @@ -82,14 +137,30 @@ pub trait PointInteraction { } fn modifiers(&self) -> Modifiers { - self.get_point_data().modifiers + let mut modifiers = Modifiers::empty(); + let point_data = self.get_point_data(); + + if point_data.alt_key { + modifiers.insert(Modifiers::ALT); + } + if point_data.ctrl_key { + modifiers.insert(Modifiers::CONTROL); + } + if point_data.meta_key { + modifiers.insert(Modifiers::META); + } + if point_data.shift_key { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers } fn held_buttons(&self) -> MouseButtonSet { - self.get_point_data().held_buttons + decode_mouse_button_set(self.get_point_data().buttons) } fn trigger_button(&self) -> MouseButton { - self.get_point_data().trigger_button + MouseButton::from_web_code(self.get_point_data().button) } } diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index 4b87fcf41..c1214f870 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -2,7 +2,7 @@ use crate::events::{ AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData, TransitionData, WheelData, }; -use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; +use crate::input_data::decode_key_location; use crate::point_interaction::PointData; use crate::{DragData, MountedData}; use keyboard_types::{Code, Key, Modifiers}; @@ -90,33 +90,21 @@ impl From<&KeyboardEvent> for KeyboardData { impl From<&MouseEvent> for MouseData { fn from(e: &MouseEvent) -> Self { - let mut modifiers = Modifiers::empty(); - - if e.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if e.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if e.meta_key() { - modifiers.insert(Modifiers::META); - } - if e.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - MouseData::new(PointData { - trigger_button: MouseButton::from_web_code(e.button()), + Self::new(PointData { + button: e.button(), + buttons: e.buttons(), + meta_key: e.meta_key(), + alt_key: e.alt_key(), + ctrl_key: e.ctrl_key(), + shift_key: e.shift_key(), client_x: e.client_x(), client_y: e.client_y(), screen_x: e.screen_x(), screen_y: e.screen_y(), - element_x: e.offset_x(), - element_y: e.offset_y(), + offset_x: e.offset_x(), + offset_y: e.offset_y(), page_x: e.page_x(), page_y: e.page_y(), - held_buttons: decode_mouse_button_set(e.buttons()), - modifiers, }) } } @@ -142,31 +130,34 @@ impl From<&TouchEvent> for TouchData { impl From<&PointerEvent> for PointerData { fn from(e: &PointerEvent) -> Self { - Self { - alt_key: e.alt_key(), - button: e.button(), - buttons: e.buttons(), - client_x: e.client_x(), - client_y: e.client_y(), - ctrl_key: e.ctrl_key(), - meta_key: e.meta_key(), - page_x: e.page_x(), - page_y: e.page_y(), - screen_x: e.screen_x(), - screen_y: e.screen_y(), - shift_key: e.shift_key(), - pointer_id: e.pointer_id(), - width: e.width(), - height: e.height(), - pressure: e.pressure(), - tangential_pressure: e.tangential_pressure(), - tilt_x: e.tilt_x(), - tilt_y: e.tilt_y(), - twist: e.twist(), - pointer_type: e.pointer_type(), - is_primary: e.is_primary(), - // get_modifier_state: evt.get_modifier_state(), - } + Self::new( + PointData { + button: e.button(), + buttons: e.buttons(), + meta_key: e.meta_key(), + alt_key: e.alt_key(), + ctrl_key: e.ctrl_key(), + shift_key: e.shift_key(), + client_x: e.client_x(), + client_y: e.client_y(), + screen_x: e.screen_x(), + screen_y: e.screen_y(), + offset_x: e.offset_x(), + offset_y: e.offset_y(), + page_x: e.page_x(), + page_y: e.page_y(), + }, + e.pointer_id(), + e.width(), + e.height(), + e.pressure(), + e.tangential_pressure(), + e.tilt_x(), + e.tilt_y(), + e.twist(), + e.pointer_type(), + e.is_primary(), + ) } } From e0f42f8173967e5c40011b8ac93add4e24141a6c Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 13:31:42 -0400 Subject: [PATCH 03/56] Fix rink --- packages/html/src/events/mouse.rs | 14 +- packages/html/src/events/pointer.rs | 190 ++++++++++++++++------- packages/html/src/lib.rs | 2 +- packages/html/src/point_interaction.rs | 25 ++- packages/html/src/web_sys_bind/events.rs | 24 +-- packages/rink/src/hooks.rs | 21 +-- packages/rink/src/widgets/slider.rs | 4 +- packages/rink/src/widgets/text_like.rs | 4 +- 8 files changed, 187 insertions(+), 97 deletions(-) diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index 6f49f70f7..74b7bdfb9 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -1,12 +1,11 @@ use crate::point_interaction::{PointData, PointInteraction}; use dioxus_core::Event; -use std::fmt::{Debug, Formatter}; pub type MouseEvent = Event; /// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] /// Data associated with a mouse event pub struct MouseData { /// Common data for all pointer/mouse events @@ -89,14 +88,3 @@ impl PointInteraction for MouseData { self.point_data } } - -impl Debug for MouseData { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("MouseData") - .field("coordinates", &self.coordinates()) - .field("modifiers", &self.modifiers()) - .field("held_buttons", &self.held_buttons()) - .field("trigger_button", &self.trigger_button()) - .finish() - } -} diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 0ba2b76b1..62e65bf76 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -1,16 +1,16 @@ -use std::fmt::{Debug, Formatter}; +use std::fmt::Debug; use dioxus_core::Event; use crate::point_interaction::{PointData, PointInteraction}; +/// A synthetic event that wraps a web-style [`PointerEvent`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) pub type PointerEvent = Event; + +/// Data associated with a pointer event, aside from the data shared with mouse events #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, PartialEq)] -pub struct PointerData { - /// Common data for all pointer/mouse events - #[cfg_attr(feature = "serialize", serde(flatten))] - point_data: PointData, +#[derive(Debug, Clone, PartialEq)] +pub struct PointerEventData { pub pointer_id: i32, pub width: i32, pub height: i32, @@ -23,6 +23,54 @@ pub struct PointerData { pub is_primary: bool, } +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq)] +pub struct PointerData { + /// Common data for all pointer/mouse events + #[cfg_attr(feature = "serialize", serde(flatten))] + point_data: PointData, + + /// The unique identifier of the pointer causing the event. + #[deprecated(since = "0.5.0", note = "use pointer_id() instead")] + pub pointer_id: i32, + + /// The width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer. + #[deprecated(since = "0.5.0", note = "use width() instead")] + pub width: i32, + + /// The height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer. + #[deprecated(since = "0.5.0", note = "use height() instead")] + pub height: i32, + + /// The normalized pressure of the pointer input in the range of 0 to 1, + #[deprecated(since = "0.5.0", note = "use pressure() instead")] + pub pressure: f32, + + /// The normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1, + #[deprecated(since = "0.5.0", note = "use tangential_pressure() instead")] + pub tangential_pressure: f32, + + /// The plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis. + #[deprecated(since = "0.5.0", note = "use tilt_x() instead")] + pub tilt_x: i32, + + /// The plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis. + #[deprecated(since = "0.5.0", note = "use tilt_y() instead")] + pub tilt_y: i32, + + /// The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359. + #[deprecated(since = "0.5.0", note = "use twist() instead")] + pub twist: i32, + + /// Indicates the device type that caused the event (mouse, pen, touch, etc.). + #[deprecated(since = "0.5.0", note = "use pointer_type() instead")] + pub pointer_type: String, + + /// Indicates if the pointer represents the primary pointer of this pointer type. + #[deprecated(since = "0.5.0", note = "use is_primary() instead")] + pub is_primary: bool, +} + impl_event![ PointerData; /// pointerdown @@ -56,59 +104,87 @@ impl_event![ onpointerout ]; +impl PointerData { + pub fn new(point_data: PointData, pointer_event_data: PointerEventData) -> Self { + #[allow(deprecated)] + Self { + point_data, + pointer_id: pointer_event_data.pointer_id, + width: pointer_event_data.width, + height: pointer_event_data.height, + pressure: pointer_event_data.pressure, + tangential_pressure: pointer_event_data.tangential_pressure, + tilt_x: pointer_event_data.tilt_x, + tilt_y: pointer_event_data.tilt_y, + twist: pointer_event_data.twist, + pointer_type: pointer_event_data.pointer_type, + is_primary: pointer_event_data.is_primary, + } + } + + /// Gets the unique identifier of the pointer causing the event. + pub fn pointer_id(&self) -> i32 { + #[allow(deprecated)] + self.pointer_id + } + + /// Gets the width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer. + pub fn width(&self) -> i32 { + #[allow(deprecated)] + self.width + } + + /// Gets the height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer. + pub fn height(&self) -> i32 { + #[allow(deprecated)] + self.height + } + + /// Gets the normalized pressure of the pointer input in the range of 0 to 1, + pub fn pressure(&self) -> f32 { + #[allow(deprecated)] + self.pressure + } + + /// Gets the normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1, + pub fn tangential_pressure(&self) -> f32 { + #[allow(deprecated)] + self.tangential_pressure + } + + /// Gets the plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis. + pub fn tilt_x(&self) -> i32 { + #[allow(deprecated)] + self.tilt_x + } + + /// Gets the plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis. + pub fn tilt_y(&self) -> i32 { + #[allow(deprecated)] + self.tilt_y + } + + /// Gets the clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359. + pub fn twist(&self) -> i32 { + #[allow(deprecated)] + self.twist + } + + /// Gets the device type that caused the event (mouse, pen, touch, etc.). + pub fn pointer_type(&self) -> &str { + #[allow(deprecated)] + self.pointer_type.as_str() + } + + /// Gets if the pointer represents the primary pointer of this pointer type. + pub fn is_primary(&self) -> bool { + #[allow(deprecated)] + self.is_primary + } +} + impl PointInteraction for PointerData { fn get_point_data(&self) -> PointData { self.point_data } } - -impl PointerData { - pub fn new( - point_data: PointData, - pointer_id: i32, - width: i32, - height: i32, - pressure: f32, - tangential_pressure: f32, - tilt_x: i32, - tilt_y: i32, - twist: i32, - pointer_type: String, - is_primary: bool, - ) -> Self { - Self { - point_data, - pointer_id, - width, - height, - pressure, - tangential_pressure, - tilt_x, - tilt_y, - twist, - pointer_type, - is_primary, - } - } -} - -impl Debug for PointerData { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PointerData") - .field("coordinates", &self.coordinates()) - .field("modifiers", &self.modifiers()) - .field("held_buttons", &self.held_buttons()) - .field("trigger_button", &self.trigger_button()) - .field("pointer_id", &self.pointer_id) - .field("width", &self.width) - .field("height", &self.height) - .field("pressure", &self.pressure) - .field("tangential_pressure", &self.tangential_pressure) - .field("tilt_x", &self.tilt_x) - .field("tilt_y", &self.tilt_y) - .field("twist", &self.twist) - .field("pointer_type", &self.pointer_type) - .field("is_primary", &self.is_primary) - .finish() - } -} diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index d555d798a..5866ed28a 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -22,7 +22,7 @@ mod global_attributes; pub mod input_data; #[cfg(feature = "native-bind")] pub mod native_bind; -mod point_interaction; +pub mod point_interaction; mod render_template; #[cfg(feature = "wasm-bind")] mod web_sys_bind; diff --git a/packages/html/src/point_interaction.rs b/packages/html/src/point_interaction.rs index 29ff997d6..f25a198cc 100644 --- a/packages/html/src/point_interaction.rs +++ b/packages/html/src/point_interaction.rs @@ -1,3 +1,5 @@ +use std::fmt::{Debug, Formatter}; + use keyboard_types::Modifiers; use crate::{ @@ -69,8 +71,8 @@ pub struct PointData { impl PointData { pub fn new( trigger_button: Option, - coordinates: Coordinates, held_buttons: MouseButtonSet, + coordinates: Coordinates, modifiers: Modifiers, ) -> Self { let alt_key = modifiers.contains(Modifiers::ALT); @@ -103,6 +105,23 @@ impl PointData { } } +impl Debug for PointData { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PointInteraction") + .field("coordinates", &self.coordinates()) + .field("modifiers", &self.modifiers()) + .field("held_buttons", &self.held_buttons()) + .field("trigger_button", &self.trigger_button()) + .finish() + } +} + +impl PointInteraction for PointData { + fn get_point_data(&self) -> PointData { + *self + } +} + pub trait PointInteraction { fn get_point_data(&self) -> PointData; @@ -160,7 +179,7 @@ pub trait PointInteraction { decode_mouse_button_set(self.get_point_data().buttons) } - fn trigger_button(&self) -> MouseButton { - MouseButton::from_web_code(self.get_point_data().button) + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.get_point_data().button)) } } diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index c1214f870..f175e98e2 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -4,7 +4,7 @@ use crate::events::{ }; use crate::input_data::decode_key_location; use crate::point_interaction::PointData; -use crate::{DragData, MountedData}; +use crate::{DragData, MountedData, PointerEventData}; use keyboard_types::{Code, Key, Modifiers}; use std::convert::TryInto; use std::str::FromStr; @@ -147,16 +147,18 @@ impl From<&PointerEvent> for PointerData { page_x: e.page_x(), page_y: e.page_y(), }, - e.pointer_id(), - e.width(), - e.height(), - e.pressure(), - e.tangential_pressure(), - e.tilt_x(), - e.tilt_y(), - e.twist(), - e.pointer_type(), - e.is_primary(), + PointerEventData { + pointer_id: e.pointer_id(), + width: e.width(), + height: e.height(), + pressure: e.pressure(), + tangential_pressure: e.tangential_pressure(), + tilt_x: e.tilt_x(), + tilt_y: e.tilt_y(), + twist: e.twist(), + pointer_type: e.pointer_type(), + is_primary: e.is_primary(), + }, ) } } diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index f8c4500d2..7e85f0e2d 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -13,6 +13,7 @@ use dioxus_html::geometry::{ use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers}; use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons; use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet}; +use dioxus_html::point_interaction::{PointData, PointInteraction}; use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, WheelData}; use std::any::Any; use std::collections::HashMap; @@ -133,12 +134,12 @@ impl InnerInputState { _ => {} } - let new_mouse_data = MouseData::new( - m.coordinates(), + let new_mouse_data = MouseData::new(PointData::new( m.trigger_button(), held_buttons, + m.coordinates(), m.modifiers(), - ); + )); self.mouse = Some(new_mouse_data.clone()); *m = new_mouse_data; @@ -291,12 +292,12 @@ impl InnerInputState { mouse_data.page_coordinates(), ); - MouseData::new( - coordinates, + MouseData::new(PointData::new( mouse_data.trigger_button(), mouse_data.held_buttons(), + coordinates, mouse_data.modifiers(), - ) + )) } if let Some(mouse_data) = &self.mouse { @@ -715,12 +716,12 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> { } // held mouse buttons get set later by maintaining state, as crossterm does not provide them - EventData::Mouse(MouseData::new( - coordinates, + EventData::Mouse(MouseData::new(PointData::new( button, - DioxusMouseButtons::empty(), + MouseButtonSet::empty(), + coordinates, modifiers, - )) + ))) }; let get_wheel_data = |up| { diff --git a/packages/rink/src/widgets/slider.rs b/packages/rink/src/widgets/slider.rs index 7080b6c12..5b24e180d 100644 --- a/packages/rink/src/widgets/slider.rs +++ b/packages/rink/src/widgets/slider.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; -use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData}; +use dioxus_html::{ + input_data::keyboard_types::Key, point_interaction::PointInteraction, KeyboardData, MouseData, +}; use dioxus_native_core::{ custom_element::CustomElement, node::{OwnedAttributeDiscription, OwnedAttributeValue}, diff --git a/packages/rink/src/widgets/text_like.rs b/packages/rink/src/widgets/text_like.rs index 6521073d0..1f96cfa61 100644 --- a/packages/rink/src/widgets/text_like.rs +++ b/packages/rink/src/widgets/text_like.rs @@ -1,7 +1,9 @@ use std::{collections::HashMap, io::stdout}; use crossterm::{cursor::MoveTo, execute}; -use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData}; +use dioxus_html::{ + input_data::keyboard_types::Key, point_interaction::PointInteraction, KeyboardData, MouseData, +}; use dioxus_native_core::{ custom_element::CustomElement, node::OwnedAttributeDiscription, From b771386c1389f9edb2dbf924694f284b667de456 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 14:02:25 -0400 Subject: [PATCH 04/56] fix dioxusmousebuttons --- packages/rink/src/hooks.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index 7e85f0e2d..a8c611c50 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -11,8 +11,9 @@ use dioxus_html::geometry::{ ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint, WheelDelta, }; use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers}; -use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons; -use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet}; +use dioxus_html::input_data::{ + MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons, +}; use dioxus_html::point_interaction::{PointData, PointInteraction}; use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, WheelData}; use std::any::Any; @@ -115,7 +116,7 @@ impl InnerInputState { EventData::Mouse(ref mut m) => { let mut held_buttons = match &self.mouse { Some(previous_data) => previous_data.held_buttons(), - None => MouseButtonSet::empty(), + None => DioxusMouseButtons::empty(), }; match evt.0 { @@ -306,7 +307,7 @@ impl InnerInputState { // a mouse button is pressed if a button was not down and is now down let previous_buttons = previous_mouse - .map_or(MouseButtonSet::empty(), |previous_data| { + .map_or(DioxusMouseButtons::empty(), |previous_data| { previous_data.held_buttons() }); let was_pressed = !(mouse_data.held_buttons() - previous_buttons).is_empty(); @@ -718,7 +719,7 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> { // held mouse buttons get set later by maintaining state, as crossterm does not provide them EventData::Mouse(MouseData::new(PointData::new( button, - MouseButtonSet::empty(), + DioxusMouseButtons::empty(), coordinates, modifiers, ))) From af9875f9b1ab363c9e82ff31d2fa98c84f0e30a9 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 14:10:18 -0400 Subject: [PATCH 05/56] revert change to cargo --- packages/html/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 83706d3bc..5644b440c 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -16,7 +16,7 @@ serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } wasm-bindgen = { workspace = true, optional = true } euclid = "0.22.7" -enumset = { version = "1.0.11", features = ["serde"] } +enumset = "1.0.11" keyboard-types = "0.7" async-trait = "0.1.58" serde-value = "0.7.0" From 56095116e263b7e7377e169ee5b8a1118ece2073 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 17:58:43 -0400 Subject: [PATCH 06/56] Fix tests --- packages/desktop/headless_tests/events.rs | 1 + packages/dioxus-tui/examples/colorpicker.rs | 11 ++-- packages/dioxus-tui/examples/hover.rs | 34 +++++------- packages/dioxus-tui/tests/events.rs | 59 +++++++-------------- packages/html/src/lib.rs | 1 + packages/rink/src/hooks.rs | 4 +- packages/rink/src/widgets/slider.rs | 4 +- 7 files changed, 43 insertions(+), 71 deletions(-) diff --git a/packages/desktop/headless_tests/events.rs b/packages/desktop/headless_tests/events.rs index ac62cd37b..d4439aec6 100644 --- a/packages/desktop/headless_tests/events.rs +++ b/packages/desktop/headless_tests/events.rs @@ -1,6 +1,7 @@ use dioxus::html::geometry::euclid::Vector3D; use dioxus::prelude::*; use dioxus_desktop::DesktopContext; +use dioxus_html::PointInteraction; pub(crate) fn check_app_exits(app: Component) { use dioxus_desktop::tao::window::WindowBuilder; diff --git a/packages/dioxus-tui/examples/colorpicker.rs b/packages/dioxus-tui/examples/colorpicker.rs index 00f8ef7e0..7a358da3e 100644 --- a/packages/dioxus-tui/examples/colorpicker.rs +++ b/packages/dioxus-tui/examples/colorpicker.rs @@ -1,5 +1,6 @@ use dioxus::core::RenderReturn; use dioxus::prelude::*; +use dioxus_html::PointInteraction; use dioxus_tui::DioxusElementToNodeId; use dioxus_tui::Query; use dioxus_tui::Size; @@ -15,21 +16,21 @@ fn app(cx: Scope) -> Element { let mapping: DioxusElementToNodeId = cx.consume_context().unwrap(); // disable templates so that every node has an id and can be queried cx.render(rsx! { - div{ + div { width: "100%", background_color: "hsl({hue}, 70%, {brightness}%)", onmousemove: move |evt| { if let RenderReturn::Ready(node) = cx.root_node() { if let Some(id) = node.root_ids.borrow().get(0).cloned() { let node = tui_query.get(mapping.get_node_id(id).unwrap()); - let Size{width, height} = node.size().unwrap(); + let Size { width, height } = node.size().unwrap(); let pos = evt.inner().element_coordinates(); - hue.set((pos.x as f32/width as f32)*255.0); - brightness.set((pos.y as f32/height as f32)*100.0); + hue.set((pos.x as f32 / width as f32) * 255.0); + brightness.set((pos.y as f32 / height as f32) * 100.0); } } }, - "hsl({hue}, 70%, {brightness}%)", + "hsl({hue}, 70%, {brightness}%)" } }) } diff --git a/packages/dioxus-tui/examples/hover.rs b/packages/dioxus-tui/examples/hover.rs index 3af2d1616..3c29521e5 100644 --- a/packages/dioxus-tui/examples/hover.rs +++ b/packages/dioxus-tui/examples/hover.rs @@ -1,5 +1,6 @@ use dioxus::{events::MouseData, prelude::*}; use dioxus_core::Event; +use dioxus_html::PointInteraction; use std::convert::TryInto; use std::fmt::Write; use std::rc::Rc; @@ -52,15 +53,9 @@ fn app(cx: Scope) -> Element { }; cx.render(rsx! { - div { - width: "100%", - height: "100%", - flex_direction: "column", + div { width: "100%", height: "100%", flex_direction: "column", - div { - width: "100%", - height: "50%", - flex_direction: "row", + div { width: "100%", height: "50%", flex_direction: "row", div { border_width: "1px", width: "50%", @@ -71,7 +66,7 @@ fn app(cx: Scope) -> Element { onmouseenter: move |m| q1_color.set([get_brightness(m.inner()), 0, 0]), onmousedown: move |m| q1_color.set([get_brightness(m.inner()), 0, 0]), onmouseup: move |m| q1_color.set([get_brightness(m.inner()), 0, 0]), - onwheel: move |w| q1_color.set([q1_color[0] + (10.0*w.delta().strip_units().y) as i32, 0, 0]), + onwheel: move |w| q1_color.set([q1_color[0] + (10.0 * w.delta().strip_units().y) as i32, 0, 0]), onmouseleave: move |_| q1_color.set([200; 3]), onmousemove: update_data, "click me" @@ -85,17 +80,14 @@ fn app(cx: Scope) -> Element { onmouseenter: move |m| q2_color.set([get_brightness(m.inner()); 3]), onmousedown: move |m| q2_color.set([get_brightness(m.inner()); 3]), onmouseup: move |m| q2_color.set([get_brightness(m.inner()); 3]), - onwheel: move |w| q2_color.set([q2_color[0] + (10.0*w.delta().strip_units().y) as i32;3]), + onwheel: move |w| q2_color.set([q2_color[0] + (10.0 * w.delta().strip_units().y) as i32; 3]), onmouseleave: move |_| q2_color.set([200; 3]), onmousemove: update_data, "click me" } } - div { - width: "100%", - height: "50%", - flex_direction: "row", + div { width: "100%", height: "50%", flex_direction: "row", div { width: "50%", height: "100%", @@ -105,7 +97,7 @@ fn app(cx: Scope) -> Element { onmouseenter: move |m| q3_color.set([0, get_brightness(m.inner()), 0]), onmousedown: move |m| q3_color.set([0, get_brightness(m.inner()), 0]), onmouseup: move |m| q3_color.set([0, get_brightness(m.inner()), 0]), - onwheel: move |w| q3_color.set([0, q3_color[1] + (10.0*w.delta().strip_units().y) as i32, 0]), + onwheel: move |w| q3_color.set([0, q3_color[1] + (10.0 * w.delta().strip_units().y) as i32, 0]), onmouseleave: move |_| q3_color.set([200; 3]), onmousemove: update_data, "click me" @@ -119,16 +111,16 @@ fn app(cx: Scope) -> Element { onmouseenter: move |m| q4_color.set([0, 0, get_brightness(m.inner())]), onmousedown: move |m| q4_color.set([0, 0, get_brightness(m.inner())]), onmouseup: move |m| q4_color.set([0, 0, get_brightness(m.inner())]), - onwheel: move |w| q4_color.set([0, 0, q4_color[2] + (10.0*w.delta().strip_units().y) as i32]), + onwheel: move |w| q4_color.set([0, 0, q4_color[2] + (10.0 * w.delta().strip_units().y) as i32]), onmouseleave: move |_| q4_color.set([200; 3]), onmousemove: update_data, "click me" } - }, - div {"Page coordinates: {page_coordinates}"}, - div {"Element coordinates: {element_coordinates}"}, - div {"Buttons: {buttons}"}, - div {"Modifiers: {modifiers}"}, + } + div { "Page coordinates: {page_coordinates}" } + div { "Element coordinates: {element_coordinates}" } + div { "Buttons: {buttons}" } + div { "Modifiers: {modifiers}" } } }) } diff --git a/packages/dioxus-tui/tests/events.rs b/packages/dioxus-tui/tests/events.rs index 671cfc349..01fea9934 100644 --- a/packages/dioxus-tui/tests/events.rs +++ b/packages/dioxus-tui/tests/events.rs @@ -1,6 +1,7 @@ use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent}; use dioxus::prelude::*; use dioxus_html::input_data::keyboard_types::Code; +use dioxus_html::PointInteraction; use dioxus_tui::TuiContext; use std::future::Future; use std::pin::Pin; @@ -63,7 +64,7 @@ fn key_down() { onkeydown: move |evt| { assert_eq!(evt.data.code(), Code::KeyA); tui_ctx.quit(); - }, + } } }) } @@ -95,9 +96,11 @@ fn mouse_down() { width: "100%", height: "100%", onmousedown: move |evt| { - assert!(evt.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary)); + assert!( + evt.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary) + ); tui_ctx.quit(); - }, + } } }) } @@ -131,13 +134,9 @@ fn mouse_up() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "100%", - height: "100%", - onmouseup: move |_| { + div { width: "100%", height: "100%", onmouseup: move |_| { tui_ctx.quit(); - }, - } + } } }) } } @@ -170,13 +169,9 @@ fn mouse_enter() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "50%", - height: "50%", - onmouseenter: move |_| { + div { width: "50%", height: "50%", onmouseenter: move |_| { tui_ctx.quit(); - }, - } + } } }) } } @@ -209,13 +204,9 @@ fn mouse_exit() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "50%", - height: "50%", - onmouseenter: move |_| { + div { width: "50%", height: "50%", onmouseenter: move |_| { tui_ctx.quit(); - }, - } + } } }) } } @@ -248,13 +239,9 @@ fn mouse_move() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "100%", - height: "100%", - onmousemove: move |_|{ + div { width: "100%", height: "100%", onmousemove: move |_| { tui_ctx.quit(); - }, - } + } } }) } } @@ -293,7 +280,7 @@ fn wheel() { onwheel: move |evt| { assert!(evt.data.delta().strip_units().y > 0.0); tui_ctx.quit(); - }, + } } }) } @@ -327,13 +314,9 @@ fn click() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "100%", - height: "100%", - onclick: move |_|{ + div { width: "100%", height: "100%", onclick: move |_| { tui_ctx.quit(); - }, - } + } } }) } } @@ -366,13 +349,9 @@ fn context_menu() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "100%", - height: "100%", - oncontextmenu: move |_|{ + div { width: "100%", height: "100%", oncontextmenu: move |_| { tui_ctx.quit(); - }, - } + } } }) } } diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 5866ed28a..973d6e750 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -36,6 +36,7 @@ pub use transit::*; pub use elements::*; pub use events::*; pub use global_attributes::*; +pub use point_interaction::PointInteraction; pub use render_template::*; mod eval; diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index a8c611c50..106e0dc41 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -14,8 +14,8 @@ use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers}; use dioxus_html::input_data::{ MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons, }; -use dioxus_html::point_interaction::{PointData, PointInteraction}; -use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, WheelData}; +use dioxus_html::point_interaction::PointData; +use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, PointInteraction, WheelData}; use std::any::Any; use std::collections::HashMap; use std::{ diff --git a/packages/rink/src/widgets/slider.rs b/packages/rink/src/widgets/slider.rs index 5b24e180d..4a78f2a79 100644 --- a/packages/rink/src/widgets/slider.rs +++ b/packages/rink/src/widgets/slider.rs @@ -1,8 +1,6 @@ use std::collections::HashMap; -use dioxus_html::{ - input_data::keyboard_types::Key, point_interaction::PointInteraction, KeyboardData, MouseData, -}; +use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData, PointInteraction}; use dioxus_native_core::{ custom_element::CustomElement, node::{OwnedAttributeDiscription, OwnedAttributeValue}, From e8b95993ac7b4c63f3508ee123fa508698cc066a Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 18:33:27 -0400 Subject: [PATCH 07/56] fix text_like import --- packages/rink/src/widgets/text_like.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/rink/src/widgets/text_like.rs b/packages/rink/src/widgets/text_like.rs index 1f96cfa61..b2d12efa2 100644 --- a/packages/rink/src/widgets/text_like.rs +++ b/packages/rink/src/widgets/text_like.rs @@ -1,9 +1,7 @@ use std::{collections::HashMap, io::stdout}; use crossterm::{cursor::MoveTo, execute}; -use dioxus_html::{ - input_data::keyboard_types::Key, point_interaction::PointInteraction, KeyboardData, MouseData, -}; +use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData, PointInteraction}; use dioxus_native_core::{ custom_element::CustomElement, node::OwnedAttributeDiscription, From 500051e2b26844a257ef65dcf75cf6494b1951ed Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 20:23:54 -0400 Subject: [PATCH 08/56] fix formatting issue --- packages/dioxus-tui/tests/events.rs | 48 +++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/packages/dioxus-tui/tests/events.rs b/packages/dioxus-tui/tests/events.rs index 01fea9934..6282ef21d 100644 --- a/packages/dioxus-tui/tests/events.rs +++ b/packages/dioxus-tui/tests/events.rs @@ -134,9 +134,13 @@ fn mouse_up() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { width: "100%", height: "100%", onmouseup: move |_| { + div { + width: "100%", + height: "100%", + onmouseup: move |_| { tui_ctx.quit(); - } } + } + } }) } } @@ -169,9 +173,13 @@ fn mouse_enter() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { width: "50%", height: "50%", onmouseenter: move |_| { + div { + width: "50%", + height: "50%", + onmouseenter: move |_| { tui_ctx.quit(); - } } + } + } }) } } @@ -204,9 +212,13 @@ fn mouse_exit() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { width: "50%", height: "50%", onmouseenter: move |_| { + div { + width: "50%", + height: "50%", + onmouseenter: move |_| { tui_ctx.quit(); - } } + } + } }) } } @@ -239,9 +251,13 @@ fn mouse_move() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { width: "100%", height: "100%", onmousemove: move |_| { + div { + width: "100%", + height: "100%", + onmousemove: move |_| { tui_ctx.quit(); - } } + } + } }) } } @@ -314,9 +330,13 @@ fn click() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { width: "100%", height: "100%", onclick: move |_| { + div { + width: "100%", + height: "100%", + onclick: move |_| { tui_ctx.quit(); - } } + } + } }) } } @@ -349,9 +369,13 @@ fn context_menu() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { width: "100%", height: "100%", oncontextmenu: move |_| { + div { + width: "100%", + height: "100%", + oncontextmenu: move |_| { tui_ctx.quit(); - } } + } + } }) } } From a8af958d759e4a353bb1ce5c716e6811d04d54d9 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 20:39:34 -0400 Subject: [PATCH 09/56] Add dxfmt::skip and workspace recommendations --- .vscode/extensions.json | 6 ++++ .vscode/settings.json | 5 ++- packages/dioxus-tui/tests/events.rs | 54 ++++++++++++++++------------- 3 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..0ec60d272 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "dioxuslabs.dioxus", + "rust-lang.rust-analyzer" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index ebd52887e..4cf40036e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,10 @@ { - "editor.formatOnSave": true, + "editor.formatOnSave": false, "[toml]": { "editor.formatOnSave": false }, + "[rust]": { + "editor.defaultFormatter": "DioxusLabs.dioxus" + }, "rust-analyzer.checkOnSave.allTargets": false, } diff --git a/packages/dioxus-tui/tests/events.rs b/packages/dioxus-tui/tests/events.rs index 6282ef21d..7a93d3ebc 100644 --- a/packages/dioxus-tui/tests/events.rs +++ b/packages/dioxus-tui/tests/events.rs @@ -134,12 +134,13 @@ fn mouse_up() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "100%", - height: "100%", + #[dxfmt::skip] + div { + width: "100%", + height: "100%", onmouseup: move |_| { tui_ctx.quit(); - } + } } }) } @@ -173,12 +174,13 @@ fn mouse_enter() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "50%", - height: "50%", + #[dxfmt::skip] + div { + width: "50%", + height: "50%", onmouseenter: move |_| { tui_ctx.quit(); - } + } } }) } @@ -212,12 +214,13 @@ fn mouse_exit() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "50%", - height: "50%", + #[dxfmt::skip] + div { + width: "50%", + height: "50%", onmouseenter: move |_| { tui_ctx.quit(); - } + } } }) } @@ -251,12 +254,13 @@ fn mouse_move() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "100%", - height: "100%", + #[dxfmt::skip] + div { + width: "100%", + height: "100%", onmousemove: move |_| { tui_ctx.quit(); - } + } } }) } @@ -330,12 +334,13 @@ fn click() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "100%", - height: "100%", + #[dxfmt::skip] + div { + width: "100%", + height: "100%", onclick: move |_| { tui_ctx.quit(); - } + } } }) } @@ -369,12 +374,13 @@ fn context_menu() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - div { - width: "100%", - height: "100%", + #[dxfmt::skip] + div { + width: "100%", + height: "100%", oncontextmenu: move |_| { tui_ctx.quit(); - } + } } }) } From 4e584fb2c29cbb5c01ee91f8566b05817e1f2c42 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 20:40:06 -0400 Subject: [PATCH 10/56] add even better toml --- .vscode/extensions.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0ec60d272..90881959b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,7 @@ { "recommendations": [ "dioxuslabs.dioxus", - "rust-lang.rust-analyzer" + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml" ] } \ No newline at end of file From bad30786e28992d52a86fef50989ec005996b9a7 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 22:09:39 -0400 Subject: [PATCH 11/56] address comments --- .vscode/settings.json | 4 +- packages/html/src/events/pointer.rs | 51 +++++++++++------------- packages/html/src/lib.rs | 2 +- packages/html/src/web_sys_bind/events.rs | 24 +++++------ packages/rink/src/hooks.rs | 2 +- packages/rink/src/widgets/slider.rs | 2 +- packages/rink/src/widgets/text_like.rs | 2 +- 7 files changed, 41 insertions(+), 46 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4cf40036e..42dfebaab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,10 @@ { - "editor.formatOnSave": false, + "editor.formatOnSave": true, "[toml]": { "editor.formatOnSave": false }, "[rust]": { - "editor.defaultFormatter": "DioxusLabs.dioxus" + "editor.defaultFormatter": "rust-lang.rust-analyzer" }, "rust-analyzer.checkOnSave.allTargets": false, } diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 62e65bf76..9277568d0 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -7,22 +7,6 @@ use crate::point_interaction::{PointData, PointInteraction}; /// A synthetic event that wraps a web-style [`PointerEvent`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) pub type PointerEvent = Event; -/// Data associated with a pointer event, aside from the data shared with mouse events -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq)] -pub struct PointerEventData { - pub pointer_id: i32, - pub width: i32, - pub height: i32, - pub pressure: f32, - pub tangential_pressure: f32, - pub tilt_x: i32, - pub tilt_y: i32, - pub twist: i32, - pub pointer_type: String, - pub is_primary: bool, -} - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq)] pub struct PointerData { @@ -105,20 +89,33 @@ impl_event![ ]; impl PointerData { - pub fn new(point_data: PointData, pointer_event_data: PointerEventData) -> Self { + #[allow(clippy::too_many_arguments)] + pub fn new( + point_data: PointData, + pointer_id: i32, + width: i32, + height: i32, + pressure: f32, + tangential_pressure: f32, + tilt_x: i32, + tilt_y: i32, + twist: i32, + pointer_type: String, + is_primary: bool, + ) -> Self { #[allow(deprecated)] Self { point_data, - pointer_id: pointer_event_data.pointer_id, - width: pointer_event_data.width, - height: pointer_event_data.height, - pressure: pointer_event_data.pressure, - tangential_pressure: pointer_event_data.tangential_pressure, - tilt_x: pointer_event_data.tilt_x, - tilt_y: pointer_event_data.tilt_y, - twist: pointer_event_data.twist, - pointer_type: pointer_event_data.pointer_type, - is_primary: pointer_event_data.is_primary, + pointer_id, + width, + height, + pressure, + tangential_pressure, + tilt_x, + tilt_y, + twist, + pointer_type, + is_primary, } } diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 973d6e750..d362d71f1 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -36,7 +36,6 @@ pub use transit::*; pub use elements::*; pub use events::*; pub use global_attributes::*; -pub use point_interaction::PointInteraction; pub use render_template::*; mod eval; @@ -44,4 +43,5 @@ mod eval; pub mod prelude { pub use crate::eval::*; pub use crate::events::*; + pub use crate::point_interaction::PointInteraction; } diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index f175e98e2..c1214f870 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -4,7 +4,7 @@ use crate::events::{ }; use crate::input_data::decode_key_location; use crate::point_interaction::PointData; -use crate::{DragData, MountedData, PointerEventData}; +use crate::{DragData, MountedData}; use keyboard_types::{Code, Key, Modifiers}; use std::convert::TryInto; use std::str::FromStr; @@ -147,18 +147,16 @@ impl From<&PointerEvent> for PointerData { page_x: e.page_x(), page_y: e.page_y(), }, - PointerEventData { - pointer_id: e.pointer_id(), - width: e.width(), - height: e.height(), - pressure: e.pressure(), - tangential_pressure: e.tangential_pressure(), - tilt_x: e.tilt_x(), - tilt_y: e.tilt_y(), - twist: e.twist(), - pointer_type: e.pointer_type(), - is_primary: e.is_primary(), - }, + e.pointer_id(), + e.width(), + e.height(), + e.pressure(), + e.tangential_pressure(), + e.tilt_x(), + e.tilt_y(), + e.twist(), + e.pointer_type(), + e.is_primary(), ) } } diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index 106e0dc41..4a07992de 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -15,7 +15,7 @@ use dioxus_html::input_data::{ MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons, }; use dioxus_html::point_interaction::PointData; -use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, PointInteraction, WheelData}; +use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, prelude::PointInteraction, WheelData}; use std::any::Any; use std::collections::HashMap; use std::{ diff --git a/packages/rink/src/widgets/slider.rs b/packages/rink/src/widgets/slider.rs index 4a78f2a79..61bb820b4 100644 --- a/packages/rink/src/widgets/slider.rs +++ b/packages/rink/src/widgets/slider.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData, PointInteraction}; +use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData, prelude::PointInteraction}; use dioxus_native_core::{ custom_element::CustomElement, node::{OwnedAttributeDiscription, OwnedAttributeValue}, diff --git a/packages/rink/src/widgets/text_like.rs b/packages/rink/src/widgets/text_like.rs index b2d12efa2..bcba2d994 100644 --- a/packages/rink/src/widgets/text_like.rs +++ b/packages/rink/src/widgets/text_like.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, io::stdout}; use crossterm::{cursor::MoveTo, execute}; -use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData, PointInteraction}; +use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData, prelude::PointInteraction}; use dioxus_native_core::{ custom_element::CustomElement, node::OwnedAttributeDiscription, From f599ad6ad7e536bec7d66f8ff9ce99e6a5d94ea6 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 22:14:57 -0400 Subject: [PATCH 12/56] remove unnecessary PointInteraction imports now that it's in prelude --- packages/desktop/headless_tests/events.rs | 1 - packages/dioxus-tui/examples/colorpicker.rs | 1 - packages/dioxus-tui/examples/hover.rs | 1 - packages/dioxus-tui/tests/events.rs | 1 - 4 files changed, 4 deletions(-) diff --git a/packages/desktop/headless_tests/events.rs b/packages/desktop/headless_tests/events.rs index d4439aec6..ac62cd37b 100644 --- a/packages/desktop/headless_tests/events.rs +++ b/packages/desktop/headless_tests/events.rs @@ -1,7 +1,6 @@ use dioxus::html::geometry::euclid::Vector3D; use dioxus::prelude::*; use dioxus_desktop::DesktopContext; -use dioxus_html::PointInteraction; pub(crate) fn check_app_exits(app: Component) { use dioxus_desktop::tao::window::WindowBuilder; diff --git a/packages/dioxus-tui/examples/colorpicker.rs b/packages/dioxus-tui/examples/colorpicker.rs index 7a358da3e..694d58ed8 100644 --- a/packages/dioxus-tui/examples/colorpicker.rs +++ b/packages/dioxus-tui/examples/colorpicker.rs @@ -1,6 +1,5 @@ use dioxus::core::RenderReturn; use dioxus::prelude::*; -use dioxus_html::PointInteraction; use dioxus_tui::DioxusElementToNodeId; use dioxus_tui::Query; use dioxus_tui::Size; diff --git a/packages/dioxus-tui/examples/hover.rs b/packages/dioxus-tui/examples/hover.rs index 3c29521e5..dc797d048 100644 --- a/packages/dioxus-tui/examples/hover.rs +++ b/packages/dioxus-tui/examples/hover.rs @@ -1,6 +1,5 @@ use dioxus::{events::MouseData, prelude::*}; use dioxus_core::Event; -use dioxus_html::PointInteraction; use std::convert::TryInto; use std::fmt::Write; use std::rc::Rc; diff --git a/packages/dioxus-tui/tests/events.rs b/packages/dioxus-tui/tests/events.rs index 7a93d3ebc..3df63c8cd 100644 --- a/packages/dioxus-tui/tests/events.rs +++ b/packages/dioxus-tui/tests/events.rs @@ -1,7 +1,6 @@ use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent}; use dioxus::prelude::*; use dioxus_html::input_data::keyboard_types::Code; -use dioxus_html::PointInteraction; use dioxus_tui::TuiContext; use std::future::Future; use std::pin::Pin; From c9da74313ce77b9279bb7f29c6d1d92468f59062 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 22:18:12 -0400 Subject: [PATCH 13/56] fix another bad format --- packages/dioxus-tui/examples/hover.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/dioxus-tui/examples/hover.rs b/packages/dioxus-tui/examples/hover.rs index dc797d048..a0b254414 100644 --- a/packages/dioxus-tui/examples/hover.rs +++ b/packages/dioxus-tui/examples/hover.rs @@ -52,9 +52,16 @@ fn app(cx: Scope) -> Element { }; cx.render(rsx! { - div { width: "100%", height: "100%", flex_direction: "column", + #[dxfmt::skip] + div { + width: "100%", + height: "100%", + flex_direction: "column", - div { width: "100%", height: "50%", flex_direction: "row", + div { + width: "100%", + height: "50%", + flex_direction: "row", div { border_width: "1px", width: "50%", @@ -86,7 +93,10 @@ fn app(cx: Scope) -> Element { } } - div { width: "100%", height: "50%", flex_direction: "row", + div { + width: "100%", + height: "50%", + flex_direction: "row", div { width: "50%", height: "100%", From a5b97aa4a3d56cb2dfb86d6ed4c5f119306d6529 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 23 Aug 2023 22:29:12 -0400 Subject: [PATCH 14/56] fix hover.rs formatting --- packages/dioxus-tui/examples/hover.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dioxus-tui/examples/hover.rs b/packages/dioxus-tui/examples/hover.rs index a0b254414..8f00c146c 100644 --- a/packages/dioxus-tui/examples/hover.rs +++ b/packages/dioxus-tui/examples/hover.rs @@ -52,10 +52,10 @@ fn app(cx: Scope) -> Element { }; cx.render(rsx! { - #[dxfmt::skip] - div { - width: "100%", - height: "100%", + div { + width: "100%", + height: "100%", + flex_direction: "column", div { From f9de91e987c1328dc7241d251d7d1647222f0d3c Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Thu, 24 Aug 2023 00:01:11 -0400 Subject: [PATCH 15/56] revert settings.json, remove extensions.json --- .vscode/extensions.json | 7 ------- .vscode/settings.json | 3 --- 2 files changed, 10 deletions(-) delete mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 90881959b..000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "recommendations": [ - "dioxuslabs.dioxus", - "rust-lang.rust-analyzer", - "tamasfe.even-better-toml" - ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 42dfebaab..ebd52887e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,8 +3,5 @@ "[toml]": { "editor.formatOnSave": false }, - "[rust]": { - "editor.defaultFormatter": "rust-lang.rust-analyzer" - }, "rust-analyzer.checkOnSave.allTargets": false, } From a5d687f7af3cad458af26a46352bc7af2e3b9bb2 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Thu, 24 Aug 2023 00:06:52 -0400 Subject: [PATCH 16/56] fix formatting --- packages/dioxus-tui/examples/hover.rs | 2 -- packages/rink/src/hooks.rs | 4 +++- packages/rink/src/widgets/slider.rs | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/dioxus-tui/examples/hover.rs b/packages/dioxus-tui/examples/hover.rs index 8f00c146c..2c2cda17d 100644 --- a/packages/dioxus-tui/examples/hover.rs +++ b/packages/dioxus-tui/examples/hover.rs @@ -55,9 +55,7 @@ fn app(cx: Scope) -> Element { div { width: "100%", height: "100%", - flex_direction: "column", - div { width: "100%", height: "50%", diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index 4a07992de..b6b34458c 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -15,7 +15,9 @@ use dioxus_html::input_data::{ MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons, }; use dioxus_html::point_interaction::PointData; -use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, prelude::PointInteraction, WheelData}; +use dioxus_html::{ + event_bubbles, prelude::PointInteraction, FocusData, KeyboardData, MouseData, WheelData, +}; use std::any::Any; use std::collections::HashMap; use std::{ diff --git a/packages/rink/src/widgets/slider.rs b/packages/rink/src/widgets/slider.rs index 61bb820b4..3af995e09 100644 --- a/packages/rink/src/widgets/slider.rs +++ b/packages/rink/src/widgets/slider.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; -use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData, prelude::PointInteraction}; +use dioxus_html::{ + input_data::keyboard_types::Key, prelude::PointInteraction, KeyboardData, MouseData, +}; use dioxus_native_core::{ custom_element::CustomElement, node::{OwnedAttributeDiscription, OwnedAttributeValue}, From 124b58afb1bc6cd9b7dd9573fa837155fe3ef7a6 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Thu, 24 Aug 2023 00:06:57 -0400 Subject: [PATCH 17/56] missed one --- packages/rink/src/widgets/text_like.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/rink/src/widgets/text_like.rs b/packages/rink/src/widgets/text_like.rs index bcba2d994..5bfebd0ab 100644 --- a/packages/rink/src/widgets/text_like.rs +++ b/packages/rink/src/widgets/text_like.rs @@ -1,7 +1,9 @@ use std::{collections::HashMap, io::stdout}; use crossterm::{cursor::MoveTo, execute}; -use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData, prelude::PointInteraction}; +use dioxus_html::{ + input_data::keyboard_types::Key, prelude::PointInteraction, KeyboardData, MouseData, +}; use dioxus_native_core::{ custom_element::CustomElement, node::OwnedAttributeDiscription, From a72718134b7cdb5a05820bf06a65c35dece20f10 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Thu, 24 Aug 2023 00:18:00 -0400 Subject: [PATCH 18/56] fix failing check --- packages/dioxus-tui/tests/events.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/dioxus-tui/tests/events.rs b/packages/dioxus-tui/tests/events.rs index 3df63c8cd..4d2639371 100644 --- a/packages/dioxus-tui/tests/events.rs +++ b/packages/dioxus-tui/tests/events.rs @@ -133,7 +133,6 @@ fn mouse_up() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - #[dxfmt::skip] div { width: "100%", height: "100%", @@ -173,7 +172,6 @@ fn mouse_enter() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - #[dxfmt::skip] div { width: "50%", height: "50%", @@ -213,7 +211,6 @@ fn mouse_exit() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - #[dxfmt::skip] div { width: "50%", height: "50%", @@ -253,7 +250,6 @@ fn mouse_move() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - #[dxfmt::skip] div { width: "100%", height: "100%", @@ -333,7 +329,6 @@ fn click() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - #[dxfmt::skip] div { width: "100%", height: "100%", @@ -373,7 +368,6 @@ fn context_menu() { modifiers: KeyModifiers::NONE, })); cx.render(rsx! { - #[dxfmt::skip] div { width: "100%", height: "100%", From 299b1234462ea750e54b424dd4d9e7a7bbf779f4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 25 Aug 2023 17:02:48 -0500 Subject: [PATCH 19/56] trait based event system --- packages/desktop/src/element.rs | 4 +- packages/desktop/src/lib.rs | 24 +- packages/html/Cargo.toml | 3 +- packages/html/src/events.rs | 4 +- packages/html/src/events/animation.rs | 118 ++++++- packages/html/src/events/clipboard.rs | 75 +++- packages/html/src/events/composition.rs | 85 ++++- packages/html/src/events/drag.rs | 180 +++++++++- packages/html/src/events/focus.rs | 71 +++- packages/html/src/events/form.rs | 210 +++++++++--- packages/html/src/events/image.rs | 88 ++++- packages/html/src/events/keyboard.rs | 247 +++++++------- packages/html/src/events/media.rs | 73 +++- packages/html/src/events/mounted.rs | 36 +- packages/html/src/events/mouse.rs | 132 ++++++- packages/html/src/events/pointer.rs | 347 ++++++++++++++----- packages/html/src/events/scroll.rs | 72 +++- packages/html/src/events/selection.rs | 72 +++- packages/html/src/events/toggle.rs | 72 +++- packages/html/src/events/touch.rs | 142 +++++++- packages/html/src/events/transition.rs | 101 +++++- packages/html/src/events/wheel.rs | 145 ++++---- packages/html/src/geometry.rs | 14 +- packages/html/src/lib.rs | 1 + packages/html/src/point_interaction.rs | 266 +++++++-------- packages/html/src/transit.rs | 4 +- packages/html/src/web_sys_bind/events.rs | 415 ++++++++++++++++------- packages/web/src/dom.rs | 140 +++++--- 28 files changed, 2407 insertions(+), 734 deletions(-) diff --git a/packages/desktop/src/element.rs b/packages/desktop/src/element.rs index 20ca951bf..8239099cb 100644 --- a/packages/desktop/src/element.rs +++ b/packages/desktop/src/element.rs @@ -17,8 +17,8 @@ impl DesktopElement { } impl RenderedElementBacking for DesktopElement { - fn get_raw_element(&self) -> dioxus_html::MountedResult<&dyn std::any::Any> { - Ok(self) + fn as_any(&self) -> &dyn std::any::Any { + self } fn get_client_rect( diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 070bfa7d1..e1a94fbfc 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -27,8 +27,8 @@ pub use desktop_context::{ }; use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers}; use dioxus_core::*; -use dioxus_html::MountedData; -use dioxus_html::{native_bind::NativeFileEngine, FormData, HtmlEvent}; +use dioxus_html::{native_bind::NativeFileEngine, HtmlEvent}; +use dioxus_html::{FileEngine, HasFormData, MountedData}; use element::DesktopElement; use eval::init_eval; use futures_util::{pin_mut, FutureExt}; @@ -340,14 +340,26 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) if let Ok(file_diolog) = serde_json::from_value::(msg.params()) { + struct DesktopFileUploadForm { + files: Arc, + } + + impl HasFormData for DesktopFileUploadForm { + fn files(&self) -> Option> { + Some(self.files.clone()) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + } + let id = ElementId(file_diolog.target); let event_name = &file_diolog.event; let event_bubbles = file_diolog.bubbles; let files = file_upload::get_file_event(&file_diolog); - let data = Rc::new(FormData { - value: Default::default(), - values: Default::default(), - files: Some(Arc::new(NativeFileEngine::new(files))), + let data = Rc::new(DesktopFileUploadForm { + files: Arc::new(NativeFileEngine::new(files)), }); let view = webviews.get_mut(&event.1).unwrap(); diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 5644b440c..3a2495b02 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -41,7 +41,6 @@ features = [ "PointerEvent", "FocusEvent", "CompositionEvent", - "ClipboardEvent", ] [dev-dependencies] @@ -51,11 +50,13 @@ serde_json = "1" default = ["serialize", "mounted"] serialize = [ "serde", + "serde/rc", "serde_repr", "serde_json", "euclid/serde", "keyboard-types/serde", "dioxus-core/serialize", + "enumset/serde", ] mounted = [ "web-sys/Element", diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index c59149493..f044b7b43 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -151,8 +151,6 @@ pub fn event_bubbles(evt: &str) -> bool { } } -use std::future::Future; - #[doc(hidden)] pub trait EventReturn

: Sized { fn spawn(self, _cx: &dioxus_core::ScopeState) {} @@ -164,7 +162,7 @@ pub struct AsyncMarker; impl EventReturn for T where - T: Future + 'static, + T: std::future::Future + 'static, { #[inline] fn spawn(self, cx: &dioxus_core::ScopeState) { diff --git a/packages/html/src/events/animation.rs b/packages/html/src/events/animation.rs index 4495191fc..ca0a26567 100644 --- a/packages/html/src/events/animation.rs +++ b/packages/html/src/events/animation.rs @@ -2,12 +2,120 @@ use dioxus_core::Event; pub type AnimationEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq)] pub struct AnimationData { - pub animation_name: String, - pub pseudo_element: String, - pub elapsed_time: f32, + inner: Box, +} + +impl AnimationData { + pub fn animation_name(&self) -> String { + self.inner.animation_name() + } + + pub fn pseudo_element(&self) -> String { + self.inner.pseudo_element() + } + + pub fn elapsed_time(&self) -> f32 { + self.inner.elapsed_time() + } + + pub fn downcast(&self) -> Option<&T> { + self.inner.as_ref().as_any().downcast_ref::() + } +} + +impl From for AnimationData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for AnimationData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AnimationData") + .field("animation_name", &self.animation_name()) + .field("pseudo_element", &self.pseudo_element()) + .field("elapsed_time", &self.elapsed_time()) + .finish() + } +} + +impl PartialEq for AnimationData { + fn eq(&self, other: &Self) -> bool { + self.animation_name() == other.animation_name() + && self.pseudo_element() == other.pseudo_element() + && self.elapsed_time() == other.elapsed_time() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedAnimationData { + animation_name: String, + pseudo_element: String, + elapsed_time: f32, +} + +#[cfg(feature = "serialize")] +impl From<&AnimationData> for SerializedAnimationData { + fn from(data: &AnimationData) -> Self { + Self { + animation_name: data.animation_name(), + pseudo_element: data.pseudo_element(), + elapsed_time: data.elapsed_time(), + } + } +} + +#[cfg(feature = "serialize")] +impl HasAnimationData for SerializedAnimationData { + fn animation_name(&self) -> String { + self.animation_name.clone() + } + + fn pseudo_element(&self) -> String { + self.pseudo_element.clone() + } + + fn elapsed_time(&self) -> f32 { + self.elapsed_time + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for AnimationData { + fn serialize(&self, serializer: S) -> Result { + SerializedAnimationData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for AnimationData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedAnimationData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +/// A trait for any object that has the data for an animation event +pub trait HasAnimationData: std::any::Any { + /// The name of the animation + fn animation_name(&self) -> String; + + /// The name of the pseudo-element the animation runs on + fn pseudo_element(&self) -> String; + + /// The amount of time the animation has been running + fn elapsed_time(&self) -> f32; + + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } impl_event! [ diff --git a/packages/html/src/events/clipboard.rs b/packages/html/src/events/clipboard.rs index 54390c6b0..84a9b1446 100644 --- a/packages/html/src/events/clipboard.rs +++ b/packages/html/src/events/clipboard.rs @@ -1,10 +1,79 @@ use dioxus_core::Event; pub type ClipboardEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] + pub struct ClipboardData { - // DOMDataTransfer clipboardData + inner: Box, +} + +impl From for ClipboardData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for ClipboardData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ClipboardData").finish() + } +} + +impl PartialEq for ClipboardData { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl ClipboardData { + pub fn new(inner: impl HasClipboardData) -> Self { + Self { + inner: Box::new(inner), + } + } + + pub fn downcast(&self) -> Option<&T> { + self.inner.as_ref().as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedClipboardData {} + +#[cfg(feature = "serialize")] +impl From<&ClipboardData> for SerializedClipboardData { + fn from(_: &ClipboardData) -> Self { + Self {} + } +} + +#[cfg(feature = "serialize")] +impl HasClipboardData for SerializedClipboardData { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for ClipboardData { + fn serialize(&self, serializer: S) -> Result { + SerializedClipboardData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for ClipboardData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedClipboardData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +pub trait HasClipboardData: std::any::Any { + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } impl_event![ diff --git a/packages/html/src/events/composition.rs b/packages/html/src/events/composition.rs index 480868f24..bf2f6214e 100644 --- a/packages/html/src/events/composition.rs +++ b/packages/html/src/events/composition.rs @@ -1,10 +1,89 @@ use dioxus_core::Event; pub type CompositionEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] + pub struct CompositionData { - pub data: String, + inner: Box, +} + +impl std::fmt::Debug for CompositionData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CompositionData") + .field("data", &self.data()) + .finish() + } +} + +impl From for CompositionData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl PartialEq for CompositionData { + fn eq(&self, other: &Self) -> bool { + self.data() == other.data() + } +} + +impl CompositionData { + pub fn data(&self) -> String { + self.inner.data() + } + + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedCompositionData { + data: String, +} + +#[cfg(feature = "serialize")] +impl From<&CompositionData> for SerializedCompositionData { + fn from(data: &CompositionData) -> Self { + Self { data: data.data() } + } +} + +#[cfg(feature = "serialize")] +impl HasCompositionData for SerializedCompositionData { + fn data(&self) -> String { + self.data.clone() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for CompositionData { + fn serialize(&self, serializer: S) -> Result { + SerializedCompositionData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for CompositionData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedCompositionData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +/// A trait for any object that has the data for a composition event +pub trait HasCompositionData: std::any::Any { + /// The characters generated by the input method that raised the event + fn data(&self) -> String; + + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } impl_event! [ diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index fa3219631..f7575c494 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -1,6 +1,10 @@ +use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; +use crate::input_data::{MouseButton, MouseButtonSet}; +use crate::prelude::PointInteraction; use dioxus_core::Event; +use keyboard_types::Modifiers; -use crate::MouseData; +use crate::HasMouseData; pub type DragEvent = Event; @@ -8,13 +12,179 @@ pub type DragEvent = Event; /// placing a pointer device (such as a mouse) on the touch surface and then dragging the pointer to a new location /// (such as another DOM element). Applications are free to interpret a drag and drop interaction in an /// application-specific way. -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] pub struct DragData { - /// Inherit mouse data - pub mouse: MouseData, + inner: Box, } +impl From for DragData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for DragData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DragData") + .field("coordinates", &self.coordinates()) + .field("modifiers", &self.modifiers()) + .field("held_buttons", &self.held_buttons()) + .field("trigger_button", &self.trigger_button()) + .finish() + } +} + +impl PartialEq for DragData { + fn eq(&self, other: &Self) -> bool { + self.coordinates() == other.coordinates() + && self.modifiers() == other.modifiers() + && self.held_buttons() == other.held_buttons() + && self.trigger_button() == other.trigger_button() + } +} + +impl DragData { + /// The event's coordinates relative to the application's viewport (as opposed to the coordinate within the page). + /// + /// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally. + pub fn client_coordinates(&self) -> ClientPoint { + self.inner.client_coordinates() + } + + /// The event's coordinates relative to the padding edge of the target element + /// + /// For example, clicking in the top left corner of an element will result in element coordinates (0., 0.) + pub fn element_coordinates(&self) -> ElementPoint { + self.inner.element_coordinates() + } + + /// The event's coordinates relative to the entire document. This includes any portion of the document not currently visible. + /// + /// For example, if the page is scrolled 200 pixels to the right and 300 pixels down, clicking in the top left corner of the viewport would result in page coordinates (200., 300.) + pub fn page_coordinates(&self) -> PagePoint { + self.inner.page_coordinates() + } + + /// The event's coordinates relative to the entire screen. This takes into account the window's offset. + pub fn screen_coordinates(&self) -> ScreenPoint { + self.inner.screen_coordinates() + } + + pub fn coordinates(&self) -> Coordinates { + self.inner.coordinates() + } + + /// The set of modifier keys which were pressed when the event occurred + pub fn modifiers(&self) -> Modifiers { + self.inner.modifiers() + } + + /// The set of mouse buttons which were held when the event occurred. + pub fn held_buttons(&self) -> MouseButtonSet { + self.inner.held_buttons() + } + + /// The mouse button that triggered the event + /// + // todo the following is kind of bad; should we just return None when the trigger_button is unreliable (and frankly irrelevant)? i guess we would need the event_type here + /// This is only guaranteed to indicate which button was pressed during events caused by pressing or releasing a button. As such, it is not reliable for events such as mouseenter, mouseleave, mouseover, mouseout, or mousemove. For example, a value of MouseButton::Primary may also indicate that no button was pressed. + pub fn trigger_button(&self) -> Option { + self.inner.trigger_button() + } + + /// Downcast this event data to a specific type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedDragData { + mouse: crate::point_interaction::SerializedPointInteraction, +} + +#[cfg(feature = "serialize")] +impl From<&DragData> for SerializedDragData { + fn from(data: &DragData) -> Self { + Self { + mouse: crate::point_interaction::SerializedPointInteraction { + client_coordinates: data.client_coordinates(), + element_coordinates: data.element_coordinates(), + page_coordinates: data.page_coordinates(), + screen_coordinates: data.screen_coordinates(), + modifiers: data.modifiers(), + held_buttons: data.held_buttons(), + trigger_button: data.trigger_button(), + }, + } + } +} + +#[cfg(feature = "serialize")] +impl HasDragData for SerializedDragData {} + +#[cfg(feature = "serialize")] +impl HasMouseData for SerializedDragData { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl PointInteraction for SerializedDragData { + fn client_coordinates(&self) -> ClientPoint { + self.mouse.client_coordinates() + } + + fn element_coordinates(&self) -> ElementPoint { + self.mouse.element_coordinates() + } + + fn page_coordinates(&self) -> PagePoint { + self.mouse.page_coordinates() + } + + fn screen_coordinates(&self) -> ScreenPoint { + self.mouse.screen_coordinates() + } + + fn coordinates(&self) -> Coordinates { + self.mouse.coordinates() + } + + fn modifiers(&self) -> Modifiers { + self.mouse.modifiers() + } + + fn held_buttons(&self) -> MouseButtonSet { + self.mouse.held_buttons() + } + + fn trigger_button(&self) -> Option { + self.mouse.trigger_button() + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for DragData { + fn serialize(&self, serializer: S) -> Result { + SerializedDragData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for DragData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedDragData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +/// A trait for any object that has the data for a drag event +pub trait HasDragData: HasMouseData {} + impl_event! { DragData; diff --git a/packages/html/src/events/focus.rs b/packages/html/src/events/focus.rs index 2092c972e..59e384f4a 100644 --- a/packages/html/src/events/focus.rs +++ b/packages/html/src/events/focus.rs @@ -2,9 +2,74 @@ use dioxus_core::Event; pub type FocusEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FocusData {/* DOMEventInner: Send + SyncTarget relatedTarget */} +pub struct FocusData { + inner: Box, +} + +impl From for FocusData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for FocusData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FocusData").finish() + } +} + +impl PartialEq for FocusData { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl FocusData { + /// Downcast this event data to a specific type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedFocusData {} + +#[cfg(feature = "serialize")] +impl From<&FocusData> for SerializedFocusData { + fn from(_: &FocusData) -> Self { + Self {} + } +} + +#[cfg(feature = "serialize")] +impl HasFocusData for SerializedFocusData { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for FocusData { + fn serialize(&self, serializer: S) -> Result { + SerializedFocusData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for FocusData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedFocusData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +pub trait HasFocusData: std::any::Any { + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; +} impl_event! [ FocusData; diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index fab13482b..015559d64 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -4,28 +4,176 @@ use dioxus_core::Event; pub type FormEvent = Event; -/* DOMEvent: Send + SyncTarget relatedTarget */ -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone)] pub struct FormData { - pub value: String, + inner: Box, +} - pub values: HashMap>, +impl From for FormData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} - #[cfg_attr( - feature = "serialize", - serde( - default, - skip_serializing, - deserialize_with = "deserialize_file_engine" - ) - )] - pub files: Option>, +impl PartialEq for FormData { + fn eq(&self, other: &Self) -> bool { + self.value() == other.value() && self.values() == other.values() + } +} + +impl Debug for FormData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FormEvent") + .field("value", &self.value()) + .field("values", &self.values()) + .finish() + } +} + +impl FormData { + /// Create a new form event + pub fn new(event: impl HasFormData + 'static) -> Self { + Self { + inner: Box::new(event), + } + } + + /// Get the value of the form event + pub fn value(&self) -> String { + self.inner.value() + } + + /// Get the values of the form event + pub fn values(&self) -> HashMap> { + self.inner.values() + } + + /// Get the files of the form event + pub fn files(&self) -> Option> { + self.inner.files() + } + + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +/// An object that has all the data for a form event +pub trait HasFormData: std::any::Any { + fn value(&self) -> String { + Default::default() + } + + fn values(&self) -> HashMap> { + Default::default() + } + + fn files(&self) -> Option> { + None + } + + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } #[cfg(feature = "serialize")] +/// A serialized form data object #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedFileEngine { +pub struct SerializedFormData { + value: String, + values: HashMap>, + files: Option>, +} + +#[cfg(feature = "serialize")] +impl SerializedFormData { + /// Create a new serialized form data object + pub fn new( + value: String, + values: HashMap>, + files: Option>, + ) -> Self { + Self { + value, + values, + files, + } + } + + /// Create a new serialized form data object from a traditional form data object + pub async fn async_from(data: &FormData) -> Self { + Self { + value: data.value(), + values: data.values(), + files: match data.files() { + Some(files) => { + let mut resolved_files = HashMap::new(); + + for file in files.files() { + let bytes = files.read_file(&file).await; + resolved_files.insert(file, bytes.unwrap_or_default()); + } + + Some(std::sync::Arc::new(SerializedFileEngine { + files: resolved_files, + })) + } + None => None, + }, + } + } + + fn from_lossy(data: &FormData) -> Self { + Self { + value: data.value(), + values: data.values(), + files: None, + } + } +} + +#[cfg(feature = "serialize")] +impl HasFormData for SerializedFormData { + fn value(&self) -> String { + self.value.clone() + } + + fn values(&self) -> HashMap> { + self.values.clone() + } + + fn files(&self) -> Option> { + self.files + .as_ref() + .map(|files| std::sync::Arc::clone(files) as std::sync::Arc) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for FormData { + fn serialize(&self, serializer: S) -> Result { + SerializedFormData::from_lossy(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for FormData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedFormData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +#[cfg(feature = "serialize")] +/// A file engine that serializes files to bytes +#[derive(serde::Serialize, serde::Deserialize)] +pub struct SerializedFileEngine { files: HashMap>, } @@ -53,38 +201,6 @@ impl FileEngine for SerializedFileEngine { } } -#[cfg(feature = "serialize")] -fn deserialize_file_engine<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: serde::Deserializer<'de>, -{ - use serde::Deserialize; - - let Ok(file_engine) = SerializedFileEngine::deserialize(deserializer) else { - return Ok(None); - }; - - let file_engine = std::sync::Arc::new(file_engine); - Ok(Some(file_engine)) -} - -impl PartialEq for FormData { - fn eq(&self, other: &Self) -> bool { - self.value == other.value && self.values == other.values - } -} - -impl Debug for FormData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("FormEvent") - .field("value", &self.value) - .field("values", &self.values) - .finish() - } -} - #[async_trait::async_trait(?Send)] pub trait FileEngine { // get a list of file names diff --git a/packages/html/src/events/image.rs b/packages/html/src/events/image.rs index d5858a7f1..cf46838b7 100644 --- a/packages/html/src/events/image.rs +++ b/packages/html/src/events/image.rs @@ -1,11 +1,91 @@ use dioxus_core::Event; pub type ImageEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] pub struct ImageData { - #[cfg_attr(feature = "serialize", serde(default))] - pub load_error: bool, + inner: Box, +} + +impl From for ImageData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for ImageData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ImageData") + .field("load_error", &self.load_error()) + .finish() + } +} + +impl PartialEq for ImageData { + fn eq(&self, other: &Self) -> bool { + self.load_error() == other.load_error() + } +} + +impl ImageData { + /// If the renderer encountered an error while loading the image + pub fn load_error(&self) -> bool { + self.inner.load_error() + } + + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedImageData { + load_error: bool, +} + +#[cfg(feature = "serialize")] +impl From<&ImageData> for SerializedImageData { + fn from(data: &ImageData) -> Self { + Self { + load_error: data.load_error(), + } + } +} + +#[cfg(feature = "serialize")] +impl HasImageData for SerializedImageData { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn load_error(&self) -> bool { + self.load_error + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for ImageData { + fn serialize(&self, serializer: S) -> Result { + SerializedImageData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for ImageData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedImageData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +/// A trait for any object that has the data for an image event +pub trait HasImageData: std::any::Any { + /// If the renderer encountered an error while loading the image + fn load_error(&self) -> bool; + + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } impl_event! [ diff --git a/packages/html/src/events/keyboard.rs b/packages/html/src/events/keyboard.rs index d00dcf8d2..07fc00871 100644 --- a/packages/html/src/events/keyboard.rs +++ b/packages/html/src/events/keyboard.rs @@ -1,9 +1,7 @@ -use crate::input_data::{decode_key_location, encode_key_location}; use dioxus_core::Event; use keyboard_types::{Code, Key, Location, Modifiers}; use std::convert::TryInto; -use std::fmt::{Debug, Formatter}; -use std::str::FromStr; +use std::fmt::Debug; #[cfg(feature = "serialize")] fn resilient_deserialize_code<'de, D>(deserializer: D) -> Result @@ -16,57 +14,136 @@ where } pub type KeyboardEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, PartialEq, Eq)] pub struct KeyboardData { - #[deprecated( - since = "0.3.0", - note = "This may not work in all environments. Use key() instead." - )] - pub char_code: u32, + inner: Box, +} - /// Identify which "key" was entered. - #[deprecated(since = "0.3.0", note = "use key() instead")] - pub key: String, +impl From for KeyboardData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} - /// Get the key code as an enum Variant. - #[deprecated( - since = "0.3.0", - note = "This may not work in all environments. Use code() instead." - )] - pub key_code: KeyCode, +impl std::fmt::Debug for KeyboardData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("KeyboardData") + .field("key", &self.key()) + .field("code", &self.code()) + .field("modifiers", &self.modifiers()) + .field("location", &self.location()) + .field("is_auto_repeating", &self.is_auto_repeating()) + .finish() + } +} - /// the physical key on the keyboard - #[cfg_attr( - feature = "serialize", - serde(deserialize_with = "resilient_deserialize_code") - )] +impl PartialEq for KeyboardData { + fn eq(&self, other: &Self) -> bool { + self.key() == other.key() + && self.code() == other.code() + && self.modifiers() == other.modifiers() + && self.location() == other.location() + && self.is_auto_repeating() == other.is_auto_repeating() + } +} + +impl KeyboardData { + /// The value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout. + pub fn key(&self) -> Key { + self.inner.key() + } + + /// A physical key on the keyboard (as opposed to the character generated by pressing the key). In other words, this property returns a value that isn't altered by keyboard layout or the state of the modifier keys. + pub fn code(&self) -> Code { + self.inner.code() + } + + /// The set of modifier keys which were pressed when the event occurred + pub fn modifiers(&self) -> Modifiers { + self.inner.modifiers() + } + + /// The location of the key on the keyboard or other input device. + pub fn location(&self) -> Location { + self.inner.location() + } + + /// `true` iff the key is being held down such that it is automatically repeating. + pub fn is_auto_repeating(&self) -> bool { + self.inner.is_auto_repeating() + } + + /// Downcast this KeyboardData to a concrete type. + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedKeyboardData { + auto_repeating: bool, + #[serde(deserialize_with = "resilient_deserialize_code")] code: Code, + key: Key, + location: Location, + modifiers: Modifiers, +} - /// Indicate if the `alt` modifier key was pressed during this keyboard event - #[deprecated(since = "0.3.0", note = "use modifiers() instead")] - pub alt_key: bool, +#[cfg(feature = "serialize")] +impl From<&KeyboardData> for SerializedKeyboardData { + fn from(data: &KeyboardData) -> Self { + Self { + auto_repeating: data.is_auto_repeating(), + code: data.code(), + key: data.key(), + location: data.location(), + modifiers: data.modifiers(), + } + } +} - /// Indicate if the `ctrl` modifier key was pressed during this keyboard event - #[deprecated(since = "0.3.0", note = "use modifiers() instead")] - pub ctrl_key: bool, +#[cfg(feature = "serialize")] +impl HasKeyboardData for SerializedKeyboardData { + fn key(&self) -> Key { + self.key.clone() + } - /// Indicate if the `meta` modifier key was pressed during this keyboard event - #[deprecated(since = "0.3.0", note = "use modifiers() instead")] - pub meta_key: bool, + fn code(&self) -> Code { + self.code + } - /// Indicate if the `shift` modifier key was pressed during this keyboard event - #[deprecated(since = "0.3.0", note = "use modifiers() instead")] - pub shift_key: bool, + fn modifiers(&self) -> Modifiers { + self.modifiers + } - #[deprecated(since = "0.3.0", note = "use location() instead")] - pub location: usize, + fn location(&self) -> Location { + self.location + } - #[deprecated(since = "0.3.0", note = "use is_auto_repeating() instead")] - pub repeat: bool, + fn is_auto_repeating(&self) -> bool { + self.auto_repeating + } - #[deprecated(since = "0.3.0", note = "use code() or key() instead")] - pub which: usize, + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for KeyboardData { + fn serialize(&self, serializer: S) -> Result { + SerializedKeyboardData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for KeyboardData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedKeyboardData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } } impl_event! { @@ -82,94 +159,24 @@ impl_event! { onkeyup } -impl KeyboardData { - pub fn new( - key: Key, - code: Code, - location: Location, - is_auto_repeating: bool, - modifiers: Modifiers, - ) -> Self { - #[allow(deprecated)] - KeyboardData { - char_code: key.legacy_charcode(), - key: key.to_string(), - key_code: KeyCode::from_raw_code( - key.legacy_keycode() - .try_into() - .expect("could not convert keycode to u8"), - ), - code, - alt_key: modifiers.contains(Modifiers::ALT), - ctrl_key: modifiers.contains(Modifiers::CONTROL), - meta_key: modifiers.contains(Modifiers::META), - shift_key: modifiers.contains(Modifiers::SHIFT), - location: encode_key_location(location), - repeat: is_auto_repeating, - which: key - .legacy_charcode() - .try_into() - .expect("could not convert charcode to usize"), - } - } - +pub trait HasKeyboardData: std::any::Any { /// The value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout. - pub fn key(&self) -> Key { - #[allow(deprecated)] - FromStr::from_str(&self.key).unwrap_or(Key::Unidentified) - } + fn key(&self) -> Key; /// A physical key on the keyboard (as opposed to the character generated by pressing the key). In other words, this property returns a value that isn't altered by keyboard layout or the state of the modifier keys. - pub fn code(&self) -> Code { - self.code - } + fn code(&self) -> Code; /// The set of modifier keys which were pressed when the event occurred - pub fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - #[allow(deprecated)] - { - if self.alt_key { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key { - modifiers.insert(Modifiers::META); - } - if self.shift_key { - modifiers.insert(Modifiers::SHIFT); - } - } - - modifiers - } + fn modifiers(&self) -> Modifiers; /// The location of the key on the keyboard or other input device. - pub fn location(&self) -> Location { - #[allow(deprecated)] - decode_key_location(self.location) - } + fn location(&self) -> Location; /// `true` iff the key is being held down such that it is automatically repeating. - pub fn is_auto_repeating(&self) -> bool { - #[allow(deprecated)] - self.repeat - } -} + fn is_auto_repeating(&self) -> bool; -impl Debug for KeyboardData { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("KeyboardData") - .field("key", &self.key()) - .field("code", &self.code()) - .field("modifiers", &self.modifiers()) - .field("location", &self.location()) - .field("is_auto_repeating", &self.is_auto_repeating()) - .finish() - } + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } #[cfg(feature = "serialize")] diff --git a/packages/html/src/events/media.rs b/packages/html/src/events/media.rs index d2ba71c08..e088bd2a5 100644 --- a/packages/html/src/events/media.rs +++ b/packages/html/src/events/media.rs @@ -1,9 +1,74 @@ use dioxus_core::Event; pub type MediaEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MediaData {} +pub struct MediaData { + inner: Box, +} + +impl From for MediaData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for MediaData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MediaData").finish() + } +} + +impl PartialEq for MediaData { + fn eq(&self, _: &Self) -> bool { + true + } +} + +impl MediaData { + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedMediaData {} + +#[cfg(feature = "serialize")] +impl From<&MediaData> for SerializedMediaData { + fn from(_: &MediaData) -> Self { + Self {} + } +} + +#[cfg(feature = "serialize")] +impl HasMediaData for SerializedMediaData { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for MediaData { + fn serialize(&self, serializer: S) -> Result { + SerializedMediaData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for MediaData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedMediaData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +pub trait HasMediaData: std::any::Any { + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; +} impl_event! [ MediaData; @@ -29,7 +94,7 @@ impl_event! [ ///ended onended - // todo: this conflicts with image events + // todo: this conflicts with Media events // neither have data, so it's okay // ///error // onerror diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs index bc561bf1b..e3908ca8a 100644 --- a/packages/html/src/events/mounted.rs +++ b/packages/html/src/events/mounted.rs @@ -3,22 +3,18 @@ use euclid::Rect; use std::{ - any::Any, fmt::{Display, Formatter}, future::Future, pin::Pin, - rc::Rc, }; /// An Element that has been rendered and allows reading and modifying information about it. /// /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. // we can not use async_trait here because it does not create a trait that is object safe -pub trait RenderedElementBacking { - /// Get the renderer specific element for the given id - fn get_raw_element(&self) -> MountedResult<&dyn Any> { - Err(MountedError::NotSupported) - } +pub trait RenderedElementBacking: std::any::Any { + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) #[allow(clippy::type_complexity)] @@ -40,7 +36,11 @@ pub trait RenderedElementBacking { } } -impl RenderedElementBacking for () {} +impl RenderedElementBacking for () { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} /// The way that scrolling should be performed #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] @@ -57,22 +57,23 @@ pub enum ScrollBehavior { /// /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. pub struct MountedData { - inner: Rc, + inner: Box, +} + +impl From for MountedData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } } impl MountedData { /// Create a new MountedData pub fn new(registry: impl RenderedElementBacking + 'static) -> Self { Self { - inner: Rc::new(registry), + inner: Box::new(registry), } } - /// Get the renderer specific element for the given id - pub fn get_raw_element(&self) -> MountedResult<&dyn Any> { - self.inner.get_raw_element() - } - /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) pub async fn get_client_rect(&self) -> MountedResult> { self.inner.get_client_rect().await @@ -90,6 +91,11 @@ impl MountedData { pub fn set_focus(&self, focus: bool) -> Pin>>> { self.inner.set_focus(focus) } + + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } } use dioxus_core::Event; diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index 74b7bdfb9..6c346a434 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -1,16 +1,47 @@ -use crate::point_interaction::{PointData, PointInteraction}; +use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; +use crate::input_data::{MouseButton, MouseButtonSet}; +use crate::point_interaction::PointInteraction; use dioxus_core::Event; +use keyboard_types::Modifiers; pub type MouseEvent = Event; /// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, Default, PartialEq, Eq)] /// Data associated with a mouse event pub struct MouseData { - /// Common data for all pointer/mouse events - #[cfg_attr(feature = "serialize", serde(flatten))] - point_data: PointData, + inner: Box, +} + +impl From for MouseData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for MouseData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MouseData") + .field("coordinates", &self.coordinates()) + .field("modifiers", &self.modifiers()) + .field("held_buttons", &self.held_buttons()) + .field("trigger_button", &self.trigger_button()) + .finish() + } +} + +impl PartialEq for MouseData { + fn eq(&self, other: &E) -> bool { + self.coordinates() == other.coordinates() + && self.modifiers() == other.modifiers() + && self.held_buttons() == other.held_buttons() + && self.trigger_button() == other.trigger_button() + } +} + +/// A trait for any object that has the data for a mouse event +pub trait HasMouseData: PointInteraction { + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } impl_event! { @@ -75,16 +106,91 @@ impl_event! { } impl MouseData { - /// Construct MouseData with the specified properties - /// - /// Note: the current implementation truncates coordinates. In the future, when we change the internal representation, it may also support a fractional part. - pub fn new(point_data: PointData) -> Self { - Self { point_data } + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() } } impl PointInteraction for MouseData { - fn get_point_data(&self) -> PointData { - self.point_data + /// The event's coordinates relative to the application's viewport (as opposed to the coordinate within the page). + /// + /// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally. + fn client_coordinates(&self) -> ClientPoint { + self.inner.client_coordinates() + } + + /// The event's coordinates relative to the padding edge of the target element + /// + /// For example, clicking in the top left corner of an element will result in element coordinates (0., 0.) + fn element_coordinates(&self) -> ElementPoint { + self.inner.element_coordinates() + } + + /// The event's coordinates relative to the entire document. This includes any portion of the document not currently visible. + /// + /// For example, if the page is scrolled 200 pixels to the right and 300 pixels down, clicking in the top left corner of the viewport would result in page coordinates (200., 300.) + fn page_coordinates(&self) -> PagePoint { + self.inner.page_coordinates() + } + + /// The event's coordinates relative to the entire screen. This takes into account the window's offset. + fn screen_coordinates(&self) -> ScreenPoint { + self.inner.screen_coordinates() + } + + fn coordinates(&self) -> Coordinates { + self.inner.coordinates() + } + + /// The set of modifier keys which were pressed when the event occurred + fn modifiers(&self) -> Modifiers { + self.inner.modifiers() + } + + /// The set of mouse buttons which were held when the event occurred. + fn held_buttons(&self) -> MouseButtonSet { + self.inner.held_buttons() + } + + /// The mouse button that triggered the event + /// + // todo the following is kind of bad; should we just return None when the trigger_button is unreliable (and frankly irrelevant)? i guess we would need the event_type here + /// This is only guaranteed to indicate which button was pressed during events caused by pressing or releasing a button. As such, it is not reliable for events such as mouseenter, mouseleave, mouseover, mouseout, or mousemove. For example, a value of MouseButton::Primary may also indicate that no button was pressed. + fn trigger_button(&self) -> Option { + self.inner.trigger_button() + } +} + +impl PartialEq for MouseData { + fn eq(&self, other: &Self) -> bool { + self.coordinates() == other.coordinates() + && self.modifiers() == other.modifiers() + && self.held_buttons() == other.held_buttons() + && self.trigger_button() == other.trigger_button() + } +} + +#[cfg(feature = "serialize")] +impl HasMouseData for crate::point_interaction::SerializedPointInteraction { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for MouseData { + fn serialize(&self, serializer: S) -> Result { + crate::point_interaction::SerializedPointInteraction::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for MouseData { + fn deserialize>(deserializer: D) -> Result { + let data = crate::point_interaction::SerializedPointInteraction::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) } } diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 9277568d0..266dd7c66 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -1,58 +1,87 @@ -use std::fmt::Debug; - use dioxus_core::Event; +use keyboard_types::Modifiers; -use crate::point_interaction::{PointData, PointInteraction}; +use crate::{geometry::*, input_data::*, point_interaction::PointInteraction}; /// A synthetic event that wraps a web-style [`PointerEvent`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) pub type PointerEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq)] pub struct PointerData { - /// Common data for all pointer/mouse events - #[cfg_attr(feature = "serialize", serde(flatten))] - point_data: PointData, + inner: Box, +} - /// The unique identifier of the pointer causing the event. - #[deprecated(since = "0.5.0", note = "use pointer_id() instead")] - pub pointer_id: i32, +impl From for PointerData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} - /// The width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer. - #[deprecated(since = "0.5.0", note = "use width() instead")] - pub width: i32, +impl std::fmt::Debug for PointerData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PointerData") + .field("pointer_id", &self.pointer_id()) + .field("width", &self.width()) + .field("height", &self.height()) + .field("pressure", &self.pressure()) + .field("tangential_pressure", &self.tangential_pressure()) + .field("tilt_x", &self.tilt_x()) + .field("tilt_y", &self.tilt_y()) + .field("twist", &self.twist()) + .field("pointer_type", &self.pointer_type()) + .field("is_primary", &self.is_primary()) + .finish() + } +} - /// The height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer. - #[deprecated(since = "0.5.0", note = "use height() instead")] - pub height: i32, +impl PartialEq for PointerData { + fn eq(&self, other: &Self) -> bool { + self.pointer_id() == other.pointer_id() + && self.width() == other.width() + && self.height() == other.height() + && self.pressure() == other.pressure() + && self.tangential_pressure() == other.tangential_pressure() + && self.tilt_x() == other.tilt_x() + && self.tilt_y() == other.tilt_y() + && self.twist() == other.twist() + && self.pointer_type() == other.pointer_type() + && self.is_primary() == other.is_primary() + } +} - /// The normalized pressure of the pointer input in the range of 0 to 1, - #[deprecated(since = "0.5.0", note = "use pressure() instead")] - pub pressure: f32, +/// A trait for any object that has the data for a pointer event +pub trait HasPointerData: PointInteraction { + /// Gets the unique identifier of the pointer causing the event. + fn pointer_id(&self) -> i32; - /// The normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1, - #[deprecated(since = "0.5.0", note = "use tangential_pressure() instead")] - pub tangential_pressure: f32, + /// Gets the width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer. + fn width(&self) -> i32; - /// The plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis. - #[deprecated(since = "0.5.0", note = "use tilt_x() instead")] - pub tilt_x: i32, + /// Gets the height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer. + fn height(&self) -> i32; - /// The plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis. - #[deprecated(since = "0.5.0", note = "use tilt_y() instead")] - pub tilt_y: i32, + /// Gets the normalized pressure of the pointer input in the range of 0 to 1, + fn pressure(&self) -> f32; - /// The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359. - #[deprecated(since = "0.5.0", note = "use twist() instead")] - pub twist: i32, + /// Gets the normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1, + fn tangential_pressure(&self) -> f32; - /// Indicates the device type that caused the event (mouse, pen, touch, etc.). - #[deprecated(since = "0.5.0", note = "use pointer_type() instead")] - pub pointer_type: String, + /// Gets the plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis. + fn tilt_x(&self) -> i32; - /// Indicates if the pointer represents the primary pointer of this pointer type. - #[deprecated(since = "0.5.0", note = "use is_primary() instead")] - pub is_primary: bool, + /// Gets the plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis. + fn tilt_y(&self) -> i32; + + /// Gets the clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359. + fn twist(&self) -> i32; + + /// Gets the device type that caused the event (mouse, pen, touch, etc.). + fn pointer_type(&self) -> String; + + /// Gets if the pointer represents the primary pointer of this pointer type. + fn is_primary(&self) -> bool; + + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } impl_event![ @@ -89,99 +118,235 @@ impl_event![ ]; impl PointerData { - #[allow(clippy::too_many_arguments)] - pub fn new( - point_data: PointData, - pointer_id: i32, - width: i32, - height: i32, - pressure: f32, - tangential_pressure: f32, - tilt_x: i32, - tilt_y: i32, - twist: i32, - pointer_type: String, - is_primary: bool, - ) -> Self { - #[allow(deprecated)] - Self { - point_data, - pointer_id, - width, - height, - pressure, - tangential_pressure, - tilt_x, - tilt_y, - twist, - pointer_type, - is_primary, - } - } - /// Gets the unique identifier of the pointer causing the event. pub fn pointer_id(&self) -> i32 { - #[allow(deprecated)] - self.pointer_id + self.inner.pointer_id() } /// Gets the width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer. pub fn width(&self) -> i32 { - #[allow(deprecated)] - self.width + self.inner.width() } /// Gets the height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer. pub fn height(&self) -> i32 { - #[allow(deprecated)] - self.height + self.inner.height() } /// Gets the normalized pressure of the pointer input in the range of 0 to 1, pub fn pressure(&self) -> f32 { - #[allow(deprecated)] - self.pressure + self.inner.pressure() } /// Gets the normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1, pub fn tangential_pressure(&self) -> f32 { - #[allow(deprecated)] - self.tangential_pressure + self.inner.tangential_pressure() } /// Gets the plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis. pub fn tilt_x(&self) -> i32 { - #[allow(deprecated)] - self.tilt_x + self.inner.tilt_x() } /// Gets the plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis. pub fn tilt_y(&self) -> i32 { - #[allow(deprecated)] - self.tilt_y + self.inner.tilt_y() } /// Gets the clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359. pub fn twist(&self) -> i32 { - #[allow(deprecated)] - self.twist + self.inner.twist() } /// Gets the device type that caused the event (mouse, pen, touch, etc.). - pub fn pointer_type(&self) -> &str { - #[allow(deprecated)] - self.pointer_type.as_str() + pub fn pointer_type(&self) -> String { + self.inner.pointer_type() } /// Gets if the pointer represents the primary pointer of this pointer type. pub fn is_primary(&self) -> bool { - #[allow(deprecated)] - self.is_primary + self.inner.is_primary() } } impl PointInteraction for PointerData { - fn get_point_data(&self) -> PointData { - self.point_data + fn client_coordinates(&self) -> ClientPoint { + self.inner.client_coordinates() + } + + fn screen_coordinates(&self) -> ScreenPoint { + self.inner.screen_coordinates() + } + + fn element_coordinates(&self) -> ElementPoint { + self.inner.element_coordinates() + } + + fn page_coordinates(&self) -> PagePoint { + self.inner.page_coordinates() + } + + fn modifiers(&self) -> Modifiers { + self.inner.modifiers() + } + + fn held_buttons(&self) -> MouseButtonSet { + self.inner.held_buttons() + } + + fn trigger_button(&self) -> Option { + self.inner.trigger_button() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedPointData { + /// Common data for all pointer/mouse events + #[serde(flatten)] + point_data: crate::point_interaction::SerializedPointInteraction, + + /// The unique identifier of the pointer causing the event. + pointer_id: i32, + + /// The width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer. + width: i32, + + /// The height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer. + height: i32, + + /// The normalized pressure of the pointer input in the range of 0 to 1, + pressure: f32, + + /// The normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1, + tangential_pressure: f32, + + /// The plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis. + tilt_x: i32, + + /// The plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis. + tilt_y: i32, + + /// The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359. + twist: i32, + + /// Indicates the device type that caused the event (mouse, pen, touch, etc.). + pointer_type: String, + + /// Indicates if the pointer represents the primary pointer of this pointer type. + is_primary: bool, +} + +#[cfg(feature = "serialize")] +impl HasPointerData for SerializedPointData { + fn pointer_id(&self) -> i32 { + self.pointer_id + } + + fn width(&self) -> i32 { + self.width + } + + fn height(&self) -> i32 { + self.height + } + + fn pressure(&self) -> f32 { + self.pressure + } + + fn tangential_pressure(&self) -> f32 { + self.tangential_pressure + } + + fn tilt_x(&self) -> i32 { + self.tilt_x + } + + fn tilt_y(&self) -> i32 { + self.tilt_y + } + + fn twist(&self) -> i32 { + self.twist + } + + fn pointer_type(&self) -> String { + self.pointer_type.clone() + } + + fn is_primary(&self) -> bool { + self.is_primary + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl PointInteraction for SerializedPointData { + fn client_coordinates(&self) -> ClientPoint { + self.point_data.client_coordinates() + } + + fn screen_coordinates(&self) -> ScreenPoint { + self.point_data.screen_coordinates() + } + + fn element_coordinates(&self) -> ElementPoint { + self.point_data.element_coordinates() + } + + fn page_coordinates(&self) -> PagePoint { + self.point_data.page_coordinates() + } + + fn modifiers(&self) -> Modifiers { + self.point_data.modifiers() + } + + fn held_buttons(&self) -> MouseButtonSet { + self.point_data.held_buttons() + } + + fn trigger_button(&self) -> Option { + self.point_data.trigger_button() + } +} + +#[cfg(feature = "serialize")] +impl From<&PointerData> for SerializedPointData { + fn from(data: &PointerData) -> Self { + Self { + point_data: data.into(), + pointer_id: data.pointer_id(), + width: data.width(), + height: data.height(), + pressure: data.pressure(), + tangential_pressure: data.tangential_pressure(), + tilt_x: data.tilt_x(), + tilt_y: data.tilt_y(), + twist: data.twist(), + pointer_type: data.pointer_type().to_string(), + is_primary: data.is_primary(), + } + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for PointerData { + fn serialize(&self, serializer: S) -> Result { + SerializedPointData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for PointerData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedPointData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) } } diff --git a/packages/html/src/events/scroll.rs b/packages/html/src/events/scroll.rs index 798218b02..01046240e 100644 --- a/packages/html/src/events/scroll.rs +++ b/packages/html/src/events/scroll.rs @@ -1,9 +1,75 @@ use dioxus_core::Event; pub type ScrollEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ScrollData {} + +pub struct ScrollData { + inner: Box, +} + +impl From for ScrollData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl ScrollData { + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +impl std::fmt::Debug for ScrollData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ScrollData").finish() + } +} + +impl PartialEq for ScrollData { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedScrollData {} + +#[cfg(feature = "serialize")] +impl From<&ScrollData> for SerializedScrollData { + fn from(_: &ScrollData) -> Self { + Self {} + } +} + +#[cfg(feature = "serialize")] +impl HasScrollData for SerializedScrollData { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for ScrollData { + fn serialize(&self, serializer: S) -> Result { + SerializedScrollData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for ScrollData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedScrollData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +pub trait HasScrollData: std::any::Any { + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; +} impl_event! { ScrollData; diff --git a/packages/html/src/events/selection.rs b/packages/html/src/events/selection.rs index 249353e63..34aa98d32 100644 --- a/packages/html/src/events/selection.rs +++ b/packages/html/src/events/selection.rs @@ -1,9 +1,75 @@ use dioxus_core::Event; pub type SelectionEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SelectionData {} + +pub struct SelectionData { + inner: Box, +} + +impl SelectionData { + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +impl From for SelectionData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for SelectionData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SelectionData").finish() + } +} + +impl PartialEq for SelectionData { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedSelectionData {} + +#[cfg(feature = "serialize")] +impl From<&SelectionData> for SerializedSelectionData { + fn from(_: &SelectionData) -> Self { + Self {} + } +} + +#[cfg(feature = "serialize")] +impl HasSelectionData for SerializedSelectionData { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for SelectionData { + fn serialize(&self, serializer: S) -> Result { + SerializedSelectionData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for SelectionData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedSelectionData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +pub trait HasSelectionData: std::any::Any { + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; +} impl_event! [ SelectionData; diff --git a/packages/html/src/events/toggle.rs b/packages/html/src/events/toggle.rs index 4f225a8e6..33e34b18b 100644 --- a/packages/html/src/events/toggle.rs +++ b/packages/html/src/events/toggle.rs @@ -1,9 +1,75 @@ use dioxus_core::Event; pub type ToggleEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ToggleData {} + +pub struct ToggleData { + inner: Box, +} + +impl From for ToggleData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for ToggleData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ToggleData").finish() + } +} + +impl PartialEq for ToggleData { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl ToggleData { + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedToggleData {} + +#[cfg(feature = "serialize")] +impl From<&ToggleData> for SerializedToggleData { + fn from(_: &ToggleData) -> Self { + Self {} + } +} + +#[cfg(feature = "serialize")] +impl HasToggleData for SerializedToggleData { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for ToggleData { + fn serialize(&self, serializer: S) -> Result { + SerializedToggleData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for ToggleData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedToggleData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +pub trait HasToggleData: std::any::Any { + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; +} impl_event! { ToggleData; diff --git a/packages/html/src/events/touch.rs b/packages/html/src/events/touch.rs index 1e795b6da..97043870d 100644 --- a/packages/html/src/events/touch.rs +++ b/packages/html/src/events/touch.rs @@ -1,17 +1,139 @@ use dioxus_core::Event; pub type TouchEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] pub struct TouchData { - pub alt_key: bool, - pub ctrl_key: bool, - pub meta_key: bool, - pub shift_key: bool, - // get_modifier_state: bool, - // changedTouches: DOMTouchList, - // targetTouches: DOMTouchList, - // touches: DOMTouchList, + inner: Box, +} + +impl From for TouchData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for TouchData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TouchData") + .field("alt_key", &self.alt_key()) + .field("ctrl_key", &self.ctrl_key()) + .field("meta_key", &self.meta_key()) + .field("shift_key", &self.shift_key()) + .finish() + } +} + +impl PartialEq for TouchData { + fn eq(&self, other: &Self) -> bool { + self.alt_key() == other.alt_key() + && self.ctrl_key() == other.ctrl_key() + && self.meta_key() == other.meta_key() + && self.shift_key() == other.shift_key() + } +} + +impl TouchData { + /// Returns true if the "ALT" key was down when the touch event was fired. + pub fn alt_key(&self) -> bool { + self.inner.alt_key() + } + + /// Returns true if the "CTRL" key was down when the touch event was fired. + pub fn ctrl_key(&self) -> bool { + self.inner.ctrl_key() + } + + /// Returns true if the "META" key was down when the touch event was fired. + pub fn meta_key(&self) -> bool { + self.inner.meta_key() + } + + /// Returns true if the "SHIFT" key was down when the touch event was fired. + pub fn shift_key(&self) -> bool { + self.inner.shift_key() + } + + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedTouchData { + alt_key: bool, + ctrl_key: bool, + meta_key: bool, + shift_key: bool, +} + +#[cfg(feature = "serialize")] +impl From<&TouchData> for SerializedTouchData { + fn from(data: &TouchData) -> Self { + Self { + alt_key: data.alt_key(), + ctrl_key: data.ctrl_key(), + meta_key: data.meta_key(), + shift_key: data.shift_key(), + } + } +} + +#[cfg(feature = "serialize")] +impl HasTouchData for SerializedTouchData { + fn alt_key(&self) -> bool { + self.alt_key + } + + fn ctrl_key(&self) -> bool { + self.ctrl_key + } + + fn meta_key(&self) -> bool { + self.meta_key + } + + fn shift_key(&self) -> bool { + self.shift_key + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for TouchData { + fn serialize(&self, serializer: S) -> Result { + SerializedTouchData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for TouchData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedTouchData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +pub trait HasTouchData: std::any::Any { + /// Returns true if the "ALT" key was down when the touch event was fired. + fn alt_key(&self) -> bool; + + /// Returns true if the "CTRL" key was down when the touch event was fired. + fn ctrl_key(&self) -> bool; + + /// Returns true if the "META" key was down when the touch event was fired. + fn meta_key(&self) -> bool; + + /// Returns true if the "SHIFT" key was down when the touch event was fired. + fn shift_key(&self) -> bool; + + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } impl_event! { diff --git a/packages/html/src/events/transition.rs b/packages/html/src/events/transition.rs index ecc3e1636..8acffe29c 100644 --- a/packages/html/src/events/transition.rs +++ b/packages/html/src/events/transition.rs @@ -1,12 +1,103 @@ use dioxus_core::Event; pub type TransitionEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, PartialEq)] + pub struct TransitionData { - pub property_name: String, - pub pseudo_element: String, - pub elapsed_time: f32, + inner: Box, +} + +impl From for TransitionData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for TransitionData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TransitionData") + .field("property_name", &self.inner.property_name()) + .field("pseudo_element", &self.inner.pseudo_element()) + .field("elapsed_time", &self.inner.elapsed_time()) + .finish() + } +} + +impl PartialEq for TransitionData { + fn eq(&self, other: &Self) -> bool { + self.inner.property_name() == other.inner.property_name() + && self.inner.pseudo_element() == other.inner.pseudo_element() + && self.inner.elapsed_time() == other.inner.elapsed_time() + } +} + +impl TransitionData { + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedTransitionData { + property_name: String, + pseudo_element: String, + elapsed_time: f32, +} + +#[cfg(feature = "serialize")] +impl From<&TransitionData> for SerializedTransitionData { + fn from(data: &TransitionData) -> Self { + Self { + property_name: data.inner.property_name(), + pseudo_element: data.inner.pseudo_element(), + elapsed_time: data.inner.elapsed_time(), + } + } +} + +#[cfg(feature = "serialize")] +impl HasTransitionData for SerializedTransitionData { + fn property_name(&self) -> String { + self.property_name.clone() + } + + fn pseudo_element(&self) -> String { + self.pseudo_element.clone() + } + + fn elapsed_time(&self) -> f32 { + self.elapsed_time + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for TransitionData { + fn serialize(&self, serializer: S) -> Result { + SerializedTransitionData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for TransitionData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedTransitionData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } +} + +pub trait HasTransitionData: std::any::Any { + fn property_name(&self) -> String; + fn pseudo_element(&self) -> String; + fn elapsed_time(&self) -> f32; + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } impl_event! { diff --git a/packages/html/src/events/wheel.rs b/packages/html/src/events/wheel.rs index 758fa7936..348f3760e 100644 --- a/packages/html/src/events/wheel.rs +++ b/packages/html/src/events/wheel.rs @@ -1,21 +1,88 @@ use dioxus_core::Event; -use euclid::UnknownUnit; -use std::fmt::{Debug, Formatter}; +use std::fmt::Formatter; -use crate::geometry::{LinesVector, PagesVector, PixelsVector, WheelDelta}; +use crate::geometry::WheelDelta; pub type WheelEvent = Event; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, PartialEq, Default)] + pub struct WheelData { - #[deprecated(since = "0.3.0", note = "use delta() instead")] - pub delta_mode: u32, - #[deprecated(since = "0.3.0", note = "use delta() instead")] - pub delta_x: f64, - #[deprecated(since = "0.3.0", note = "use delta() instead")] - pub delta_y: f64, - #[deprecated(since = "0.3.0", note = "use delta() instead")] - pub delta_z: f64, + inner: Box, +} + +impl From for WheelData { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl std::fmt::Debug for WheelData { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WheelData") + .field("delta", &self.delta()) + .finish() + } +} + +impl PartialEq for WheelData { + fn eq(&self, other: &Self) -> bool { + self.inner.delta() == other.inner.delta() + } +} + +impl WheelData { + /// The amount of wheel movement + #[allow(deprecated)] + pub fn delta(&self) -> WheelDelta { + self.inner.delta() + } + + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedWheelData { + delta: WheelDelta, +} + +#[cfg(feature = "serialize")] +impl From<&WheelData> for SerializedWheelData { + fn from(data: &WheelData) -> Self { + Self { + delta: data.inner.delta(), + } + } +} + +#[cfg(feature = "serialize")] +impl HasWheelData for SerializedWheelData { + fn delta(&self) -> WheelDelta { + self.delta + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for WheelData { + fn serialize(&self, serializer: S) -> Result { + SerializedWheelData::from(self).serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for WheelData { + fn deserialize>(deserializer: D) -> Result { + let data = SerializedWheelData::deserialize(deserializer)?; + Ok(Self { + inner: Box::new(data), + }) + } } impl_event![ @@ -25,54 +92,10 @@ impl_event![ onwheel ]; -impl WheelData { - /// Construct a new WheelData with the specified wheel movement delta - pub fn new(delta: WheelDelta) -> Self { - let (delta_mode, vector) = match delta { - WheelDelta::Pixels(v) => (0, v.cast_unit::()), - WheelDelta::Lines(v) => (1, v.cast_unit::()), - WheelDelta::Pages(v) => (2, v.cast_unit::()), - }; - - #[allow(deprecated)] - WheelData { - delta_mode, - delta_x: vector.x, - delta_y: vector.y, - delta_z: vector.z, - } - } - - /// Construct from the attributes of the web wheel event - pub fn from_web_attributes(delta_mode: u32, delta_x: f64, delta_y: f64, delta_z: f64) -> Self { - #[allow(deprecated)] - Self { - delta_mode, - delta_x, - delta_y, - delta_z, - } - } - +pub trait HasWheelData: std::any::Any { /// The amount of wheel movement - #[allow(deprecated)] - pub fn delta(&self) -> WheelDelta { - let x = self.delta_x; - let y = self.delta_y; - let z = self.delta_z; - match self.delta_mode { - 0 => WheelDelta::Pixels(PixelsVector::new(x, y, z)), - 1 => WheelDelta::Lines(LinesVector::new(x, y, z)), - 2 => WheelDelta::Pages(PagesVector::new(x, y, z)), - _ => panic!("Invalid delta mode, {:?}", self.delta_mode), - } - } -} + fn delta(&self) -> WheelDelta; -impl Debug for WheelData { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("WheelData") - .field("delta", &self.delta()) - .finish() - } + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; } diff --git a/packages/html/src/geometry.rs b/packages/html/src/geometry.rs index 990ca5b5e..4a0b16f4a 100644 --- a/packages/html/src/geometry.rs +++ b/packages/html/src/geometry.rs @@ -47,7 +47,7 @@ pub type PagesVector = Vector3D; /// A vector representing the amount the mouse wheel was moved /// /// This may be expressed in Pixels, Lines or Pages -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum WheelDelta { /// Movement in Pixels @@ -59,6 +59,16 @@ pub enum WheelDelta { } impl WheelDelta { + /// Construct from the attributes of the web wheel event + pub fn from_web_attributes(delta_mode: u32, delta_x: f64, delta_y: f64, delta_z: f64) -> Self { + match delta_mode { + 0 => WheelDelta::Pixels(PixelsVector::new(delta_x, delta_y, delta_z)), + 1 => WheelDelta::Lines(LinesVector::new(delta_x, delta_y, delta_z)), + 2 => WheelDelta::Pages(PagesVector::new(delta_x, delta_y, delta_z)), + _ => panic!("Invalid delta mode, {:?}", delta_mode), + } + } + /// Convenience function for constructing a WheelDelta with pixel units pub fn pixels(x: f64, y: f64, z: f64) -> Self { WheelDelta::Pixels(PixelsVector::new(x, y, z)) @@ -96,7 +106,7 @@ impl WheelDelta { } /// Coordinates of a point in the app's interface -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Coordinates { screen: ScreenPoint, client: ClientPoint, diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index d362d71f1..8168d0c5e 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -44,4 +44,5 @@ pub mod prelude { pub use crate::eval::*; pub use crate::events::*; pub use crate::point_interaction::PointInteraction; + pub use keyboard_types::{self, Code, Key, Modifiers}; } diff --git a/packages/html/src/point_interaction.rs b/packages/html/src/point_interaction.rs index f25a198cc..1d6301176 100644 --- a/packages/html/src/point_interaction.rs +++ b/packages/html/src/point_interaction.rs @@ -1,185 +1,167 @@ -use std::fmt::{Debug, Formatter}; - use keyboard_types::Modifiers; use crate::{ geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}, - input_data::{decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet}, + input_data::{MouseButton, MouseButtonSet}, }; -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Copy, Clone, Default, PartialEq, Eq)] -pub struct PointData { - pub alt_key: bool, +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// #[derive(Copy, Clone, Default, PartialEq, Eq)] +// pub struct PointData { +// pub alt_key: bool, - /// The button number that was pressed (if applicable) when the mouse event was fired. - pub button: i16, +// /// The button number that was pressed (if applicable) when the mouse event was fired. +// pub button: i16, - /// Indicates which buttons are pressed on the mouse (or other input device) when a mouse event is triggered. - /// - /// Each button that can be pressed is represented by a given number (see below). If more than one button is pressed, the button values are added together to produce a new number. For example, if the secondary (2) and auxiliary (4) buttons are pressed simultaneously, the value is 6 (i.e., 2 + 4). - /// - /// - 1: Primary button (usually the left button) - /// - 2: Secondary button (usually the right button) - /// - 4: Auxiliary button (usually the mouse wheel button or middle button) - /// - 8: 4th button (typically the "Browser Back" button) - /// - 16 : 5th button (typically the "Browser Forward" button) - pub buttons: u16, +// /// Indicates which buttons are pressed on the mouse (or other input device) when a mouse event is triggered. +// /// +// /// Each button that can be pressed is represented by a given number (see below). If more than one button is pressed, the button values are added together to produce a new number. For example, if the secondary (2) and auxiliary (4) buttons are pressed simultaneously, the value is 6 (i.e., 2 + 4). +// /// +// /// - 1: Primary button (usually the left button) +// /// - 2: Secondary button (usually the right button) +// /// - 4: Auxiliary button (usually the mouse wheel button or middle button) +// /// - 8: 4th button (typically the "Browser Back" button) +// /// - 16 : 5th button (typically the "Browser Forward" button) +// pub buttons: u16, - /// The horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). - /// - /// For example, clicking on the left edge of the viewport will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally. - pub client_x: i32, +// /// The horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). +// /// +// /// For example, clicking on the left edge of the viewport will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally. +// pub client_x: i32, - /// The vertical coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). - /// - /// For example, clicking on the top edge of the viewport will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically. - pub client_y: i32, +// /// The vertical coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). +// /// +// /// For example, clicking on the top edge of the viewport will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically. +// pub client_y: i32, - /// True if the control key was down when the mouse event was fired. - pub ctrl_key: bool, +// /// True if the control key was down when the mouse event was fired. +// pub ctrl_key: bool, - /// True if the meta key was down when the mouse event was fired. - pub meta_key: bool, +// /// True if the meta key was down when the mouse event was fired. +// pub meta_key: bool, - /// The offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node. - pub offset_x: i32, +// /// The offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node. +// pub offset_x: i32, - /// The offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node. - pub offset_y: i32, +// /// The offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node. +// pub offset_y: i32, - /// The X (horizontal) coordinate (in pixels) of the mouse, relative to the left edge of the entire document. This includes any portion of the document not currently visible. - /// - /// Being based on the edge of the document as it is, this property takes into account any horizontal scrolling of the page. For example, if the page is scrolled such that 200 pixels of the left side of the document are scrolled out of view, and the mouse is clicked 100 pixels inward from the left edge of the view, the value returned by pageX will be 300. - pub page_x: i32, +// /// The X (horizontal) coordinate (in pixels) of the mouse, relative to the left edge of the entire document. This includes any portion of the document not currently visible. +// /// +// /// Being based on the edge of the document as it is, this property takes into account any horizontal scrolling of the page. For example, if the page is scrolled such that 200 pixels of the left side of the document are scrolled out of view, and the mouse is clicked 100 pixels inward from the left edge of the view, the value returned by pageX will be 300. +// pub page_x: i32, - /// The Y (vertical) coordinate in pixels of the event relative to the whole document. - /// - /// See `page_x`. - pub page_y: i32, +// /// The Y (vertical) coordinate in pixels of the event relative to the whole document. +// /// +// /// See `page_x`. +// pub page_y: i32, - /// The X coordinate of the mouse pointer in global (screen) coordinates. - pub screen_x: i32, +// /// The X coordinate of the mouse pointer in global (screen) coordinates. +// pub screen_x: i32, - /// The Y coordinate of the mouse pointer in global (screen) coordinates. - pub screen_y: i32, +// /// The Y coordinate of the mouse pointer in global (screen) coordinates. +// pub screen_y: i32, - /// True if the shift key was down when the mouse event was fired. - pub shift_key: bool, -} - -impl PointData { - pub fn new( - trigger_button: Option, - held_buttons: MouseButtonSet, - coordinates: Coordinates, - modifiers: Modifiers, - ) -> Self { - let alt_key = modifiers.contains(Modifiers::ALT); - let ctrl_key = modifiers.contains(Modifiers::CONTROL); - let meta_key = modifiers.contains(Modifiers::META); - let shift_key = modifiers.contains(Modifiers::SHIFT); - - let [client_x, client_y]: [i32; 2] = coordinates.client().cast().into(); - let [offset_x, offset_y]: [i32; 2] = coordinates.element().cast().into(); - let [page_x, page_y]: [i32; 2] = coordinates.page().cast().into(); - let [screen_x, screen_y]: [i32; 2] = coordinates.screen().cast().into(); - Self { - button: trigger_button - .map_or(MouseButton::default(), |b| b) - .into_web_code(), - buttons: encode_mouse_button_set(held_buttons), - meta_key, - ctrl_key, - shift_key, - alt_key, - client_x, - client_y, - screen_x, - screen_y, - offset_x, - offset_y, - page_x, - page_y, - } - } -} - -impl Debug for PointData { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PointInteraction") - .field("coordinates", &self.coordinates()) - .field("modifiers", &self.modifiers()) - .field("held_buttons", &self.held_buttons()) - .field("trigger_button", &self.trigger_button()) - .finish() - } -} - -impl PointInteraction for PointData { - fn get_point_data(&self) -> PointData { - *self - } -} - -pub trait PointInteraction { - fn get_point_data(&self) -> PointData; +// /// True if the shift key was down when the mouse event was fired. +// pub shift_key: bool, +// } +pub trait PointInteraction: std::any::Any { + /// Gets the coordinates of the pointer event. fn coordinates(&self) -> Coordinates { - let point_data = self.get_point_data(); Coordinates::new( - ScreenPoint::new(point_data.screen_x.into(), point_data.screen_y.into()), - ClientPoint::new(point_data.client_x.into(), point_data.client_y.into()), - ElementPoint::new(point_data.offset_x.into(), point_data.offset_y.into()), - PagePoint::new(point_data.page_x.into(), point_data.page_y.into()), + self.screen_coordinates(), + self.client_coordinates(), + self.element_coordinates(), + self.page_coordinates(), ) } - fn client_coordinates(&self) -> ClientPoint { - let point_data = self.get_point_data(); - ClientPoint::new(point_data.client_x.into(), point_data.client_y.into()) - } + /// Gets the coordinates of the pointer event relative to the browser viewport. + fn client_coordinates(&self) -> ClientPoint; - fn screen_coordinates(&self) -> ScreenPoint { - let point_data = self.get_point_data(); - ScreenPoint::new(point_data.screen_x.into(), point_data.screen_y.into()) + /// Gets the coordinates of the pointer event relative to the screen. + fn screen_coordinates(&self) -> ScreenPoint; + + /// Gets the coordinates of the pointer event relative to the target element. + fn element_coordinates(&self) -> ElementPoint; + + /// Gets the coordinates of the pointer event relative to the page. + fn page_coordinates(&self) -> PagePoint; + + /// Gets the modifiers of the pointer event. + fn modifiers(&self) -> Modifiers; + + /// Gets the buttons that are currently held down. + fn held_buttons(&self) -> MouseButtonSet; + + /// Gets the button that triggered the event. + fn trigger_button(&self) -> Option; +} + +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +pub(crate) struct SerializedPointInteraction { + pub(crate) client_coordinates: ClientPoint, + pub(crate) element_coordinates: ElementPoint, + pub(crate) page_coordinates: PagePoint, + pub(crate) screen_coordinates: ScreenPoint, + pub(crate) modifiers: Modifiers, + pub(crate) held_buttons: MouseButtonSet, + pub(crate) trigger_button: Option, +} + +#[cfg(feature = "serialize")] +impl From<&E> for SerializedPointInteraction { + fn from(data: &E) -> Self { + Self { + client_coordinates: data.client_coordinates(), + element_coordinates: data.element_coordinates(), + page_coordinates: data.page_coordinates(), + screen_coordinates: data.screen_coordinates(), + modifiers: data.modifiers(), + held_buttons: data.held_buttons(), + trigger_button: data.trigger_button(), + } + } +} + +#[cfg(feature = "serialize")] +impl PointInteraction for SerializedPointInteraction { + fn client_coordinates(&self) -> ClientPoint { + self.client_coordinates } fn element_coordinates(&self) -> ElementPoint { - let point_data = self.get_point_data(); - ElementPoint::new(point_data.offset_x.into(), point_data.offset_y.into()) + self.element_coordinates } fn page_coordinates(&self) -> PagePoint { - let point_data = self.get_point_data(); - PagePoint::new(point_data.page_x.into(), point_data.page_y.into()) + self.page_coordinates + } + + fn screen_coordinates(&self) -> ScreenPoint { + self.screen_coordinates + } + + fn coordinates(&self) -> Coordinates { + Coordinates::new( + self.screen_coordinates(), + self.client_coordinates(), + self.element_coordinates(), + self.page_coordinates(), + ) } fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - let point_data = self.get_point_data(); - - if point_data.alt_key { - modifiers.insert(Modifiers::ALT); - } - if point_data.ctrl_key { - modifiers.insert(Modifiers::CONTROL); - } - if point_data.meta_key { - modifiers.insert(Modifiers::META); - } - if point_data.shift_key { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers + self.modifiers } fn held_buttons(&self) -> MouseButtonSet { - decode_mouse_button_set(self.get_point_data().buttons) + self.held_buttons } fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.get_point_data().button)) + self.trigger_button } } diff --git a/packages/html/src/transit.rs b/packages/html/src/transit.rs index b0fa0d16f..b27f8670a 100644 --- a/packages/html/src/transit.rs +++ b/packages/html/src/transit.rs @@ -4,7 +4,7 @@ use crate::events::*; use dioxus_core::ElementId; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Debug, PartialEq)] pub struct HtmlEvent { pub element: ElementId, pub name: String, @@ -136,7 +136,7 @@ impl HtmlEvent { } } -#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)] +#[derive(Deserialize, Serialize, Debug, PartialEq)] #[serde(untagged)] #[non_exhaustive] pub enum EventData { diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index c1214f870..757cc2ea0 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -1,12 +1,12 @@ +use crate::events::HasKeyboardData; use crate::events::{ - AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData, + AnimationData, CompositionData, HasTouchData, KeyboardData, MouseData, PointerData, TouchData, TransitionData, WheelData, }; -use crate::input_data::decode_key_location; -use crate::point_interaction::PointData; -use crate::{DragData, MountedData}; +use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}; +use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; +use crate::prelude::*; use keyboard_types::{Code, Key, Modifiers}; -use std::convert::TryInto; use std::str::FromStr; use wasm_bindgen::{JsCast, JsValue}; use web_sys::{ @@ -20,7 +20,7 @@ macro_rules! uncheck_convert { #[inline] fn from(e: Event) -> Self { let e: $t = e.unchecked_into(); - Self::from(&e) + Self::from(e) } } @@ -28,7 +28,7 @@ macro_rules! uncheck_convert { #[inline] fn from(e: &Event) -> Self { let e: &$t = e.unchecked_ref(); - Self::from(e) + Self::from(e.clone()) } } }; @@ -38,152 +38,283 @@ macro_rules! uncheck_convert { } uncheck_convert![ - CompositionEvent => CompositionData, - KeyboardEvent => KeyboardData, - MouseEvent => MouseData, - MouseEvent => DragData, - TouchEvent => TouchData, - PointerEvent => PointerData, - WheelEvent => WheelData, - AnimationEvent => AnimationData, - TransitionEvent => TransitionData, + web_sys::CompositionEvent => CompositionData, + web_sys::KeyboardEvent => KeyboardData, + web_sys::MouseEvent => MouseData, + web_sys::TouchEvent => TouchData, + web_sys::PointerEvent => PointerData, + web_sys::WheelEvent => WheelData, + web_sys::AnimationEvent => AnimationData, + web_sys::TransitionEvent => TransitionData, + web_sys::MouseEvent => DragData, + web_sys::FocusEvent => FocusData, ]; -impl From<&CompositionEvent> for CompositionData { - fn from(e: &CompositionEvent) -> Self { - Self { - data: e.data().unwrap_or_default(), - } +impl HasCompositionData for CompositionEvent { + fn data(&self) -> std::string::String { + self.data().unwrap_or_default() + } + + fn as_any(&self) -> &dyn std::any::Any { + self } } -impl From<&KeyboardEvent> for KeyboardData { - fn from(e: &KeyboardEvent) -> Self { +impl HasKeyboardData for KeyboardEvent { + fn key(&self) -> Key { + Key::from_str(self.key().as_str()).unwrap_or(Key::Unidentified) + } + + fn code(&self) -> Code { + Code::from_str(self.code().as_str()).unwrap_or(Code::Unidentified) + } + + fn modifiers(&self) -> Modifiers { let mut modifiers = Modifiers::empty(); - if e.alt_key() { + if self.alt_key() { modifiers.insert(Modifiers::ALT); } - if e.ctrl_key() { + if self.ctrl_key() { modifiers.insert(Modifiers::CONTROL); } - if e.meta_key() { + if self.meta_key() { modifiers.insert(Modifiers::META); } - if e.shift_key() { + if self.shift_key() { modifiers.insert(Modifiers::SHIFT); } - Self::new( - Key::from_str(&e.key()).expect("could not parse key"), - Code::from_str(&e.code()).expect("could not parse code"), - decode_key_location( - e.location() - .try_into() - .expect("could not convert location to u32"), - ), - e.repeat(), - modifiers, + modifiers + } + + fn location(&self) -> keyboard_types::Location { + decode_key_location(self.location() as usize) + } + + fn is_auto_repeating(&self) -> bool { + self.repeat() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasDragData for MouseEvent {} + +impl PointInteraction for MouseEvent { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.client_x().into(), self.client_y().into()) + } + + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.offset_x().into(), self.offset_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.page_x().into(), self.page_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) + } + + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } + + fn held_buttons(&self) -> crate::input_data::MouseButtonSet { + decode_mouse_button_set(self.buttons()) + } + + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.button())) + } +} + +impl HasMouseData for MouseEvent { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasTouchData for TouchEvent { + fn alt_key(&self) -> bool { + self.alt_key() + } + + fn ctrl_key(&self) -> bool { + self.ctrl_key() + } + + fn meta_key(&self) -> bool { + self.meta_key() + } + + fn shift_key(&self) -> bool { + self.shift_key() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasPointerData for PointerEvent { + fn pointer_id(&self) -> i32 { + self.pointer_id() + } + + fn width(&self) -> i32 { + self.width() + } + + fn height(&self) -> i32 { + self.height() + } + + fn pressure(&self) -> f32 { + self.pressure() + } + + fn tangential_pressure(&self) -> f32 { + self.tangential_pressure() + } + + fn tilt_x(&self) -> i32 { + self.tilt_x() + } + + fn tilt_y(&self) -> i32 { + self.tilt_y() + } + + fn twist(&self) -> i32 { + self.twist() + } + + fn pointer_type(&self) -> String { + self.pointer_type() + } + + fn is_primary(&self) -> bool { + self.is_primary() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl PointInteraction for PointerEvent { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.client_x().into(), self.client_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) + } + + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.offset_x().into(), self.offset_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.page_x().into(), self.page_y().into()) + } + + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } + + fn held_buttons(&self) -> crate::input_data::MouseButtonSet { + decode_mouse_button_set(self.buttons()) + } + + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.button())) + } +} + +impl HasWheelData for WheelEvent { + fn delta(&self) -> crate::geometry::WheelDelta { + crate::geometry::WheelDelta::from_web_attributes( + self.delta_mode(), + self.delta_x(), + self.delta_y(), + self.delta_z(), ) } -} -impl From<&MouseEvent> for MouseData { - fn from(e: &MouseEvent) -> Self { - Self::new(PointData { - button: e.button(), - buttons: e.buttons(), - meta_key: e.meta_key(), - alt_key: e.alt_key(), - ctrl_key: e.ctrl_key(), - shift_key: e.shift_key(), - client_x: e.client_x(), - client_y: e.client_y(), - screen_x: e.screen_x(), - screen_y: e.screen_y(), - offset_x: e.offset_x(), - offset_y: e.offset_y(), - page_x: e.page_x(), - page_y: e.page_y(), - }) + fn as_any(&self) -> &dyn std::any::Any { + self } } -impl From<&MouseEvent> for DragData { - fn from(value: &MouseEvent) -> Self { - Self { - mouse: MouseData::from(value), - } +impl HasAnimationData for AnimationEvent { + fn animation_name(&self) -> String { + self.animation_name() + } + + fn pseudo_element(&self) -> String { + self.pseudo_element() + } + + fn elapsed_time(&self) -> f32 { + self.elapsed_time() + } + + fn as_any(&self) -> &dyn std::any::Any { + self } } -impl From<&TouchEvent> for TouchData { - fn from(e: &TouchEvent) -> Self { - Self { - alt_key: e.alt_key(), - ctrl_key: e.ctrl_key(), - meta_key: e.meta_key(), - shift_key: e.shift_key(), - } +impl HasTransitionData for TransitionEvent { + fn elapsed_time(&self) -> f32 { + self.elapsed_time() } -} -impl From<&PointerEvent> for PointerData { - fn from(e: &PointerEvent) -> Self { - Self::new( - PointData { - button: e.button(), - buttons: e.buttons(), - meta_key: e.meta_key(), - alt_key: e.alt_key(), - ctrl_key: e.ctrl_key(), - shift_key: e.shift_key(), - client_x: e.client_x(), - client_y: e.client_y(), - screen_x: e.screen_x(), - screen_y: e.screen_y(), - offset_x: e.offset_x(), - offset_y: e.offset_y(), - page_x: e.page_x(), - page_y: e.page_y(), - }, - e.pointer_id(), - e.width(), - e.height(), - e.pressure(), - e.tangential_pressure(), - e.tilt_x(), - e.tilt_y(), - e.twist(), - e.pointer_type(), - e.is_primary(), - ) + fn property_name(&self) -> String { + self.property_name() } -} -impl From<&WheelEvent> for WheelData { - fn from(e: &WheelEvent) -> Self { - WheelData::from_web_attributes(e.delta_mode(), e.delta_x(), e.delta_y(), e.delta_z()) + fn pseudo_element(&self) -> String { + self.pseudo_element() } -} -impl From<&AnimationEvent> for AnimationData { - fn from(e: &AnimationEvent) -> Self { - Self { - elapsed_time: e.elapsed_time(), - animation_name: e.animation_name(), - pseudo_element: e.pseudo_element(), - } - } -} - -impl From<&TransitionEvent> for TransitionData { - fn from(e: &TransitionEvent) -> Self { - Self { - elapsed_time: e.elapsed_time(), - property_name: e.property_name(), - pseudo_element: e.pseudo_element(), - } + fn as_any(&self) -> &dyn std::any::Any { + self } } @@ -209,8 +340,8 @@ impl crate::RenderedElementBacking for web_sys::Element { Box::pin(async { result }) } - fn get_raw_element(&self) -> crate::MountedResult<&dyn std::any::Any> { - Ok(self) + fn as_any(&self) -> &dyn std::any::Any { + self } fn scroll_to( @@ -254,3 +385,39 @@ impl std::fmt::Display for FocusError { } impl std::error::Error for FocusError {} + +impl HasClipboardData for Event { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl From<&Event> for ClipboardData { + fn from(e: &Event) -> Self { + ClipboardData::new(e.clone()) + } +} + +impl HasFocusData for web_sys::FocusEvent { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasToggleData for web_sys::Event { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasSelectionData for web_sys::Event { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasMediaData for web_sys::Event { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 9d0d837c5..d2e154129 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -10,12 +10,12 @@ use dioxus_core::{ BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode, }; -use dioxus_html::{event_bubbles, CompositionData, FormData, MountedData}; +use dioxus_html::{event_bubbles, FileEngine, HasFormData, HasImageData, MountedData}; use dioxus_interpreter_js::{get_node, minimal_bindings, save_template, Channel}; use futures_channel::mpsc; use js_sys::Array; use rustc_hash::FxHashMap; -use std::{any::Any, rc::Rc}; +use std::{any::Any, collections::HashMap, rc::Rc}; use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen, JsCast, JsValue}; use web_sys::{Document, Element, Event}; @@ -269,30 +269,29 @@ pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) - use dioxus_html::events::*; match event.type_().as_str() { - "copy" | "cut" | "paste" => Rc::new(ClipboardData {}), + "copy" | "cut" | "paste" => Rc::new(ClipboardData::from(event)), "compositionend" | "compositionstart" | "compositionupdate" => { - make_composition_event(&event) + Rc::new(CompositionData::from(event)) } "keydown" | "keypress" | "keyup" => Rc::new(KeyboardData::from(event)), - "focus" | "blur" | "focusout" | "focusin" => Rc::new(FocusData {}), + "focus" | "blur" | "focusout" | "focusin" => Rc::new(FocusData::from(event)), - "change" | "input" | "invalid" | "reset" | "submit" => read_input_to_data(target), + "change" | "input" | "invalid" | "reset" | "submit" => { + Rc::new(WebFormData::new(target, event)) + } "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => { Rc::new(MouseData::from(event)) } "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" - | "drop" => { - let mouse = MouseData::from(event); - Rc::new(DragData { mouse }) - } + | "drop" => Rc::new(DragData::from(event)), "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture" | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => { Rc::new(PointerData::from(event)) } - "select" => Rc::new(SelectionData {}), + "select" => Rc::new(SelectionData::from(event)), "touchcancel" | "touchend" | "touchmove" | "touchstart" => Rc::new(TouchData::from(event)), "scroll" => Rc::new(()), @@ -304,22 +303,15 @@ pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) - "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted" | "ended" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play" | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" - | "timeupdate" | "volumechange" | "waiting" => Rc::new(MediaData {}), - "error" => Rc::new(ImageData { load_error: true }), - "load" => Rc::new(ImageData { load_error: false }), - "toggle" => Rc::new(ToggleData {}), + | "timeupdate" | "volumechange" | "waiting" => Rc::new(MediaData::from(event)), + "error" => Rc::new(WebImageEvent::new(event, true)), + "load" => Rc::new(WebImageEvent::new(event, false)), + "toggle" => Rc::new(ToggleData::from(event)), _ => Rc::new(()), } } -fn make_composition_event(event: &Event) -> Rc { - let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap(); - Rc::new(CompositionData { - data: evt.data().unwrap_or_default(), - }) -} - pub(crate) fn load_document() -> Document { web_sys::window() .expect("should have access to the Window") @@ -327,11 +319,42 @@ pub(crate) fn load_document() -> Document { .expect("should have access to the Document") } -fn read_input_to_data(target: Element) -> Rc { - // todo: these handlers might get really slow if the input box gets large and allocation pressure is heavy - // don't have a good solution with the serialized event problem +struct WebImageEvent { + raw: Event, + error: bool, +} - let value: String = target +impl WebImageEvent { + fn new(raw: Event, error: bool) -> Self { + Self { raw, error } + } +} + +impl HasImageData for WebImageEvent { + fn load_error(&self) -> bool { + self.error + } + + fn as_any(&self) -> &dyn Any { + &self.raw + } +} + +struct WebFormData { + element: Element, + raw: Event, +} + +impl WebFormData { + fn new(element: Element, raw: Event) -> Self { + Self { element, raw } + } +} + +impl HasFormData for WebFormData { + fn value(&self) -> String { + let target = &self.element; + target .dyn_ref() .map(|input: &web_sys::HtmlInputElement| { // todo: special case more input types @@ -364,44 +387,53 @@ fn read_input_to_data(target: Element) -> Rc { .unwrap() .text_content() }) - .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener"); + .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener") + } - let mut values = std::collections::HashMap::new(); + fn values(&self) -> HashMap> { + let mut values = std::collections::HashMap::new(); - // try to fill in form values - if let Some(form) = target.dyn_ref::() { - let form_data = get_form_data(form); - for value in form_data.entries().into_iter().flatten() { - if let Ok(array) = value.dyn_into::() { - if let Some(name) = array.get(0).as_string() { - if let Ok(item_values) = array.get(1).dyn_into::() { - let item_values = - item_values.iter().filter_map(|v| v.as_string()).collect(); + // try to fill in form values + if let Some(form) = self.element.dyn_ref::() { + let form_data = get_form_data(form); + for value in form_data.entries().into_iter().flatten() { + if let Ok(array) = value.dyn_into::() { + if let Some(name) = array.get(0).as_string() { + if let Ok(item_values) = array.get(1).dyn_into::() { + let item_values = + item_values.iter().filter_map(|v| v.as_string()).collect(); - values.insert(name, item_values); + values.insert(name, item_values); + } } } } } + + values } - #[cfg(not(feature = "file_engine"))] - let files = None; - #[cfg(feature = "file_engine")] - let files = target - .dyn_ref() - .and_then(|input: &web_sys::HtmlInputElement| { - input.files().and_then(|files| { - crate::file_engine::WebFileEngine::new(files) - .map(|f| std::sync::Arc::new(f) as std::sync::Arc) - }) - }); + fn files(&self) -> Option> { + #[cfg(not(feature = "file_engine"))] + let files = None; + #[cfg(feature = "file_engine")] + let files = self + .element + .dyn_ref() + .and_then(|input: &web_sys::HtmlInputElement| { + input.files().and_then(|files| { + crate::file_engine::WebFileEngine::new(files).map(|f| { + std::sync::Arc::new(f) as std::sync::Arc + }) + }) + }); - Rc::new(FormData { - value, - values, - files, - }) + files + } + + fn as_any(&self) -> &dyn Any { + &self.raw + } } // web-sys does not expose the keys api for form data, so we need to manually bind to it From ca1a502714117f9ef72500a40a41aa6a37a5d27d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 25 Aug 2023 21:03:03 -0500 Subject: [PATCH 20/56] use an event converter for bundle splitting --- packages/core/src/events.rs | 21 +++ packages/html/src/events.rs | 173 +++++++++++++++++++++- packages/html/src/events/image.rs | 5 + packages/html/src/lib.rs | 2 +- packages/html/src/web_sys_bind/events.rs | 6 + packages/web/src/dom.rs | 177 ++++++++++++++++------- 6 files changed, 332 insertions(+), 52 deletions(-) diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index b85e0394d..644d69f29 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -28,6 +28,27 @@ pub struct Event { } impl Event { + /// Map the event data to a new type + /// + /// # Example + /// + /// ```rust, ignore + /// rsx! { + /// button { + /// onclick: move |evt: Event| { + /// let data = evt.map(|data| data.value()); + /// assert_eq!(data.inner(), "hello world"); + /// } + /// } + /// } + /// ``` + pub fn map U>(&self, f: F) -> Event { + Event { + data: Rc::new(f(&self.data)), + propagates: self.propagates.clone(), + } + } + /// Prevent this event from continuing to bubble up the tree to parent elements. /// /// # Example diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index f044b7b43..a24294378 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -1,3 +1,6 @@ +use std::any::Any; +use std::sync::RwLock; + macro_rules! impl_event { ( $data:ty; @@ -12,8 +15,8 @@ macro_rules! impl_event { pub fn $name<'a, E: crate::EventReturn, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute<'a> { ::dioxus_core::Attribute::new( stringify!($name), - _cx.listener(move |e: ::dioxus_core::Event<$data>| { - _f(e).spawn(_cx); + _cx.listener(move |e: ::dioxus_core::Event| { + _f(e.map(|e|e.into())).spawn(_cx); }), None, false, @@ -23,6 +26,172 @@ macro_rules! impl_event { }; } +static EVENT_CONVERTER: RwLock>> = RwLock::new(None); + +pub fn set_event_converter(converter: Box) { + *EVENT_CONVERTER.write().unwrap() = Some(converter); +} + +pub(crate) fn with_event_converter(f: F) -> R +where + F: FnOnce(&dyn HtmlEventConverter) -> R, +{ + let converter = EVENT_CONVERTER.read().unwrap(); + f(converter.as_ref().unwrap().as_ref()) +} + +/// A platform specific event. +pub struct PlatformEventData { + event: Box, +} + +impl PlatformEventData { + pub fn new(event: Box) -> Self { + Self { event } + } + + pub fn downcast(&self) -> Option<&T> { + self.event.downcast_ref::() + } + + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.event.downcast_mut::() + } + + pub fn into_inner(self) -> Option { + self.event.downcast::().ok().map(|e| *e) + } +} + +pub trait HtmlEventConverter: Send + Sync { + fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData; + fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData; + fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData; + fn convert_drag_data(&self, event: &PlatformEventData) -> DragData; + fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData; + fn convert_form_data(&self, event: &PlatformEventData) -> FormData; + fn convert_image_data(&self, event: &PlatformEventData) -> ImageData; + fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData; + fn convert_media_data(&self, event: &PlatformEventData) -> MediaData; + fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData; + fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData; + fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData; + fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData; + fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData; + fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData; + fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData; + fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData; + fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData; +} + +impl Into for &PlatformEventData { + fn into(self) -> AnimationData { + with_event_converter(|c| c.convert_animation_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> ClipboardData { + with_event_converter(|c| c.convert_clipboard_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> CompositionData { + with_event_converter(|c| c.convert_composition_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> DragData { + with_event_converter(|c| c.convert_drag_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> FocusData { + with_event_converter(|c| c.convert_focus_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> FormData { + with_event_converter(|c| c.convert_form_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> ImageData { + with_event_converter(|c| c.convert_image_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> KeyboardData { + with_event_converter(|c| c.convert_keyboard_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> MediaData { + with_event_converter(|c| c.convert_media_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> MountedData { + with_event_converter(|c| c.convert_mounted_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> MouseData { + with_event_converter(|c| c.convert_mouse_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> PointerData { + with_event_converter(|c| c.convert_pointer_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> ScrollData { + with_event_converter(|c| c.convert_scroll_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> SelectionData { + with_event_converter(|c| c.convert_selection_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> ToggleData { + with_event_converter(|c| c.convert_toggle_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> TouchData { + with_event_converter(|c| c.convert_touch_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> TransitionData { + with_event_converter(|c| c.convert_transition_data(self)) + } +} + +impl Into for &PlatformEventData { + fn into(self) -> WheelData { + with_event_converter(|c| c.convert_wheel_data(self)) + } +} + mod animation; mod clipboard; mod composition; diff --git a/packages/html/src/events/image.rs b/packages/html/src/events/image.rs index cf46838b7..e7d67ea69 100644 --- a/packages/html/src/events/image.rs +++ b/packages/html/src/events/image.rs @@ -26,6 +26,11 @@ impl PartialEq for ImageData { } impl ImageData { + /// Create a new ImageData + pub fn new(e: impl HasImageData) -> Self { + Self { inner: Box::new(e) } + } + /// If the renderer encountered an error while loading the image pub fn load_error(&self) -> bool { self.inner.load_error() diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 8168d0c5e..2a08241d9 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -38,7 +38,7 @@ pub use events::*; pub use global_attributes::*; pub use render_template::*; -mod eval; +pub mod eval; pub mod prelude { pub use crate::eval::*; diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index 757cc2ea0..643a06045 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -386,6 +386,12 @@ impl std::fmt::Display for FocusError { impl std::error::Error for FocusError {} +impl HasScrollData for Event { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + impl HasClipboardData for Event { fn as_any(&self) -> &dyn std::any::Any { self diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index d2e154129..c19886666 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -1,7 +1,6 @@ //! Implementation of a renderer for Dioxus on the web. //! -//! Oustanding todos: -//! - Removing event listeners (delegation) +//! Outstanding todos: //! - Passive event listeners //! - no-op event listener patch for safari //! - tests to ensure dyn_into works for various event types. @@ -10,7 +9,10 @@ use dioxus_core::{ BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode, }; -use dioxus_html::{event_bubbles, FileEngine, HasFormData, HasImageData, MountedData}; +use dioxus_html::{ + event_bubbles, FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter, ImageData, + MountedData, ScrollData, +}; use dioxus_interpreter_js::{get_node, minimal_bindings, save_template, Channel}; use futures_channel::mpsc; use js_sys::Array; @@ -263,53 +265,130 @@ impl WebsysDom { } } +struct WebEventConverter; + +fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent { + event + .downcast::() + .expect("event should be a GenericWebSysEvent") +} + +impl HtmlEventConverter for WebEventConverter { + fn convert_animation_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::AnimationData { + downcast_event(event).raw.clone().into() + } + + fn convert_clipboard_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ClipboardData { + downcast_event(event).raw.clone().into() + } + + fn convert_composition_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::CompositionData { + downcast_event(event).raw.clone().into() + } + + fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData { + downcast_event(event).raw.clone().into() + } + + fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData { + downcast_event(event).raw.clone().into() + } + + fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData { + let event = downcast_event(event); + FormData::new(WebFormData::new(event.element.clone(), event.raw.clone())) + } + + fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData { + let event = downcast_event(event); + let error = event.raw.type_() == "error"; + ImageData::new(WebImageEvent::new(event.raw.clone(), error)) + } + + fn convert_keyboard_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::KeyboardData { + downcast_event(event).raw.clone().into() + } + + fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData { + downcast_event(event).raw.clone().into() + } + + fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { + MountedData::from(downcast_event(event).element.clone()) + } + + fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData { + downcast_event(event).raw.clone().into() + } + + fn convert_pointer_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::PointerData { + downcast_event(event).raw.clone().into() + } + + fn convert_scroll_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ScrollData { + ScrollData::from(downcast_event(event).raw.clone()) + } + + fn convert_selection_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::SelectionData { + downcast_event(event).raw.clone().into() + } + + fn convert_toggle_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ToggleData { + downcast_event(event).raw.clone().into() + } + + fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData { + downcast_event(event).raw.clone().into() + } + + fn convert_transition_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::TransitionData { + downcast_event(event).raw.clone().into() + } + + fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData { + downcast_event(event).raw.clone().into() + } +} + +struct GenericWebSysEvent { + raw: Event, + element: Element, +} + // todo: some of these events are being casted to the wrong event type. // We need tests that simulate clicks/etc and make sure every event type works. pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -> Rc { - use dioxus_html::events::*; - - match event.type_().as_str() { - "copy" | "cut" | "paste" => Rc::new(ClipboardData::from(event)), - "compositionend" | "compositionstart" | "compositionupdate" => { - Rc::new(CompositionData::from(event)) - } - "keydown" | "keypress" | "keyup" => Rc::new(KeyboardData::from(event)), - "focus" | "blur" | "focusout" | "focusin" => Rc::new(FocusData::from(event)), - - "change" | "input" | "invalid" | "reset" | "submit" => { - Rc::new(WebFormData::new(target, event)) - } - - "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" - | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => { - Rc::new(MouseData::from(event)) - } - "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" - | "drop" => Rc::new(DragData::from(event)), - - "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture" - | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => { - Rc::new(PointerData::from(event)) - } - "select" => Rc::new(SelectionData::from(event)), - "touchcancel" | "touchend" | "touchmove" | "touchstart" => Rc::new(TouchData::from(event)), - - "scroll" => Rc::new(()), - "wheel" => Rc::new(WheelData::from(event)), - "animationstart" | "animationend" | "animationiteration" => { - Rc::new(AnimationData::from(event)) - } - "transitionend" => Rc::new(TransitionData::from(event)), - "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted" - | "ended" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play" - | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" - | "timeupdate" | "volumechange" | "waiting" => Rc::new(MediaData::from(event)), - "error" => Rc::new(WebImageEvent::new(event, true)), - "load" => Rc::new(WebImageEvent::new(event, false)), - "toggle" => Rc::new(ToggleData::from(event)), - - _ => Rc::new(()), - } + Rc::new(GenericWebSysEvent { + raw: event, + element: target, + }) } pub(crate) fn load_document() -> Document { @@ -336,7 +415,7 @@ impl HasImageData for WebImageEvent { } fn as_any(&self) -> &dyn Any { - &self.raw + &self.raw as &dyn Any } } @@ -432,7 +511,7 @@ impl HasFormData for WebFormData { } fn as_any(&self) -> &dyn Any { - &self.raw + &self.raw as &dyn Any } } From b54f7dd5d29bef6add9458ea64ecee94d0d81cd2 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 25 Aug 2023 21:28:44 -0500 Subject: [PATCH 21/56] fix no default features on the web renderer --- Cargo.toml | 2 +- packages/html/Cargo.toml | 6 +++++- packages/html/src/lib.rs | 2 ++ packages/web/Cargo.toml | 4 +++- packages/web/src/dom.rs | 10 +++++++++- packages/web/src/lib.rs | 1 + 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 252e9c83e..07f25171a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ dioxus-core = { path = "packages/core", version = "0.4.2" } dioxus-core-macro = { path = "packages/core-macro", version = "0.4.0" } dioxus-router = { path = "packages/router", version = "0.4.1" } dioxus-router-macro = { path = "packages/router-macro", version = "0.4.1" } -dioxus-html = { path = "packages/html", version = "0.4.0" } +dioxus-html = { path = "packages/html", default-features = false, version = "0.4.0" } dioxus-hooks = { path = "packages/hooks", version = "0.4.0" } dioxus-web = { path = "packages/web", version = "0.4.0" } dioxus-ssr = { path = "packages/ssr", version = "0.4.0" } diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 3a2495b02..b06ce3375 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -47,7 +47,7 @@ features = [ serde_json = "1" [features] -default = ["serialize", "mounted"] +default = ["serialize", "mounted", "eval"] serialize = [ "serde", "serde/rc", @@ -66,6 +66,10 @@ mounted = [ "web-sys/ScrollBehavior", "web-sys/HtmlElement", ] +eval = [ + "serde", + "serde_json" +] wasm-bind = ["web-sys", "wasm-bindgen"] native-bind = ["tokio"] hot-reload-context = ["dioxus-rsx"] diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 2a08241d9..55037b2dc 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -38,9 +38,11 @@ pub use events::*; pub use global_attributes::*; pub use render_template::*; +#[cfg(feature = "eval")] pub mod eval; pub mod prelude { + #[cfg(feature = "eval")] pub use crate::eval::*; pub use crate::events::*; pub use crate::point_interaction::PointInteraction; diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 8de282869..88ee3b680 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -65,7 +65,9 @@ hot_reload = [ "web-sys/WebSocket", "web-sys/Location", ] -eval = [] +eval = [ + "dioxus-html/eval", +] [dev-dependencies] dioxus = { workspace = true } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index c19886666..3a38b22b6 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -173,6 +173,7 @@ impl WebsysDom { pub fn apply_edits(&mut self, mut edits: Vec) { use Mutation::*; let i = &mut self.interpreter; + #[cfg(feature = "mounted")] // we need to apply the mount events last, so we collect them here let mut to_mount = Vec::new(); for edit in &edits { @@ -228,6 +229,7 @@ impl WebsysDom { match *name { // mounted events are fired immediately after the element is mounted. "mounted" => { + #[cfg(feature = "mounted")] to_mount.push(*id); } _ => { @@ -248,6 +250,7 @@ impl WebsysDom { edits.clear(); i.flush(); + #[cfg(feature = "mounted")] for id in to_mount { let node = get_node(id.0 as u32); if let Some(element) = node.dyn_ref::() { @@ -326,7 +329,12 @@ impl HtmlEventConverter for WebEventConverter { } fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { - MountedData::from(downcast_event(event).element.clone()) + #[cfg(feature = "mounted")] + {MountedData::from(downcast_event(event).element.clone())} + #[cfg(not(feature = "mounted"))] + { + panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled") + } } fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData { diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 261232607..ce1fad116 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -54,6 +54,7 @@ // - Do DOM work in the next requestAnimationFrame callback pub use crate::cfg::Config; +#[cfg(feature = "file_engine")] pub use crate::file_engine::WebFileEngineExt; use dioxus_core::{Element, Scope, VirtualDom}; use futures_util::{ From b4ebf34595d4e876716e6f45d91939f4166ac7cf Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 28 Aug 2023 11:39:55 -0500 Subject: [PATCH 22/56] forward data PointInteraction data in the PointerData debug and partial implementation --- packages/html/src/events/pointer.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 266dd7c66..280cc923e 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -29,6 +29,10 @@ impl std::fmt::Debug for PointerData { .field("twist", &self.twist()) .field("pointer_type", &self.pointer_type()) .field("is_primary", &self.is_primary()) + .field("coordinates", &self.coordinates()) + .field("modifiers", &self.modifiers()) + .field("held_buttons", &self.held_buttons()) + .field("trigger_button", &self.trigger_button()) .finish() } } @@ -45,6 +49,10 @@ impl PartialEq for PointerData { && self.twist() == other.twist() && self.pointer_type() == other.pointer_type() && self.is_primary() == other.is_primary() + && self.coordinates() == other.coordinates() + && self.modifiers() == other.modifiers() + && self.held_buttons() == other.held_buttons() + && self.trigger_button() == other.trigger_button() } } @@ -201,7 +209,7 @@ impl PointInteraction for PointerData { #[cfg(feature = "serialize")] #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedPointData { +struct SerializedPointerData { /// Common data for all pointer/mouse events #[serde(flatten)] point_data: crate::point_interaction::SerializedPointInteraction, @@ -238,7 +246,7 @@ struct SerializedPointData { } #[cfg(feature = "serialize")] -impl HasPointerData for SerializedPointData { +impl HasPointerData for SerializedPointerData { fn pointer_id(&self) -> i32 { self.pointer_id } @@ -285,7 +293,7 @@ impl HasPointerData for SerializedPointData { } #[cfg(feature = "serialize")] -impl PointInteraction for SerializedPointData { +impl PointInteraction for SerializedPointerData { fn client_coordinates(&self) -> ClientPoint { self.point_data.client_coordinates() } @@ -316,7 +324,7 @@ impl PointInteraction for SerializedPointData { } #[cfg(feature = "serialize")] -impl From<&PointerData> for SerializedPointData { +impl From<&PointerData> for SerializedPointerData { fn from(data: &PointerData) -> Self { Self { point_data: data.into(), @@ -337,14 +345,14 @@ impl From<&PointerData> for SerializedPointData { #[cfg(feature = "serialize")] impl serde::Serialize for PointerData { fn serialize(&self, serializer: S) -> Result { - SerializedPointData::from(self).serialize(serializer) + SerializedPointerData::from(self).serialize(serializer) } } #[cfg(feature = "serialize")] impl<'de> serde::Deserialize<'de> for PointerData { fn deserialize>(deserializer: D) -> Result { - let data = SerializedPointData::deserialize(deserializer)?; + let data = SerializedPointerData::deserialize(deserializer)?; Ok(Self { inner: Box::new(data), }) From f7dff22552725fcc8b3b3650dacf563f84eb24c6 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 28 Aug 2023 11:49:16 -0500 Subject: [PATCH 23/56] expose all serialized events as public --- packages/html/src/events/animation.rs | 3 ++- packages/html/src/events/clipboard.rs | 3 ++- packages/html/src/events/composition.rs | 3 ++- packages/html/src/events/drag.rs | 3 ++- packages/html/src/events/focus.rs | 3 ++- packages/html/src/events/image.rs | 3 ++- packages/html/src/events/keyboard.rs | 3 ++- packages/html/src/events/media.rs | 3 ++- packages/html/src/events/pointer.rs | 3 ++- packages/html/src/events/scroll.rs | 3 ++- packages/html/src/events/selection.rs | 3 ++- packages/html/src/events/toggle.rs | 3 ++- packages/html/src/events/touch.rs | 3 ++- packages/html/src/events/transition.rs | 3 ++- packages/html/src/events/wheel.rs | 3 ++- 15 files changed, 30 insertions(+), 15 deletions(-) diff --git a/packages/html/src/events/animation.rs b/packages/html/src/events/animation.rs index ca0a26567..6b4d398da 100644 --- a/packages/html/src/events/animation.rs +++ b/packages/html/src/events/animation.rs @@ -49,8 +49,9 @@ impl PartialEq for AnimationData { } #[cfg(feature = "serialize")] +/// A serialized version of AnimationData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedAnimationData { +pub struct SerializedAnimationData { animation_name: String, pseudo_element: String, elapsed_time: f32, diff --git a/packages/html/src/events/clipboard.rs b/packages/html/src/events/clipboard.rs index 84a9b1446..7f945378d 100644 --- a/packages/html/src/events/clipboard.rs +++ b/packages/html/src/events/clipboard.rs @@ -37,8 +37,9 @@ impl ClipboardData { } #[cfg(feature = "serialize")] +/// A serialized version of ClipboardData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedClipboardData {} +pub struct SerializedClipboardData {} #[cfg(feature = "serialize")] impl From<&ClipboardData> for SerializedClipboardData { diff --git a/packages/html/src/events/composition.rs b/packages/html/src/events/composition.rs index bf2f6214e..41671a077 100644 --- a/packages/html/src/events/composition.rs +++ b/packages/html/src/events/composition.rs @@ -37,8 +37,9 @@ impl CompositionData { } #[cfg(feature = "serialize")] +/// A serialized version of CompositionData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedCompositionData { +pub struct SerializedCompositionData { data: String, } diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index f7575c494..c8599850f 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -98,8 +98,9 @@ impl DragData { } #[cfg(feature = "serialize")] +/// A serialized version of DragData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedDragData { +pub struct SerializedDragData { mouse: crate::point_interaction::SerializedPointInteraction, } diff --git a/packages/html/src/events/focus.rs b/packages/html/src/events/focus.rs index 59e384f4a..af1da2c59 100644 --- a/packages/html/src/events/focus.rs +++ b/packages/html/src/events/focus.rs @@ -32,8 +32,9 @@ impl FocusData { } #[cfg(feature = "serialize")] +/// A serialized version of FocusData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedFocusData {} +pub struct SerializedFocusData {} #[cfg(feature = "serialize")] impl From<&FocusData> for SerializedFocusData { diff --git a/packages/html/src/events/image.rs b/packages/html/src/events/image.rs index e7d67ea69..4e094c925 100644 --- a/packages/html/src/events/image.rs +++ b/packages/html/src/events/image.rs @@ -42,8 +42,9 @@ impl ImageData { } #[cfg(feature = "serialize")] +/// A serialized version of ImageData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedImageData { +pub struct SerializedImageData { load_error: bool, } diff --git a/packages/html/src/events/keyboard.rs b/packages/html/src/events/keyboard.rs index 07fc00871..74109bea2 100644 --- a/packages/html/src/events/keyboard.rs +++ b/packages/html/src/events/keyboard.rs @@ -79,8 +79,9 @@ impl KeyboardData { } #[cfg(feature = "serialize")] +/// A serialized version of KeyboardData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedKeyboardData { +pub struct SerializedKeyboardData { auto_repeating: bool, #[serde(deserialize_with = "resilient_deserialize_code")] code: Code, diff --git a/packages/html/src/events/media.rs b/packages/html/src/events/media.rs index e088bd2a5..9382e6831 100644 --- a/packages/html/src/events/media.rs +++ b/packages/html/src/events/media.rs @@ -31,8 +31,9 @@ impl MediaData { } #[cfg(feature = "serialize")] +/// A serialized version of MediaData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedMediaData {} +pub struct SerializedMediaData {} #[cfg(feature = "serialize")] impl From<&MediaData> for SerializedMediaData { diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 280cc923e..cba360679 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -208,8 +208,9 @@ impl PointInteraction for PointerData { } #[cfg(feature = "serialize")] +/// A serialized version of PointerData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedPointerData { +pub struct SerializedPointerData { /// Common data for all pointer/mouse events #[serde(flatten)] point_data: crate::point_interaction::SerializedPointInteraction, diff --git a/packages/html/src/events/scroll.rs b/packages/html/src/events/scroll.rs index 01046240e..50be913d2 100644 --- a/packages/html/src/events/scroll.rs +++ b/packages/html/src/events/scroll.rs @@ -32,8 +32,9 @@ impl PartialEq for ScrollData { } #[cfg(feature = "serialize")] +/// A serialized version of ScrollData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedScrollData {} +pub struct SerializedScrollData {} #[cfg(feature = "serialize")] impl From<&ScrollData> for SerializedScrollData { diff --git a/packages/html/src/events/selection.rs b/packages/html/src/events/selection.rs index 34aa98d32..272b3e898 100644 --- a/packages/html/src/events/selection.rs +++ b/packages/html/src/events/selection.rs @@ -32,8 +32,9 @@ impl PartialEq for SelectionData { } #[cfg(feature = "serialize")] +/// A serialized version of SelectionData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedSelectionData {} +pub struct SerializedSelectionData {} #[cfg(feature = "serialize")] impl From<&SelectionData> for SerializedSelectionData { diff --git a/packages/html/src/events/toggle.rs b/packages/html/src/events/toggle.rs index 33e34b18b..f2240bcbf 100644 --- a/packages/html/src/events/toggle.rs +++ b/packages/html/src/events/toggle.rs @@ -32,8 +32,9 @@ impl ToggleData { } #[cfg(feature = "serialize")] +/// A serialized version of ToggleData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedToggleData {} +pub struct SerializedToggleData {} #[cfg(feature = "serialize")] impl From<&ToggleData> for SerializedToggleData { diff --git a/packages/html/src/events/touch.rs b/packages/html/src/events/touch.rs index 97043870d..4537bce92 100644 --- a/packages/html/src/events/touch.rs +++ b/packages/html/src/events/touch.rs @@ -59,8 +59,9 @@ impl TouchData { } #[cfg(feature = "serialize")] +/// A serialized version of TouchData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedTouchData { +pub struct SerializedTouchData { alt_key: bool, ctrl_key: bool, meta_key: bool, diff --git a/packages/html/src/events/transition.rs b/packages/html/src/events/transition.rs index 8acffe29c..9cd66b761 100644 --- a/packages/html/src/events/transition.rs +++ b/packages/html/src/events/transition.rs @@ -38,8 +38,9 @@ impl TransitionData { } #[cfg(feature = "serialize")] +/// A serialized version of TransitionData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedTransitionData { +pub struct SerializedTransitionData { property_name: String, pseudo_element: String, elapsed_time: f32, diff --git a/packages/html/src/events/wheel.rs b/packages/html/src/events/wheel.rs index 348f3760e..87048f4ce 100644 --- a/packages/html/src/events/wheel.rs +++ b/packages/html/src/events/wheel.rs @@ -43,8 +43,9 @@ impl WheelData { } #[cfg(feature = "serialize")] +/// A serialized version of WheelData #[derive(serde::Serialize, serde::Deserialize)] -struct SerializedWheelData { +pub struct SerializedWheelData { delta: WheelDelta, } From 93cbfd0dbcfd1cc292be86139ebeea38ca8f3fbd Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 28 Aug 2023 12:35:43 -0500 Subject: [PATCH 24/56] derive more traits for serialized types --- packages/html/Cargo.toml | 26 ++++---- packages/html/src/events/animation.rs | 2 +- packages/html/src/events/clipboard.rs | 2 +- packages/html/src/events/composition.rs | 2 +- packages/html/src/events/drag.rs | 2 +- packages/html/src/events/focus.rs | 2 +- packages/html/src/events/form.rs | 4 +- packages/html/src/events/image.rs | 2 +- packages/html/src/events/keyboard.rs | 25 ++++++- packages/html/src/events/media.rs | 2 +- packages/html/src/events/mouse.rs | 88 ++++++++++++++++++++++++- packages/html/src/events/pointer.rs | 2 +- packages/html/src/events/scroll.rs | 2 +- packages/html/src/events/selection.rs | 2 +- packages/html/src/events/toggle.rs | 2 +- packages/html/src/events/touch.rs | 2 +- packages/html/src/events/transition.rs | 2 +- packages/html/src/events/wheel.rs | 10 ++- packages/html/src/point_interaction.rs | 87 +++++++----------------- packages/html/src/transit.rs | 2 +- 20 files changed, 171 insertions(+), 97 deletions(-) diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index b06ce3375..7e061855e 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -16,7 +16,7 @@ serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } wasm-bindgen = { workspace = true, optional = true } euclid = "0.22.7" -enumset = "1.0.11" +enumset = "1.1.2" keyboard-types = "0.7" async-trait = "0.1.58" serde-value = "0.7.0" @@ -29,18 +29,18 @@ serde_json = { version = "1", optional = true } optional = true version = "0.3.56" features = [ - "TouchEvent", - "MouseEvent", - "InputEvent", - "ClipboardEvent", - "KeyboardEvent", - "TouchEvent", - "WheelEvent", - "AnimationEvent", - "TransitionEvent", - "PointerEvent", - "FocusEvent", - "CompositionEvent", + "TouchEvent", + "MouseEvent", + "InputEvent", + "ClipboardEvent", + "KeyboardEvent", + "TouchEvent", + "WheelEvent", + "AnimationEvent", + "TransitionEvent", + "PointerEvent", + "FocusEvent", + "CompositionEvent", ] [dev-dependencies] diff --git a/packages/html/src/events/animation.rs b/packages/html/src/events/animation.rs index 6b4d398da..02612bc77 100644 --- a/packages/html/src/events/animation.rs +++ b/packages/html/src/events/animation.rs @@ -50,7 +50,7 @@ impl PartialEq for AnimationData { #[cfg(feature = "serialize")] /// A serialized version of AnimationData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedAnimationData { animation_name: String, pseudo_element: String, diff --git a/packages/html/src/events/clipboard.rs b/packages/html/src/events/clipboard.rs index 7f945378d..0469c4f1c 100644 --- a/packages/html/src/events/clipboard.rs +++ b/packages/html/src/events/clipboard.rs @@ -38,7 +38,7 @@ impl ClipboardData { #[cfg(feature = "serialize")] /// A serialized version of ClipboardData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedClipboardData {} #[cfg(feature = "serialize")] diff --git a/packages/html/src/events/composition.rs b/packages/html/src/events/composition.rs index 41671a077..18ef4f5d1 100644 --- a/packages/html/src/events/composition.rs +++ b/packages/html/src/events/composition.rs @@ -38,7 +38,7 @@ impl CompositionData { #[cfg(feature = "serialize")] /// A serialized version of CompositionData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedCompositionData { data: String, } diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index c8599850f..004ea6612 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -99,7 +99,7 @@ impl DragData { #[cfg(feature = "serialize")] /// A serialized version of DragData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedDragData { mouse: crate::point_interaction::SerializedPointInteraction, } diff --git a/packages/html/src/events/focus.rs b/packages/html/src/events/focus.rs index af1da2c59..f96c1eaee 100644 --- a/packages/html/src/events/focus.rs +++ b/packages/html/src/events/focus.rs @@ -33,7 +33,7 @@ impl FocusData { #[cfg(feature = "serialize")] /// A serialized version of FocusData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone, Default)] pub struct SerializedFocusData {} #[cfg(feature = "serialize")] diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index 015559d64..de8bb0dbf 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -78,7 +78,7 @@ pub trait HasFormData: std::any::Any { #[cfg(feature = "serialize")] /// A serialized form data object -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedFormData { value: String, values: HashMap>, @@ -172,7 +172,7 @@ impl<'de> serde::Deserialize<'de> for FormData { #[cfg(feature = "serialize")] /// A file engine that serializes files to bytes -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedFileEngine { files: HashMap>, } diff --git a/packages/html/src/events/image.rs b/packages/html/src/events/image.rs index 4e094c925..3c75edbdd 100644 --- a/packages/html/src/events/image.rs +++ b/packages/html/src/events/image.rs @@ -43,7 +43,7 @@ impl ImageData { #[cfg(feature = "serialize")] /// A serialized version of ImageData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedImageData { load_error: bool, } diff --git a/packages/html/src/events/keyboard.rs b/packages/html/src/events/keyboard.rs index 74109bea2..2af582089 100644 --- a/packages/html/src/events/keyboard.rs +++ b/packages/html/src/events/keyboard.rs @@ -1,6 +1,5 @@ use dioxus_core::Event; use keyboard_types::{Code, Key, Location, Modifiers}; -use std::convert::TryInto; use std::fmt::Debug; #[cfg(feature = "serialize")] @@ -80,7 +79,7 @@ impl KeyboardData { #[cfg(feature = "serialize")] /// A serialized version of KeyboardData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedKeyboardData { auto_repeating: bool, #[serde(deserialize_with = "resilient_deserialize_code")] @@ -90,6 +89,26 @@ pub struct SerializedKeyboardData { modifiers: Modifiers, } +#[cfg(feature = "serialize")] +impl SerializedKeyboardData { + /// Create a new SerializedKeyboardData + pub fn new( + auto_repeating: bool, + code: Code, + key: Key, + location: Location, + modifiers: Modifiers, + ) -> Self { + Self { + auto_repeating, + code, + key, + location, + modifiers, + } + } +} + #[cfg(feature = "serialize")] impl From<&KeyboardData> for SerializedKeyboardData { fn from(data: &KeyboardData) -> Self { @@ -186,6 +205,8 @@ impl<'de> serde::Deserialize<'de> for KeyCode { where D: serde::Deserializer<'de>, { + use std::convert::TryInto; + // We could be deserializing a unicode character, so we need to use u64 even if the output only takes u8 let value = u64::deserialize(deserializer)?; diff --git a/packages/html/src/events/media.rs b/packages/html/src/events/media.rs index 9382e6831..1abcab21d 100644 --- a/packages/html/src/events/media.rs +++ b/packages/html/src/events/media.rs @@ -32,7 +32,7 @@ impl MediaData { #[cfg(feature = "serialize")] /// A serialized version of MediaData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedMediaData {} #[cfg(feature = "serialize")] diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index 6c346a434..0e73ef4f6 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -106,6 +106,13 @@ impl_event! { } impl MouseData { + /// Create a new instance of MouseData + pub fn new(inner: impl HasMouseData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// Downcast this event to a concrete event type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() @@ -172,23 +179,98 @@ impl PartialEq for MouseData { } #[cfg(feature = "serialize")] -impl HasMouseData for crate::point_interaction::SerializedPointInteraction { +/// A serialized version of [`MouseData`] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone, Default)] +pub struct SerializedMouseData { + /// Common data for all pointer/mouse events + #[serde(flatten)] + point_data: crate::point_interaction::SerializedPointInteraction, +} + +#[cfg(feature = "serialize")] +impl SerializedMouseData { + /// Create a new instance of SerializedMouseData + pub fn new( + client_coordinates: ClientPoint, + element_coordinates: ElementPoint, + page_coordinates: PagePoint, + screen_coordinates: ScreenPoint, + modifiers: Modifiers, + held_buttons: MouseButtonSet, + trigger_button: Option, + ) -> Self { + Self { + point_data: crate::point_interaction::SerializedPointInteraction::new( + client_coordinates, + element_coordinates, + page_coordinates, + screen_coordinates, + modifiers, + held_buttons, + trigger_button, + ), + } + } +} + +#[cfg(feature = "serialize")] +impl From<&MouseData> for SerializedMouseData { + fn from(e: &MouseData) -> Self { + Self { + point_data: crate::point_interaction::SerializedPointInteraction::from(e), + } + } +} + +#[cfg(feature = "serialize")] +impl HasMouseData for SerializedMouseData { fn as_any(&self) -> &dyn std::any::Any { self } } +#[cfg(feature = "serialize")] +impl PointInteraction for SerializedMouseData { + fn client_coordinates(&self) -> ClientPoint { + self.point_data.client_coordinates() + } + + fn element_coordinates(&self) -> ElementPoint { + self.point_data.element_coordinates() + } + + fn page_coordinates(&self) -> PagePoint { + self.point_data.page_coordinates() + } + + fn screen_coordinates(&self) -> ScreenPoint { + self.point_data.screen_coordinates() + } + + fn modifiers(&self) -> Modifiers { + self.point_data.modifiers() + } + + fn held_buttons(&self) -> MouseButtonSet { + self.point_data.held_buttons() + } + + fn trigger_button(&self) -> Option { + self.point_data.trigger_button() + } +} + #[cfg(feature = "serialize")] impl serde::Serialize for MouseData { fn serialize(&self, serializer: S) -> Result { - crate::point_interaction::SerializedPointInteraction::from(self).serialize(serializer) + SerializedMouseData::from(self).serialize(serializer) } } #[cfg(feature = "serialize")] impl<'de> serde::Deserialize<'de> for MouseData { fn deserialize>(deserializer: D) -> Result { - let data = crate::point_interaction::SerializedPointInteraction::deserialize(deserializer)?; + let data = SerializedMouseData::deserialize(deserializer)?; Ok(Self { inner: Box::new(data), }) diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index cba360679..5540dbe62 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -209,7 +209,7 @@ impl PointInteraction for PointerData { #[cfg(feature = "serialize")] /// A serialized version of PointerData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedPointerData { /// Common data for all pointer/mouse events #[serde(flatten)] diff --git a/packages/html/src/events/scroll.rs b/packages/html/src/events/scroll.rs index 50be913d2..4655afbcc 100644 --- a/packages/html/src/events/scroll.rs +++ b/packages/html/src/events/scroll.rs @@ -33,7 +33,7 @@ impl PartialEq for ScrollData { #[cfg(feature = "serialize")] /// A serialized version of ScrollData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedScrollData {} #[cfg(feature = "serialize")] diff --git a/packages/html/src/events/selection.rs b/packages/html/src/events/selection.rs index 272b3e898..de10b06c4 100644 --- a/packages/html/src/events/selection.rs +++ b/packages/html/src/events/selection.rs @@ -33,7 +33,7 @@ impl PartialEq for SelectionData { #[cfg(feature = "serialize")] /// A serialized version of SelectionData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedSelectionData {} #[cfg(feature = "serialize")] diff --git a/packages/html/src/events/toggle.rs b/packages/html/src/events/toggle.rs index f2240bcbf..7de8be36d 100644 --- a/packages/html/src/events/toggle.rs +++ b/packages/html/src/events/toggle.rs @@ -33,7 +33,7 @@ impl ToggleData { #[cfg(feature = "serialize")] /// A serialized version of ToggleData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedToggleData {} #[cfg(feature = "serialize")] diff --git a/packages/html/src/events/touch.rs b/packages/html/src/events/touch.rs index 4537bce92..7e9009ab6 100644 --- a/packages/html/src/events/touch.rs +++ b/packages/html/src/events/touch.rs @@ -60,7 +60,7 @@ impl TouchData { #[cfg(feature = "serialize")] /// A serialized version of TouchData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedTouchData { alt_key: bool, ctrl_key: bool, diff --git a/packages/html/src/events/transition.rs b/packages/html/src/events/transition.rs index 9cd66b761..c6dc65ae1 100644 --- a/packages/html/src/events/transition.rs +++ b/packages/html/src/events/transition.rs @@ -39,7 +39,7 @@ impl TransitionData { #[cfg(feature = "serialize")] /// A serialized version of TransitionData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedTransitionData { property_name: String, pseudo_element: String, diff --git a/packages/html/src/events/wheel.rs b/packages/html/src/events/wheel.rs index 87048f4ce..72550d835 100644 --- a/packages/html/src/events/wheel.rs +++ b/packages/html/src/events/wheel.rs @@ -44,11 +44,19 @@ impl WheelData { #[cfg(feature = "serialize")] /// A serialized version of WheelData -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedWheelData { delta: WheelDelta, } +#[cfg(feature = "serialize")] +impl SerializedWheelData { + /// Create a new SerializedWheelData + pub fn new(delta: WheelDelta) -> Self { + Self { delta } + } +} + #[cfg(feature = "serialize")] impl From<&WheelData> for SerializedWheelData { fn from(data: &WheelData) -> Self { diff --git a/packages/html/src/point_interaction.rs b/packages/html/src/point_interaction.rs index 1d6301176..0b2230ab3 100644 --- a/packages/html/src/point_interaction.rs +++ b/packages/html/src/point_interaction.rs @@ -5,67 +5,6 @@ use crate::{ input_data::{MouseButton, MouseButtonSet}, }; -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// #[derive(Copy, Clone, Default, PartialEq, Eq)] -// pub struct PointData { -// pub alt_key: bool, - -// /// The button number that was pressed (if applicable) when the mouse event was fired. -// pub button: i16, - -// /// Indicates which buttons are pressed on the mouse (or other input device) when a mouse event is triggered. -// /// -// /// Each button that can be pressed is represented by a given number (see below). If more than one button is pressed, the button values are added together to produce a new number. For example, if the secondary (2) and auxiliary (4) buttons are pressed simultaneously, the value is 6 (i.e., 2 + 4). -// /// -// /// - 1: Primary button (usually the left button) -// /// - 2: Secondary button (usually the right button) -// /// - 4: Auxiliary button (usually the mouse wheel button or middle button) -// /// - 8: 4th button (typically the "Browser Back" button) -// /// - 16 : 5th button (typically the "Browser Forward" button) -// pub buttons: u16, - -// /// The horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). -// /// -// /// For example, clicking on the left edge of the viewport will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally. -// pub client_x: i32, - -// /// The vertical coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). -// /// -// /// For example, clicking on the top edge of the viewport will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically. -// pub client_y: i32, - -// /// True if the control key was down when the mouse event was fired. -// pub ctrl_key: bool, - -// /// True if the meta key was down when the mouse event was fired. -// pub meta_key: bool, - -// /// The offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node. -// pub offset_x: i32, - -// /// The offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node. -// pub offset_y: i32, - -// /// The X (horizontal) coordinate (in pixels) of the mouse, relative to the left edge of the entire document. This includes any portion of the document not currently visible. -// /// -// /// Being based on the edge of the document as it is, this property takes into account any horizontal scrolling of the page. For example, if the page is scrolled such that 200 pixels of the left side of the document are scrolled out of view, and the mouse is clicked 100 pixels inward from the left edge of the view, the value returned by pageX will be 300. -// pub page_x: i32, - -// /// The Y (vertical) coordinate in pixels of the event relative to the whole document. -// /// -// /// See `page_x`. -// pub page_y: i32, - -// /// The X coordinate of the mouse pointer in global (screen) coordinates. -// pub screen_x: i32, - -// /// The Y coordinate of the mouse pointer in global (screen) coordinates. -// pub screen_y: i32, - -// /// True if the shift key was down when the mouse event was fired. -// pub shift_key: bool, -// } - pub trait PointInteraction: std::any::Any { /// Gets the coordinates of the pointer event. fn coordinates(&self) -> Coordinates { @@ -100,7 +39,7 @@ pub trait PointInteraction: std::any::Any { } #[cfg(feature = "serialize")] -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone, Default)] pub(crate) struct SerializedPointInteraction { pub(crate) client_coordinates: ClientPoint, pub(crate) element_coordinates: ElementPoint, @@ -111,6 +50,30 @@ pub(crate) struct SerializedPointInteraction { pub(crate) trigger_button: Option, } +#[cfg(feature = "serialize")] +impl SerializedPointInteraction { + /// Create a new serialized point interaction. + pub fn new( + client_coordinates: ClientPoint, + element_coordinates: ElementPoint, + page_coordinates: PagePoint, + screen_coordinates: ScreenPoint, + modifiers: Modifiers, + held_buttons: MouseButtonSet, + trigger_button: Option, + ) -> Self { + Self { + client_coordinates, + element_coordinates, + page_coordinates, + screen_coordinates, + modifiers, + held_buttons, + trigger_button, + } + } +} + #[cfg(feature = "serialize")] impl From<&E> for SerializedPointInteraction { fn from(data: &E) -> Self { diff --git a/packages/html/src/transit.rs b/packages/html/src/transit.rs index b27f8670a..b34a4b882 100644 --- a/packages/html/src/transit.rs +++ b/packages/html/src/transit.rs @@ -189,7 +189,7 @@ impl EventData { fn test_back_and_forth() { let data = HtmlEvent { element: ElementId(0), - data: EventData::Mouse(MouseData::default()), + data: EventData::Mouse(SerializedMouseData::default().into()), name: "click".to_string(), bubbles: true, }; From 0ef765e060dde3cb4a4ace929895fd5f9b89a9a5 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 28 Aug 2023 12:35:55 -0500 Subject: [PATCH 25/56] move rink over to the new event system --- packages/rink/src/hooks.rs | 134 ++++++++++++++----------- packages/rink/src/widgets/button.rs | 2 +- packages/rink/src/widgets/checkbox.rs | 2 +- packages/rink/src/widgets/number.rs | 2 +- packages/rink/src/widgets/slider.rs | 7 +- packages/rink/src/widgets/text_like.rs | 9 +- 6 files changed, 86 insertions(+), 70 deletions(-) diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index b6b34458c..f6caf484c 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -2,22 +2,21 @@ use crossterm::event::{ Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, ModifierKeyCode, MouseButton, MouseEventKind, }; +use dioxus_html::{ + HasFormData, HasKeyboardData, HasWheelData, SerializedFocusData, SerializedKeyboardData, + SerializedMouseData, SerializedWheelData, +}; use dioxus_native_core::prelude::*; use dioxus_native_core::real_dom::NodeImmutable; use rustc_hash::{FxHashMap, FxHashSet}; use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D}; -use dioxus_html::geometry::{ - ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint, WheelDelta, -}; +use dioxus_html::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint, WheelDelta}; use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers}; use dioxus_html::input_data::{ MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons, }; -use dioxus_html::point_interaction::PointData; -use dioxus_html::{ - event_bubbles, prelude::PointInteraction, FocusData, KeyboardData, MouseData, WheelData, -}; +use dioxus_html::{event_bubbles, prelude::PointInteraction}; use std::any::Any; use std::collections::HashMap; use std::{ @@ -42,10 +41,10 @@ pub struct Event { #[derive(Debug, Clone, PartialEq)] pub enum EventData { - Mouse(MouseData), - Keyboard(KeyboardData), - Focus(FocusData), - Wheel(WheelData), + Mouse(SerializedMouseData), + Keyboard(SerializedKeyboardData), + Focus(SerializedFocusData), + Wheel(SerializedWheelData), Form(FormData), } @@ -70,13 +69,23 @@ pub struct FormData { pub files: Option, } +impl HasFormData for FormData { + fn value(&self) -> String { + self.value.clone() + } + + fn values(&self) -> HashMap> { + self.values.clone() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + impl FormData { fn into_html(self) -> dioxus_html::FormData { - dioxus_html::FormData { - value: self.value, - values: self.values, - files: None, - } + dioxus_html::FormData::new(self) } } @@ -93,9 +102,9 @@ type EventCore = (&'static str, EventData); const MAX_REPEAT_TIME: Duration = Duration::from_millis(100); pub struct InnerInputState { - mouse: Option, - wheel: Option, - last_key_pressed: Option<(KeyboardData, Instant)>, + mouse: Option, + wheel: Option, + last_key_pressed: Option<(SerializedKeyboardData, Instant)>, pub(crate) focus_state: FocusState, // subscribers: Vec>, } @@ -137,12 +146,16 @@ impl InnerInputState { _ => {} } - let new_mouse_data = MouseData::new(PointData::new( - m.trigger_button(), - held_buttons, - m.coordinates(), + let coordinates = m.coordinates(); + let new_mouse_data = SerializedMouseData::new( + m.client_coordinates(), + coordinates.element(), + m.page_coordinates(), + m.screen_coordinates(), m.modifiers(), - )); + held_buttons, + m.trigger_button(), + ); self.mouse = Some(new_mouse_data.clone()); *m = new_mouse_data; @@ -159,7 +172,13 @@ impl InnerInputState { .is_some(); if is_repeating { - *k = KeyboardData::new(k.key(), k.code(), k.location(), true, k.modifiers()); + *k = SerializedKeyboardData::new( + is_repeating, + k.code(), + k.key(), + k.location(), + k.modifiers(), + ); } self.last_key_pressed = Some((k.clone(), Instant::now())); @@ -203,13 +222,13 @@ impl InnerInputState { resolved_events.push(Event { name: "focus", id, - data: EventData::Focus(FocusData {}), + data: EventData::Focus(SerializedFocusData::default()), bubbles: event_bubbles("focus"), }); resolved_events.push(Event { name: "focusin", id, - data: EventData::Focus(FocusData {}), + data: EventData::Focus(SerializedFocusData::default()), bubbles: event_bubbles("focusin"), }); } @@ -217,7 +236,7 @@ impl InnerInputState { resolved_events.push(Event { name: "focusout", id, - data: EventData::Focus(FocusData {}), + data: EventData::Focus(SerializedFocusData::default()), bubbles: event_bubbles("focusout"), }); } @@ -230,7 +249,7 @@ impl InnerInputState { fn resolve_mouse_events( &mut self, - previous_mouse: Option, + previous_mouse: Option, resolved_events: &mut Vec, layout: &Taffy, dom: &mut RealDom, @@ -277,7 +296,10 @@ impl InnerInputState { } } - fn prepare_mouse_data(mouse_data: &MouseData, layout: &Layout) -> MouseData { + fn prepare_mouse_data( + mouse_data: &SerializedMouseData, + layout: &Layout, + ) -> SerializedMouseData { let Point { x, y } = layout.location; let node_origin = ClientPoint::new( layout_to_screen_space(x).into(), @@ -288,19 +310,15 @@ impl InnerInputState { .to_point() .cast_unit(); - let coordinates = Coordinates::new( - mouse_data.screen_coordinates(), + SerializedMouseData::new( mouse_data.client_coordinates(), new_client_coordinates, mouse_data.page_coordinates(), - ); - - MouseData::new(PointData::new( - mouse_data.trigger_button(), - mouse_data.held_buttons(), - coordinates, + mouse_data.screen_coordinates(), mouse_data.modifiers(), - )) + mouse_data.held_buttons(), + mouse_data.trigger_button(), + ) } if let Some(mouse_data) = &self.mouse { @@ -693,17 +711,6 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> { // from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent - // The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively. - // todo? - // But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway. - let coordinates = Coordinates::new( - ScreenPoint::new(x, y), - ClientPoint::new(x, y), - // offset x/y are set when the origin of the event is assigned to an element - ElementPoint::new(0., 0.), - PagePoint::new(x, y), - ); - let mut modifiers = Modifiers::empty(); if shift { modifiers.insert(Modifiers::SHIFT); @@ -719,17 +726,24 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> { } // held mouse buttons get set later by maintaining state, as crossterm does not provide them - EventData::Mouse(MouseData::new(PointData::new( - button, - DioxusMouseButtons::empty(), - coordinates, + EventData::Mouse(SerializedMouseData::new( + // The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively. + // todo? + // But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway. + ClientPoint::new(x, y), + // offset x/y are set when the origin of the event is assigned to an element + ElementPoint::new(0., 0.), + PagePoint::new(x, y), + ScreenPoint::new(x, y), modifiers, - ))) + DioxusMouseButtons::empty(), + button, + )) }; let get_wheel_data = |up| { let y = if up { -1.0 } else { 1.0 }; - EventData::Wheel(WheelData::new(WheelDelta::lines(0., y, 0.))) + EventData::Wheel(SerializedWheelData::new(WheelDelta::lines(0., y, 0.))) }; match m.kind { @@ -754,11 +768,11 @@ fn translate_key_event(event: crossterm::event::KeyEvent) -> Option { let code = guess_code_from_crossterm_key_code(event.code)?; let modifiers = modifiers_from_crossterm_modifiers(event.modifiers); - Some(EventData::Keyboard(KeyboardData::new( - key, - code, - Location::Standard, + Some(EventData::Keyboard(SerializedKeyboardData::new( false, + code, + key, + Location::Standard, modifiers, ))) } diff --git a/packages/rink/src/widgets/button.rs b/packages/rink/src/widgets/button.rs index 523b2392a..d07dd79f0 100644 --- a/packages/rink/src/widgets/button.rs +++ b/packages/rink/src/widgets/button.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use dioxus_html::input_data::keyboard_types::Key; +use dioxus_html::{input_data::keyboard_types::Key, HasKeyboardData}; use dioxus_native_core::{ custom_element::CustomElement, node::OwnedAttributeDiscription, diff --git a/packages/rink/src/widgets/checkbox.rs b/packages/rink/src/widgets/checkbox.rs index fab809ed2..fe878e5b8 100644 --- a/packages/rink/src/widgets/checkbox.rs +++ b/packages/rink/src/widgets/checkbox.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use dioxus_html::input_data::keyboard_types::Key; +use dioxus_html::{input_data::keyboard_types::Key, HasKeyboardData}; use dioxus_native_core::{ custom_element::CustomElement, node::OwnedAttributeDiscription, diff --git a/packages/rink/src/widgets/number.rs b/packages/rink/src/widgets/number.rs index 371c8b41d..c2a71ceac 100644 --- a/packages/rink/src/widgets/number.rs +++ b/packages/rink/src/widgets/number.rs @@ -1,4 +1,4 @@ -use dioxus_html::input_data::keyboard_types::Key; +use dioxus_html::{input_data::keyboard_types::Key, HasKeyboardData}; use dioxus_native_core::{ custom_element::CustomElement, real_dom::{NodeImmutable, RealDom}, diff --git a/packages/rink/src/widgets/slider.rs b/packages/rink/src/widgets/slider.rs index 3af995e09..5cef645cf 100644 --- a/packages/rink/src/widgets/slider.rs +++ b/packages/rink/src/widgets/slider.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; use dioxus_html::{ - input_data::keyboard_types::Key, prelude::PointInteraction, KeyboardData, MouseData, + input_data::keyboard_types::Key, prelude::PointInteraction, HasKeyboardData, + SerializedKeyboardData, SerializedMouseData, }; use dioxus_native_core::{ custom_element::CustomElement, @@ -211,7 +212,7 @@ impl Slider { } } - fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) { + fn handle_keydown(&mut self, mut root: NodeMut, data: &SerializedKeyboardData) { let key = data.key(); let step = self.step(); @@ -233,7 +234,7 @@ impl Slider { self.write_value(rdom, id); } - fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) { + fn handle_mousemove(&mut self, mut root: NodeMut, data: &SerializedMouseData) { if !data.held_buttons().is_empty() { let id = root.id(); let rdom = root.real_dom_mut(); diff --git a/packages/rink/src/widgets/text_like.rs b/packages/rink/src/widgets/text_like.rs index 5bfebd0ab..e7869600a 100644 --- a/packages/rink/src/widgets/text_like.rs +++ b/packages/rink/src/widgets/text_like.rs @@ -2,7 +2,8 @@ use std::{collections::HashMap, io::stdout}; use crossterm::{cursor::MoveTo, execute}; use dioxus_html::{ - input_data::keyboard_types::Key, prelude::PointInteraction, KeyboardData, MouseData, + input_data::keyboard_types::Key, prelude::PointInteraction, HasKeyboardData, + SerializedKeyboardData, SerializedMouseData, }; use dioxus_native_core::{ custom_element::CustomElement, @@ -186,7 +187,7 @@ impl TextLike { } } - fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) { + fn handle_keydown(&mut self, mut root: NodeMut, data: &SerializedKeyboardData) { let key = data.key(); let modifiers = data.modifiers(); let code = data.code(); @@ -230,7 +231,7 @@ impl TextLike { } } - fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) { + fn handle_mousemove(&mut self, mut root: NodeMut, data: &SerializedMouseData) { if self.dragging { let id = root.id(); let offset = data.element_coordinates(); @@ -247,7 +248,7 @@ impl TextLike { } } - fn handle_mousedown(&mut self, mut root: NodeMut, data: &MouseData) { + fn handle_mousedown(&mut self, mut root: NodeMut, data: &SerializedMouseData) { let offset = data.element_coordinates(); let mut new = Pos::new(offset.x as usize, offset.y as usize); From 62556b2c34eab4cf33bafd1c6e556278f11bcd26 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 28 Aug 2023 12:43:21 -0500 Subject: [PATCH 26/56] fix desktop and liveview compilation --- packages/desktop/Cargo.toml | 2 +- packages/liveview/Cargo.toml | 2 +- packages/liveview/src/element.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index a85c2e045..aaeb7f6ca 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["dom", "ui", "gui", "react"] [dependencies] dioxus-core = { workspace = true, features = ["serialize"] } -dioxus-html = { workspace = true, features = ["serialize", "native-bind"] } +dioxus-html = { workspace = true, features = ["serialize", "native-bind", "mounted", "eval"] } dioxus-interpreter-js = { workspace = true } dioxus-hot-reload = { workspace = true, optional = true } diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 3dfce5bf4..a1309e809 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -22,7 +22,7 @@ tokio-stream = { version = "0.1.11", features = ["net"] } tokio-util = { version = "0.7.4", features = ["rt"] } serde = { version = "1.0.151", features = ["derive"] } serde_json = "1.0.91" -dioxus-html = { workspace = true, features = ["serialize"] } +dioxus-html = { workspace = true, features = ["serialize", "eval", "mounted"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-interpreter-js = { workspace = true } dioxus-hot-reload = { workspace = true, optional = true } diff --git a/packages/liveview/src/element.rs b/packages/liveview/src/element.rs index ae0eb4efe..d813a47d5 100644 --- a/packages/liveview/src/element.rs +++ b/packages/liveview/src/element.rs @@ -16,8 +16,8 @@ impl LiveviewElement { } impl RenderedElementBacking for LiveviewElement { - fn get_raw_element(&self) -> dioxus_html::MountedResult<&dyn std::any::Any> { - Ok(self) + fn as_any(&self) -> &dyn std::any::Any { + self } fn get_client_rect( From bd387d601c1f721fe575858fb6ba5b124b719b6a Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 28 Aug 2023 15:49:46 -0500 Subject: [PATCH 27/56] fix formatting --- packages/html/src/events/drag.rs | 4 ++-- packages/web/src/dom.rs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index 004ea6612..aa01b4748 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -1,6 +1,6 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; use crate::input_data::{MouseButton, MouseButtonSet}; -use crate::prelude::PointInteraction; + use dioxus_core::Event; use keyboard_types::Modifiers; @@ -132,7 +132,7 @@ impl HasMouseData for SerializedDragData { } #[cfg(feature = "serialize")] -impl PointInteraction for SerializedDragData { +impl crate::prelude::PointInteraction for SerializedDragData { fn client_coordinates(&self) -> ClientPoint { self.mouse.client_coordinates() } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 3a38b22b6..1b7a11ca0 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -330,7 +330,9 @@ impl HtmlEventConverter for WebEventConverter { fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { #[cfg(feature = "mounted")] - {MountedData::from(downcast_event(event).element.clone())} + { + MountedData::from(downcast_event(event).element.clone()) + } #[cfg(not(feature = "mounted"))] { panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled") From 8ba1c05a939b0e31b59d075a6490e0d35fc21f6e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 28 Aug 2023 16:37:19 -0500 Subject: [PATCH 28/56] fix desktop events --- packages/desktop/src/element.rs | 1 + packages/desktop/src/events.rs | 147 ++++++++++++++++++++++ packages/desktop/src/lib.rs | 15 ++- packages/html/src/events/drag.rs | 94 ++++++-------- packages/html/src/events/keyboard.rs | 73 +++++++---- packages/html/src/events/mouse.rs | 18 +-- packages/html/src/events/wheel.rs | 24 +++- packages/html/src/point_interaction.rs | 165 +++++++++++++++++-------- packages/html/src/transit.rs | 106 ++++++++++------ packages/rink/src/hooks.rs | 54 ++++---- packages/web/src/dom.rs | 18 +-- packages/web/src/lib.rs | 9 +- 12 files changed, 507 insertions(+), 217 deletions(-) diff --git a/packages/desktop/src/element.rs b/packages/desktop/src/element.rs index 8239099cb..86741d415 100644 --- a/packages/desktop/src/element.rs +++ b/packages/desktop/src/element.rs @@ -3,6 +3,7 @@ use dioxus_html::{geometry::euclid::Rect, MountedResult, RenderedElementBacking} use crate::{desktop_context::DesktopContext, query::QueryEngine}; +#[derive(Clone)] /// A mounted element passed to onmounted events pub struct DesktopElement { id: ElementId, diff --git a/packages/desktop/src/events.rs b/packages/desktop/src/events.rs index 7059a7c85..a4fbb4e1e 100644 --- a/packages/desktop/src/events.rs +++ b/packages/desktop/src/events.rs @@ -1,7 +1,10 @@ //! Convert a serialized event to an event trigger +use dioxus_html::*; use serde::{Deserialize, Serialize}; +use crate::element::DesktopElement; + #[derive(Deserialize, Serialize, Debug, Clone)] pub struct IpcMessage { method: String, @@ -17,3 +20,147 @@ impl IpcMessage { self.params } } + +pub(crate) struct SerializedHtmlEventConverter; + +impl HtmlEventConverter for SerializedHtmlEventConverter { + fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_drag_data(&self, event: &PlatformEventData) -> DragData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_form_data(&self, event: &PlatformEventData) -> FormData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_image_data(&self, event: &PlatformEventData) -> ImageData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_media_data(&self, event: &PlatformEventData) -> MediaData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData { + event.downcast::().cloned().unwrap().into() + } + + fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } +} diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index e1a94fbfc..3d72b487c 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -28,9 +28,10 @@ pub use desktop_context::{ use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers}; use dioxus_core::*; use dioxus_html::{native_bind::NativeFileEngine, HtmlEvent}; -use dioxus_html::{FileEngine, HasFormData, MountedData}; +use dioxus_html::{FileEngine, HasFormData, MountedData, PlatformEventData}; use element::DesktopElement; use eval::init_eval; +use events::SerializedHtmlEventConverter; use futures_util::{pin_mut, FutureExt}; use shortcut::ShortcutRegistry; pub use shortcut::{use_global_shortcut, ShortcutHandle, ShortcutId, ShortcutRegistryError}; @@ -136,6 +137,9 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) } }); + // Set the event converter + dioxus_html::set_event_converter(Box::new(SerializedHtmlEventConverter)); + // We start the tokio runtime *on this thread* // Any future we poll later will use this runtime to spawn tasks and for IO let rt = tokio::runtime::Builder::new_multi_thread() @@ -292,7 +296,7 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) let element = DesktopElement::new(element, view.desktop_context.clone(), query); - Rc::new(MountedData::new(element)) + Rc::new(PlatformEventData::new(Box::new(MountedData::new(element)))) } else { data.into_any() }; @@ -358,9 +362,10 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) let event_name = &file_diolog.event; let event_bubbles = file_diolog.bubbles; let files = file_upload::get_file_event(&file_diolog); - let data = Rc::new(DesktopFileUploadForm { - files: Arc::new(NativeFileEngine::new(files)), - }); + let data = + Rc::new(PlatformEventData::new(Box::new(DesktopFileUploadForm { + files: Arc::new(NativeFileEngine::new(files)), + }))); let view = webviews.get_mut(&event.1).unwrap(); diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index aa01b4748..67da07721 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -1,5 +1,6 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; use crate::input_data::{MouseButton, MouseButtonSet}; +use crate::prelude::PointInteraction; use dioxus_core::Event; use keyboard_types::Modifiers; @@ -43,60 +44,47 @@ impl PartialEq for DragData { } impl DragData { - /// The event's coordinates relative to the application's viewport (as opposed to the coordinate within the page). - /// - /// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally. - pub fn client_coordinates(&self) -> ClientPoint { - self.inner.client_coordinates() - } - - /// The event's coordinates relative to the padding edge of the target element - /// - /// For example, clicking in the top left corner of an element will result in element coordinates (0., 0.) - pub fn element_coordinates(&self) -> ElementPoint { - self.inner.element_coordinates() - } - - /// The event's coordinates relative to the entire document. This includes any portion of the document not currently visible. - /// - /// For example, if the page is scrolled 200 pixels to the right and 300 pixels down, clicking in the top left corner of the viewport would result in page coordinates (200., 300.) - pub fn page_coordinates(&self) -> PagePoint { - self.inner.page_coordinates() - } - - /// The event's coordinates relative to the entire screen. This takes into account the window's offset. - pub fn screen_coordinates(&self) -> ScreenPoint { - self.inner.screen_coordinates() - } - - pub fn coordinates(&self) -> Coordinates { - self.inner.coordinates() - } - - /// The set of modifier keys which were pressed when the event occurred - pub fn modifiers(&self) -> Modifiers { - self.inner.modifiers() - } - - /// The set of mouse buttons which were held when the event occurred. - pub fn held_buttons(&self) -> MouseButtonSet { - self.inner.held_buttons() - } - - /// The mouse button that triggered the event - /// - // todo the following is kind of bad; should we just return None when the trigger_button is unreliable (and frankly irrelevant)? i guess we would need the event_type here - /// This is only guaranteed to indicate which button was pressed during events caused by pressing or releasing a button. As such, it is not reliable for events such as mouseenter, mouseleave, mouseover, mouseout, or mousemove. For example, a value of MouseButton::Primary may also indicate that no button was pressed. - pub fn trigger_button(&self) -> Option { - self.inner.trigger_button() - } - /// Downcast this event data to a specific type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() } } +impl PointInteraction for DragData { + fn client_coordinates(&self) -> ClientPoint { + self.inner.client_coordinates() + } + + fn element_coordinates(&self) -> ElementPoint { + self.inner.element_coordinates() + } + + fn page_coordinates(&self) -> PagePoint { + self.inner.page_coordinates() + } + + fn screen_coordinates(&self) -> ScreenPoint { + self.inner.screen_coordinates() + } + + fn coordinates(&self) -> Coordinates { + self.inner.coordinates() + } + + fn modifiers(&self) -> Modifiers { + self.inner.modifiers() + } + + fn held_buttons(&self) -> MouseButtonSet { + self.inner.held_buttons() + } + + // todo the following is kind of bad; should we just return None when the trigger_button is unreliable (and frankly irrelevant)? i guess we would need the event_type here + fn trigger_button(&self) -> Option { + self.inner.trigger_button() + } +} + #[cfg(feature = "serialize")] /// A serialized version of DragData #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] @@ -108,15 +96,7 @@ pub struct SerializedDragData { impl From<&DragData> for SerializedDragData { fn from(data: &DragData) -> Self { Self { - mouse: crate::point_interaction::SerializedPointInteraction { - client_coordinates: data.client_coordinates(), - element_coordinates: data.element_coordinates(), - page_coordinates: data.page_coordinates(), - screen_coordinates: data.screen_coordinates(), - modifiers: data.modifiers(), - held_buttons: data.held_buttons(), - trigger_button: data.trigger_button(), - }, + mouse: crate::point_interaction::SerializedPointInteraction::from(data), } } } diff --git a/packages/html/src/events/keyboard.rs b/packages/html/src/events/keyboard.rs index 2af582089..c4c6115c1 100644 --- a/packages/html/src/events/keyboard.rs +++ b/packages/html/src/events/keyboard.rs @@ -81,30 +81,46 @@ impl KeyboardData { /// A serialized version of KeyboardData #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedKeyboardData { - auto_repeating: bool, + char_code: u32, + key: String, + key_code: KeyCode, #[serde(deserialize_with = "resilient_deserialize_code")] code: Code, - key: Key, - location: Location, - modifiers: Modifiers, + alt_key: bool, + ctrl_key: bool, + meta_key: bool, + shift_key: bool, + location: usize, + repeat: bool, + which: usize, } #[cfg(feature = "serialize")] impl SerializedKeyboardData { /// Create a new SerializedKeyboardData pub fn new( - auto_repeating: bool, - code: Code, key: Key, + code: Code, location: Location, + is_auto_repeating: bool, modifiers: Modifiers, ) -> Self { Self { - auto_repeating, + char_code: key.legacy_charcode(), + key: key.to_string(), + key_code: KeyCode::from_raw_code( + std::convert::TryInto::try_into(key.legacy_keycode()) + .expect("could not convert keycode to u8"), + ), code, - key, - location, - modifiers, + alt_key: modifiers.contains(Modifiers::ALT), + ctrl_key: modifiers.contains(Modifiers::CONTROL), + meta_key: modifiers.contains(Modifiers::META), + shift_key: modifiers.contains(Modifiers::SHIFT), + location: crate::input_data::encode_key_location(location), + repeat: is_auto_repeating, + which: std::convert::TryInto::try_into(key.legacy_charcode()) + .expect("could not convert charcode to usize"), } } } @@ -112,20 +128,20 @@ impl SerializedKeyboardData { #[cfg(feature = "serialize")] impl From<&KeyboardData> for SerializedKeyboardData { fn from(data: &KeyboardData) -> Self { - Self { - auto_repeating: data.is_auto_repeating(), - code: data.code(), - key: data.key(), - location: data.location(), - modifiers: data.modifiers(), - } + Self::new( + data.key(), + data.code(), + data.location(), + data.is_auto_repeating(), + data.modifiers(), + ) } } #[cfg(feature = "serialize")] impl HasKeyboardData for SerializedKeyboardData { fn key(&self) -> Key { - self.key.clone() + std::str::FromStr::from_str(&self.key).unwrap_or(Key::Unidentified) } fn code(&self) -> Code { @@ -133,15 +149,30 @@ impl HasKeyboardData for SerializedKeyboardData { } fn modifiers(&self) -> Modifiers { - self.modifiers + let mut modifiers = Modifiers::empty(); + + if self.alt_key { + modifiers.insert(Modifiers::ALT); + } + if self.ctrl_key { + modifiers.insert(Modifiers::CONTROL); + } + if self.meta_key { + modifiers.insert(Modifiers::META); + } + if self.shift_key { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers } fn location(&self) -> Location { - self.location + crate::input_data::decode_key_location(self.location) } fn is_auto_repeating(&self) -> bool { - self.auto_repeating + self.repeat } fn as_any(&self) -> &dyn std::any::Any { diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index 0e73ef4f6..c6a98e25e 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -191,23 +191,17 @@ pub struct SerializedMouseData { impl SerializedMouseData { /// Create a new instance of SerializedMouseData pub fn new( - client_coordinates: ClientPoint, - element_coordinates: ElementPoint, - page_coordinates: PagePoint, - screen_coordinates: ScreenPoint, - modifiers: Modifiers, - held_buttons: MouseButtonSet, trigger_button: Option, + held_buttons: MouseButtonSet, + coordinates: Coordinates, + modifiers: Modifiers, ) -> Self { Self { point_data: crate::point_interaction::SerializedPointInteraction::new( - client_coordinates, - element_coordinates, - page_coordinates, - screen_coordinates, - modifiers, - held_buttons, trigger_button, + held_buttons, + coordinates, + modifiers, ), } } diff --git a/packages/html/src/events/wheel.rs b/packages/html/src/events/wheel.rs index 72550d835..157a8dc06 100644 --- a/packages/html/src/events/wheel.rs +++ b/packages/html/src/events/wheel.rs @@ -46,30 +46,42 @@ impl WheelData { /// A serialized version of WheelData #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] pub struct SerializedWheelData { - delta: WheelDelta, + pub delta_mode: u32, + pub delta_x: f64, + pub delta_y: f64, + pub delta_z: f64, } #[cfg(feature = "serialize")] impl SerializedWheelData { /// Create a new SerializedWheelData pub fn new(delta: WheelDelta) -> Self { - Self { delta } + let delta_mode = match delta { + WheelDelta::Pixels(_) => 0, + WheelDelta::Lines(_) => 1, + WheelDelta::Pages(_) => 2, + }; + let delta_raw = delta.strip_units(); + Self { + delta_mode, + delta_x: delta_raw.x, + delta_y: delta_raw.y, + delta_z: delta_raw.z, + } } } #[cfg(feature = "serialize")] impl From<&WheelData> for SerializedWheelData { fn from(data: &WheelData) -> Self { - Self { - delta: data.inner.delta(), - } + Self::new(data.delta()) } } #[cfg(feature = "serialize")] impl HasWheelData for SerializedWheelData { fn delta(&self) -> WheelDelta { - self.delta + WheelDelta::from_web_attributes(self.delta_mode, self.delta_x, self.delta_y, self.delta_z) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/packages/html/src/point_interaction.rs b/packages/html/src/point_interaction.rs index 0b2230ab3..f26917201 100644 --- a/packages/html/src/point_interaction.rs +++ b/packages/html/src/point_interaction.rs @@ -41,35 +41,98 @@ pub trait PointInteraction: std::any::Any { #[cfg(feature = "serialize")] #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone, Default)] pub(crate) struct SerializedPointInteraction { - pub(crate) client_coordinates: ClientPoint, - pub(crate) element_coordinates: ElementPoint, - pub(crate) page_coordinates: PagePoint, - pub(crate) screen_coordinates: ScreenPoint, - pub(crate) modifiers: Modifiers, - pub(crate) held_buttons: MouseButtonSet, - pub(crate) trigger_button: Option, + pub alt_key: bool, + + /// The button number that was pressed (if applicable) when the mouse event was fired. + pub button: i16, + + /// Indicates which buttons are pressed on the mouse (or other input device) when a mouse event is triggered. + /// + /// Each button that can be pressed is represented by a given number (see below). If more than one button is pressed, the button values are added together to produce a new number. For example, if the secondary (2) and auxiliary (4) buttons are pressed simultaneously, the value is 6 (i.e., 2 + 4). + /// + /// - 1: Primary button (usually the left button) + /// - 2: Secondary button (usually the right button) + /// - 4: Auxiliary button (usually the mouse wheel button or middle button) + /// - 8: 4th button (typically the "Browser Back" button) + /// - 16 : 5th button (typically the "Browser Forward" button) + pub buttons: u16, + + /// The horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). + /// + /// For example, clicking on the left edge of the viewport will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally. + pub client_x: i32, + + /// The vertical coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page). + /// + /// For example, clicking on the top edge of the viewport will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically. + pub client_y: i32, + + /// True if the control key was down when the mouse event was fired. + pub ctrl_key: bool, + + /// True if the meta key was down when the mouse event was fired. + pub meta_key: bool, + + /// The offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node. + pub offset_x: i32, + + /// The offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node. + pub offset_y: i32, + + /// The X (horizontal) coordinate (in pixels) of the mouse, relative to the left edge of the entire document. This includes any portion of the document not currently visible. + /// + /// Being based on the edge of the document as it is, this property takes into account any horizontal scrolling of the page. For example, if the page is scrolled such that 200 pixels of the left side of the document are scrolled out of view, and the mouse is clicked 100 pixels inward from the left edge of the view, the value returned by pageX will be 300. + pub page_x: i32, + + /// The Y (vertical) coordinate in pixels of the event relative to the whole document. + /// + /// See `page_x`. + pub page_y: i32, + + /// The X coordinate of the mouse pointer in global (screen) coordinates. + pub screen_x: i32, + + /// The Y coordinate of the mouse pointer in global (screen) coordinates. + pub screen_y: i32, + + /// True if the shift key was down when the mouse event was fired. + pub shift_key: bool, } #[cfg(feature = "serialize")] impl SerializedPointInteraction { - /// Create a new serialized point interaction. pub fn new( - client_coordinates: ClientPoint, - element_coordinates: ElementPoint, - page_coordinates: PagePoint, - screen_coordinates: ScreenPoint, - modifiers: Modifiers, - held_buttons: MouseButtonSet, trigger_button: Option, + held_buttons: MouseButtonSet, + coordinates: Coordinates, + modifiers: Modifiers, ) -> Self { + let alt_key = modifiers.contains(Modifiers::ALT); + let ctrl_key = modifiers.contains(Modifiers::CONTROL); + let meta_key = modifiers.contains(Modifiers::META); + let shift_key = modifiers.contains(Modifiers::SHIFT); + + let [client_x, client_y]: [i32; 2] = coordinates.client().cast().into(); + let [offset_x, offset_y]: [i32; 2] = coordinates.element().cast().into(); + let [page_x, page_y]: [i32; 2] = coordinates.page().cast().into(); + let [screen_x, screen_y]: [i32; 2] = coordinates.screen().cast().into(); Self { - client_coordinates, - element_coordinates, - page_coordinates, - screen_coordinates, - modifiers, - held_buttons, - trigger_button, + button: trigger_button + .map_or(MouseButton::default(), |b| b) + .into_web_code(), + buttons: crate::input_data::encode_mouse_button_set(held_buttons), + meta_key, + ctrl_key, + shift_key, + alt_key, + client_x, + client_y, + screen_x, + screen_y, + offset_x, + offset_y, + page_x, + page_y, } } } @@ -77,54 +140,56 @@ impl SerializedPointInteraction { #[cfg(feature = "serialize")] impl From<&E> for SerializedPointInteraction { fn from(data: &E) -> Self { - Self { - client_coordinates: data.client_coordinates(), - element_coordinates: data.element_coordinates(), - page_coordinates: data.page_coordinates(), - screen_coordinates: data.screen_coordinates(), - modifiers: data.modifiers(), - held_buttons: data.held_buttons(), - trigger_button: data.trigger_button(), - } + let trigger_button = data.trigger_button(); + let held_buttons = data.held_buttons(); + let coordinates = data.coordinates(); + let modifiers = data.modifiers(); + Self::new(trigger_button, held_buttons, coordinates, modifiers) } } #[cfg(feature = "serialize")] impl PointInteraction for SerializedPointInteraction { fn client_coordinates(&self) -> ClientPoint { - self.client_coordinates - } - - fn element_coordinates(&self) -> ElementPoint { - self.element_coordinates - } - - fn page_coordinates(&self) -> PagePoint { - self.page_coordinates + ClientPoint::new(self.client_x.into(), self.client_y.into()) } fn screen_coordinates(&self) -> ScreenPoint { - self.screen_coordinates + ScreenPoint::new(self.screen_x.into(), self.screen_y.into()) } - fn coordinates(&self) -> Coordinates { - Coordinates::new( - self.screen_coordinates(), - self.client_coordinates(), - self.element_coordinates(), - self.page_coordinates(), - ) + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.offset_x.into(), self.offset_y.into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.page_x.into(), self.page_y.into()) } fn modifiers(&self) -> Modifiers { - self.modifiers + let mut modifiers = Modifiers::empty(); + + if self.alt_key { + modifiers.insert(Modifiers::ALT); + } + if self.ctrl_key { + modifiers.insert(Modifiers::CONTROL); + } + if self.meta_key { + modifiers.insert(Modifiers::META); + } + if self.shift_key { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers } fn held_buttons(&self) -> MouseButtonSet { - self.held_buttons + crate::input_data::decode_mouse_button_set(self.buttons) } fn trigger_button(&self) -> Option { - self.trigger_button + Some(MouseButton::from_web_code(self.button)) } } diff --git a/packages/html/src/transit.rs b/packages/html/src/transit.rs index b34a4b882..8998b5fd7 100644 --- a/packages/html/src/transit.rs +++ b/packages/html/src/transit.rs @@ -4,6 +4,7 @@ use crate::events::*; use dioxus_core::ElementId; use serde::{Deserialize, Serialize}; +#[cfg(feature = "serialize")] #[derive(Serialize, Debug, PartialEq)] pub struct HtmlEvent { pub element: ElementId, @@ -12,6 +13,7 @@ pub struct HtmlEvent { pub data: EventData, } +#[cfg(feature = "serialize")] impl<'de> Deserialize<'de> for HtmlEvent { fn deserialize(deserializer: D) -> Result where @@ -41,6 +43,7 @@ impl<'de> Deserialize<'de> for HtmlEvent { } } +#[cfg(feature = "serialize")] fn fun_name( name: &str, data: serde_value::Value, @@ -130,6 +133,7 @@ fn fun_name( Ok(data) } +#[cfg(feature = "serialize")] impl HtmlEvent { pub fn bubbles(&self) -> bool { event_bubbles(&self.name) @@ -140,47 +144,79 @@ impl HtmlEvent { #[serde(untagged)] #[non_exhaustive] pub enum EventData { - Mouse(MouseData), - Clipboard(ClipboardData), - Composition(CompositionData), - Keyboard(KeyboardData), - Focus(FocusData), - Form(FormData), - Drag(DragData), - Pointer(PointerData), - Selection(SelectionData), - Touch(TouchData), - Scroll(ScrollData), - Wheel(WheelData), - Media(MediaData), - Animation(AnimationData), - Transition(TransitionData), - Toggle(ToggleData), - Image(ImageData), + Mouse(SerializedMouseData), + Clipboard(SerializedClipboardData), + Composition(SerializedCompositionData), + Keyboard(SerializedKeyboardData), + Focus(SerializedFocusData), + Form(SerializedFormData), + Drag(SerializedDragData), + Pointer(SerializedPointerData), + Selection(SerializedSelectionData), + Touch(SerializedTouchData), + Scroll(SerializedScrollData), + Wheel(SerializedWheelData), + Media(SerializedMediaData), + Animation(SerializedAnimationData), + Transition(SerializedTransitionData), + Toggle(SerializedToggleData), + Image(SerializedImageData), Mounted, } impl EventData { pub fn into_any(self) -> Rc { match self { - EventData::Mouse(data) => Rc::new(data) as Rc, - EventData::Clipboard(data) => Rc::new(data) as Rc, - EventData::Composition(data) => Rc::new(data) as Rc, - EventData::Keyboard(data) => Rc::new(data) as Rc, - EventData::Focus(data) => Rc::new(data) as Rc, - EventData::Form(data) => Rc::new(data) as Rc, - EventData::Drag(data) => Rc::new(data) as Rc, - EventData::Pointer(data) => Rc::new(data) as Rc, - EventData::Selection(data) => Rc::new(data) as Rc, - EventData::Touch(data) => Rc::new(data) as Rc, - EventData::Scroll(data) => Rc::new(data) as Rc, - EventData::Wheel(data) => Rc::new(data) as Rc, - EventData::Media(data) => Rc::new(data) as Rc, - EventData::Animation(data) => Rc::new(data) as Rc, - EventData::Transition(data) => Rc::new(data) as Rc, - EventData::Toggle(data) => Rc::new(data) as Rc, - EventData::Image(data) => Rc::new(data) as Rc, - EventData::Mounted => Rc::new(MountedData::new(())) as Rc, + EventData::Mouse(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Clipboard(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Composition(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Keyboard(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Focus(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Form(data) => Rc::new(PlatformEventData::new(Box::new(data))) as Rc, + EventData::Drag(data) => Rc::new(PlatformEventData::new(Box::new(data))) as Rc, + EventData::Pointer(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Selection(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Touch(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Scroll(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Wheel(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Media(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Animation(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Transition(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Toggle(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Image(data) => { + Rc::new(PlatformEventData::new(Box::new(data))) as Rc + } + EventData::Mounted => { + Rc::new(PlatformEventData::new(Box::new(MountedData::new(())))) as Rc + } } } } diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index f6caf484c..3191bafd8 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -11,7 +11,9 @@ use dioxus_native_core::real_dom::NodeImmutable; use rustc_hash::{FxHashMap, FxHashSet}; use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D}; -use dioxus_html::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint, WheelDelta}; +use dioxus_html::geometry::{ + ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint, WheelDelta, +}; use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers}; use dioxus_html::input_data::{ MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons, @@ -148,13 +150,15 @@ impl InnerInputState { let coordinates = m.coordinates(); let new_mouse_data = SerializedMouseData::new( - m.client_coordinates(), - coordinates.element(), - m.page_coordinates(), - m.screen_coordinates(), - m.modifiers(), - held_buttons, m.trigger_button(), + held_buttons, + Coordinates::new( + m.screen_coordinates(), + m.client_coordinates(), + coordinates.element(), + m.page_coordinates(), + ), + m.modifiers(), ); self.mouse = Some(new_mouse_data.clone()); @@ -311,13 +315,15 @@ impl InnerInputState { .cast_unit(); SerializedMouseData::new( - mouse_data.client_coordinates(), - new_client_coordinates, - mouse_data.page_coordinates(), - mouse_data.screen_coordinates(), - mouse_data.modifiers(), - mouse_data.held_buttons(), mouse_data.trigger_button(), + mouse_data.held_buttons(), + Coordinates::new( + mouse_data.screen_coordinates(), + mouse_data.client_coordinates(), + new_client_coordinates, + mouse_data.page_coordinates(), + ), + mouse_data.modifiers(), ) } @@ -727,17 +733,19 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> { // held mouse buttons get set later by maintaining state, as crossterm does not provide them EventData::Mouse(SerializedMouseData::new( - // The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively. - // todo? - // But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway. - ClientPoint::new(x, y), - // offset x/y are set when the origin of the event is assigned to an element - ElementPoint::new(0., 0.), - PagePoint::new(x, y), - ScreenPoint::new(x, y), - modifiers, - DioxusMouseButtons::empty(), button, + DioxusMouseButtons::empty(), + Coordinates::new( + // The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively. + // todo? + // But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway. + ScreenPoint::new(x, y), + ClientPoint::new(x, y), + // offset x/y are set when the origin of the event is assigned to an element + ElementPoint::new(0., 0.), + PagePoint::new(x, y), + ), + modifiers, )) }; diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 1b7a11ca0..fb0dfef46 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -11,13 +11,13 @@ use dioxus_core::{ }; use dioxus_html::{ event_bubbles, FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter, ImageData, - MountedData, ScrollData, + MountedData, PlatformEventData, ScrollData, }; use dioxus_interpreter_js::{get_node, minimal_bindings, save_template, Channel}; use futures_channel::mpsc; use js_sys::Array; use rustc_hash::FxHashMap; -use std::{any::Any, collections::HashMap, rc::Rc}; +use std::{any::Any, collections::HashMap}; use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen, JsCast, JsValue}; use web_sys::{Document, Element, Event}; @@ -37,7 +37,7 @@ pub struct UiEvent { pub name: String, pub bubbles: bool, pub element: ElementId, - pub data: Rc, + pub data: PlatformEventData, } impl WebsysDom { @@ -96,6 +96,7 @@ impl WebsysDom { root.clone().unchecked_into(), handler.as_ref().unchecked_ref(), ); + dioxus_html::set_event_converter(Box::new(WebEventConverter)); handler.forget(); Self { document, @@ -256,7 +257,7 @@ impl WebsysDom { if let Some(element) = node.dyn_ref::() { log::info!("mounted event fired: {}", id.0); let data: MountedData = element.into(); - let data = Rc::new(data); + let data = PlatformEventData::new(Box::new(data)); let _ = self.event_channel.unbounded_send(UiEvent { name: "mounted".to_string(), bubbles: false, @@ -394,11 +395,14 @@ struct GenericWebSysEvent { // todo: some of these events are being casted to the wrong event type. // We need tests that simulate clicks/etc and make sure every event type works. -pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -> Rc { - Rc::new(GenericWebSysEvent { +pub fn virtual_event_from_websys_event( + event: web_sys::Event, + target: Element, +) -> PlatformEventData { + PlatformEventData::new(Box::new(GenericWebSysEvent { raw: event, element: target, - }) + })) } pub(crate) fn load_document() -> Document { diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index ce1fad116..016d805e3 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -53,6 +53,8 @@ // - Do the VDOM work during the idlecallback // - Do DOM work in the next requestAnimationFrame callback +use std::rc::Rc; + pub use crate::cfg::Config; #[cfg(feature = "file_engine")] pub use crate::file_engine::WebFileEngineExt; @@ -276,7 +278,12 @@ pub async fn run_with_props(root: fn(Scope) -> Element, root_prop // Dequeue all of the events from the channel in send order // todo: we should re-order these if possible while let Some(evt) = res { - dom.handle_event(evt.name.as_str(), evt.data, evt.element, evt.bubbles); + dom.handle_event( + evt.name.as_str(), + Rc::new(evt.data), + evt.element, + evt.bubbles, + ); res = rx.try_next().transpose().unwrap().ok(); } From 097eb2099cca421d00777f56ab07e909d0db6281 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 28 Aug 2023 17:16:56 -0500 Subject: [PATCH 29/56] add extension to downcast web events --- packages/html/src/events/pointer.rs | 12 + packages/web/src/dom.rs | 296 +------------------- packages/web/src/event.rs | 420 ++++++++++++++++++++++++++++ packages/web/src/lib.rs | 2 + 4 files changed, 442 insertions(+), 288 deletions(-) create mode 100644 packages/web/src/event.rs diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 5540dbe62..ec351d3dc 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -10,6 +10,18 @@ pub struct PointerData { inner: Box, } +impl PointerData { + /// Create a new PointerData + pub fn new(data: impl HasPointerData + 'static) -> Self { + Self::from(data) + } + + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + impl From for PointerData { fn from(e: E) -> Self { Self { inner: Box::new(e) } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index fb0dfef46..9799b50be 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -9,19 +9,14 @@ use dioxus_core::{ BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode, }; -use dioxus_html::{ - event_bubbles, FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter, ImageData, - MountedData, PlatformEventData, ScrollData, -}; -use dioxus_interpreter_js::{get_node, minimal_bindings, save_template, Channel}; +use dioxus_html::{event_bubbles, PlatformEventData}; +use dioxus_interpreter_js::{minimal_bindings, save_template, Channel}; use futures_channel::mpsc; -use js_sys::Array; use rustc_hash::FxHashMap; -use std::{any::Any, collections::HashMap}; -use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen, JsCast, JsValue}; +use wasm_bindgen::{closure::Closure, JsCast, JsValue}; use web_sys::{Document, Element, Event}; -use crate::Config; +use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter}; pub struct WebsysDom { document: Document, @@ -30,6 +25,7 @@ pub struct WebsysDom { templates: FxHashMap, max_template_id: u32, pub(crate) interpreter: Channel, + #[cfg(feature = "mounted")] event_channel: mpsc::UnboundedSender, } @@ -104,6 +100,7 @@ impl WebsysDom { interpreter, templates: FxHashMap::default(), max_template_id: 0, + #[cfg(feature = "mounted")] event_channel, } } @@ -253,10 +250,10 @@ impl WebsysDom { #[cfg(feature = "mounted")] for id in to_mount { - let node = get_node(id.0 as u32); + let node = dioxus_interpreter_js::get_node(id.0 as u32); if let Some(element) = node.dyn_ref::() { log::info!("mounted event fired: {}", id.0); - let data: MountedData = element.into(); + let data: dioxus_html::MountedData = element.into(); let data = PlatformEventData::new(Box::new(data)); let _ = self.event_channel.unbounded_send(UiEvent { name: "mounted".to_string(), @@ -269,283 +266,6 @@ impl WebsysDom { } } -struct WebEventConverter; - -fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent { - event - .downcast::() - .expect("event should be a GenericWebSysEvent") -} - -impl HtmlEventConverter for WebEventConverter { - fn convert_animation_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::AnimationData { - downcast_event(event).raw.clone().into() - } - - fn convert_clipboard_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ClipboardData { - downcast_event(event).raw.clone().into() - } - - fn convert_composition_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::CompositionData { - downcast_event(event).raw.clone().into() - } - - fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData { - downcast_event(event).raw.clone().into() - } - - fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData { - downcast_event(event).raw.clone().into() - } - - fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData { - let event = downcast_event(event); - FormData::new(WebFormData::new(event.element.clone(), event.raw.clone())) - } - - fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData { - let event = downcast_event(event); - let error = event.raw.type_() == "error"; - ImageData::new(WebImageEvent::new(event.raw.clone(), error)) - } - - fn convert_keyboard_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::KeyboardData { - downcast_event(event).raw.clone().into() - } - - fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData { - downcast_event(event).raw.clone().into() - } - - fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { - #[cfg(feature = "mounted")] - { - MountedData::from(downcast_event(event).element.clone()) - } - #[cfg(not(feature = "mounted"))] - { - panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled") - } - } - - fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData { - downcast_event(event).raw.clone().into() - } - - fn convert_pointer_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::PointerData { - downcast_event(event).raw.clone().into() - } - - fn convert_scroll_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ScrollData { - ScrollData::from(downcast_event(event).raw.clone()) - } - - fn convert_selection_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::SelectionData { - downcast_event(event).raw.clone().into() - } - - fn convert_toggle_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ToggleData { - downcast_event(event).raw.clone().into() - } - - fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData { - downcast_event(event).raw.clone().into() - } - - fn convert_transition_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::TransitionData { - downcast_event(event).raw.clone().into() - } - - fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData { - downcast_event(event).raw.clone().into() - } -} - -struct GenericWebSysEvent { - raw: Event, - element: Element, -} - -// todo: some of these events are being casted to the wrong event type. -// We need tests that simulate clicks/etc and make sure every event type works. -pub fn virtual_event_from_websys_event( - event: web_sys::Event, - target: Element, -) -> PlatformEventData { - PlatformEventData::new(Box::new(GenericWebSysEvent { - raw: event, - element: target, - })) -} - -pub(crate) fn load_document() -> Document { - web_sys::window() - .expect("should have access to the Window") - .document() - .expect("should have access to the Document") -} - -struct WebImageEvent { - raw: Event, - error: bool, -} - -impl WebImageEvent { - fn new(raw: Event, error: bool) -> Self { - Self { raw, error } - } -} - -impl HasImageData for WebImageEvent { - fn load_error(&self) -> bool { - self.error - } - - fn as_any(&self) -> &dyn Any { - &self.raw as &dyn Any - } -} - -struct WebFormData { - element: Element, - raw: Event, -} - -impl WebFormData { - fn new(element: Element, raw: Event) -> Self { - Self { element, raw } - } -} - -impl HasFormData for WebFormData { - fn value(&self) -> String { - let target = &self.element; - target - .dyn_ref() - .map(|input: &web_sys::HtmlInputElement| { - // todo: special case more input types - match input.type_().as_str() { - "checkbox" => { - match input.checked() { - true => "true".to_string(), - false => "false".to_string(), - } - }, - _ => { - input.value() - } - } - }) - .or_else(|| { - target - .dyn_ref() - .map(|input: &web_sys::HtmlTextAreaElement| input.value()) - }) - // select elements are NOT input events - because - why woudn't they be?? - .or_else(|| { - target - .dyn_ref() - .map(|input: &web_sys::HtmlSelectElement| input.value()) - }) - .or_else(|| { - target - .dyn_ref::() - .unwrap() - .text_content() - }) - .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener") - } - - fn values(&self) -> HashMap> { - let mut values = std::collections::HashMap::new(); - - // try to fill in form values - if let Some(form) = self.element.dyn_ref::() { - let form_data = get_form_data(form); - for value in form_data.entries().into_iter().flatten() { - if let Ok(array) = value.dyn_into::() { - if let Some(name) = array.get(0).as_string() { - if let Ok(item_values) = array.get(1).dyn_into::() { - let item_values = - item_values.iter().filter_map(|v| v.as_string()).collect(); - - values.insert(name, item_values); - } - } - } - } - } - - values - } - - fn files(&self) -> Option> { - #[cfg(not(feature = "file_engine"))] - let files = None; - #[cfg(feature = "file_engine")] - let files = self - .element - .dyn_ref() - .and_then(|input: &web_sys::HtmlInputElement| { - input.files().and_then(|files| { - crate::file_engine::WebFileEngine::new(files).map(|f| { - std::sync::Arc::new(f) as std::sync::Arc - }) - }) - }); - - files - } - - fn as_any(&self) -> &dyn Any { - &self.raw as &dyn Any - } -} - -// web-sys does not expose the keys api for form data, so we need to manually bind to it -#[wasm_bindgen(inline_js = r#" - export function get_form_data(form) { - let values = new Map(); - const formData = new FormData(form); - - for (let name of formData.keys()) { - values.set(name, formData.getAll(name)); - } - - return values; - } -"#)] -extern "C" { - fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map; -} - fn walk_event_for_id(event: &web_sys::Event) -> Option<(ElementId, web_sys::Element)> { let target = event .target() diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs new file mode 100644 index 000000000..8a4a2c204 --- /dev/null +++ b/packages/web/src/event.rs @@ -0,0 +1,420 @@ +use std::{any::Any, collections::HashMap}; + +use dioxus_html::{ + FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter, ImageData, MountedData, + PlatformEventData, ScrollData, +}; +use js_sys::Array; +use wasm_bindgen::{prelude::wasm_bindgen, JsCast}; +use web_sys::{Document, Element, Event}; + +pub(crate) struct WebEventConverter; + +fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent { + event + .downcast::() + .expect("event should be a GenericWebSysEvent") +} + +impl HtmlEventConverter for WebEventConverter { + fn convert_animation_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::AnimationData { + downcast_event(event).raw.clone().into() + } + + fn convert_clipboard_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ClipboardData { + downcast_event(event).raw.clone().into() + } + + fn convert_composition_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::CompositionData { + downcast_event(event).raw.clone().into() + } + + fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData { + downcast_event(event).raw.clone().into() + } + + fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData { + downcast_event(event).raw.clone().into() + } + + fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData { + let event = downcast_event(event); + FormData::new(WebFormData::new(event.element.clone(), event.raw.clone())) + } + + fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData { + let event = downcast_event(event); + let error = event.raw.type_() == "error"; + ImageData::new(WebImageEvent::new(event.raw.clone(), error)) + } + + fn convert_keyboard_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::KeyboardData { + downcast_event(event).raw.clone().into() + } + + fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData { + downcast_event(event).raw.clone().into() + } + + #[allow(unused_variables)] + fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { + #[cfg(feature = "mounted")] + { + MountedData::from(downcast_event(event).element.clone()) + } + #[cfg(not(feature = "mounted"))] + { + panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled") + } + } + + fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData { + downcast_event(event).raw.clone().into() + } + + fn convert_pointer_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::PointerData { + downcast_event(event).raw.clone().into() + } + + fn convert_scroll_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ScrollData { + ScrollData::from(downcast_event(event).raw.clone()) + } + + fn convert_selection_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::SelectionData { + downcast_event(event).raw.clone().into() + } + + fn convert_toggle_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ToggleData { + downcast_event(event).raw.clone().into() + } + + fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData { + downcast_event(event).raw.clone().into() + } + + fn convert_transition_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::TransitionData { + downcast_event(event).raw.clone().into() + } + + fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData { + downcast_event(event).raw.clone().into() + } +} + +/// A extension trait for web-sys events that provides a way to get the event as a web-sys event. +pub trait WebEventExt { + /// Get the event as a web-sys event. + fn web_event(&self) -> &E; +} + +impl WebEventExt for dioxus_html::AnimationData { + fn web_event(&self) -> &web_sys::AnimationEvent { + self.downcast::() + .expect("event should be a WebAnimationEvent") + } +} + +impl WebEventExt for dioxus_html::ClipboardData { + fn web_event(&self) -> &web_sys::Event { + &self + .downcast::() + .expect("event should be a web_sys::Event") + } +} + +impl WebEventExt for dioxus_html::CompositionData { + fn web_event(&self) -> &web_sys::CompositionEvent { + self.downcast::() + .expect("event should be a WebCompositionEvent") + } +} + +impl WebEventExt for dioxus_html::DragData { + fn web_event(&self) -> &web_sys::MouseEvent { + self.downcast::() + .expect("event should be a WebMouseEvent") + } +} + +impl WebEventExt for dioxus_html::FocusData { + fn web_event(&self) -> &web_sys::FocusEvent { + self.downcast::() + .expect("event should be a WebFocusEvent") + } +} + +impl WebEventExt for dioxus_html::FormData { + fn web_event(&self) -> &web_sys::Event { + self.downcast::() + .expect("event should be a WebFormData") + } +} + +impl WebEventExt for dioxus_html::ImageData { + fn web_event(&self) -> &WebImageEvent { + self.downcast::() + .expect("event should be a WebImageEvent") + } +} + +impl WebEventExt for dioxus_html::KeyboardData { + fn web_event(&self) -> &web_sys::KeyboardEvent { + self.downcast::() + .expect("event should be a WebKeyboardEvent") + } +} + +impl WebEventExt for dioxus_html::MediaData { + fn web_event(&self) -> &web_sys::Event { + self.downcast::() + .expect("event should be a WebMediaEvent") + } +} + +impl WebEventExt for MountedData { + fn web_event(&self) -> &MountedData { + self.downcast::() + .expect("event should be a WebMountedEvent") + } +} + +impl WebEventExt for dioxus_html::MouseData { + fn web_event(&self) -> &web_sys::MouseEvent { + self.downcast::() + .expect("event should be a WebMouseEvent") + } +} + +impl WebEventExt for dioxus_html::PointerData { + fn web_event(&self) -> &web_sys::PointerEvent { + self.downcast::() + .expect("event should be a WebPointerEvent") + } +} + +impl WebEventExt for ScrollData { + fn web_event(&self) -> &web_sys::Event { + self.downcast::() + .expect("event should be a WebScrollEvent") + } +} + +impl WebEventExt for dioxus_html::SelectionData { + fn web_event(&self) -> &web_sys::Event { + self.downcast::() + .expect("event should be a WebSelectionEvent") + } +} + +impl WebEventExt for dioxus_html::ToggleData { + fn web_event(&self) -> &web_sys::Event { + self.downcast::() + .expect("event should be a WebToggleEvent") + } +} + +impl WebEventExt for dioxus_html::TouchData { + fn web_event(&self) -> &web_sys::TouchEvent { + self.downcast::() + .expect("event should be a WebTouchEvent") + } +} + +impl WebEventExt for dioxus_html::TransitionData { + fn web_event(&self) -> &web_sys::TransitionEvent { + self.downcast::() + .expect("event should be a WebTransitionEvent") + } +} + +impl WebEventExt for dioxus_html::WheelData { + fn web_event(&self) -> &web_sys::WheelEvent { + self.downcast::() + .expect("event should be a WebWheelEvent") + } +} + +struct GenericWebSysEvent { + raw: Event, + element: Element, +} + +// todo: some of these events are being casted to the wrong event type. +// We need tests that simulate clicks/etc and make sure every event type works. +pub(crate) fn virtual_event_from_websys_event( + event: web_sys::Event, + target: Element, +) -> PlatformEventData { + PlatformEventData::new(Box::new(GenericWebSysEvent { + raw: event, + element: target, + })) +} + +pub(crate) fn load_document() -> Document { + web_sys::window() + .expect("should have access to the Window") + .document() + .expect("should have access to the Document") +} + +struct WebImageEvent { + raw: Event, + error: bool, +} + +impl WebImageEvent { + fn new(raw: Event, error: bool) -> Self { + Self { raw, error } + } +} + +impl HasImageData for WebImageEvent { + fn load_error(&self) -> bool { + self.error + } + + fn as_any(&self) -> &dyn Any { + &self.raw as &dyn Any + } +} + +struct WebFormData { + element: Element, + raw: Event, +} + +impl WebFormData { + fn new(element: Element, raw: Event) -> Self { + Self { element, raw } + } +} + +impl HasFormData for WebFormData { + fn value(&self) -> String { + let target = &self.element; + target + .dyn_ref() + .map(|input: &web_sys::HtmlInputElement| { + // todo: special case more input types + match input.type_().as_str() { + "checkbox" => { + match input.checked() { + true => "true".to_string(), + false => "false".to_string(), + } + }, + _ => { + input.value() + } + } + }) + .or_else(|| { + target + .dyn_ref() + .map(|input: &web_sys::HtmlTextAreaElement| input.value()) + }) + // select elements are NOT input events - because - why woudn't they be?? + .or_else(|| { + target + .dyn_ref() + .map(|input: &web_sys::HtmlSelectElement| input.value()) + }) + .or_else(|| { + target + .dyn_ref::() + .unwrap() + .text_content() + }) + .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener") + } + + fn values(&self) -> HashMap> { + let mut values = std::collections::HashMap::new(); + + // try to fill in form values + if let Some(form) = self.element.dyn_ref::() { + let form_data = get_form_data(form); + for value in form_data.entries().into_iter().flatten() { + if let Ok(array) = value.dyn_into::() { + if let Some(name) = array.get(0).as_string() { + if let Ok(item_values) = array.get(1).dyn_into::() { + let item_values = + item_values.iter().filter_map(|v| v.as_string()).collect(); + + values.insert(name, item_values); + } + } + } + } + } + + values + } + + fn files(&self) -> Option> { + #[cfg(not(feature = "file_engine"))] + let files = None; + #[cfg(feature = "file_engine")] + let files = self + .element + .dyn_ref() + .and_then(|input: &web_sys::HtmlInputElement| { + input.files().and_then(|files| { + crate::file_engine::WebFileEngine::new(files).map(|f| { + std::sync::Arc::new(f) as std::sync::Arc + }) + }) + }); + + files + } + + fn as_any(&self) -> &dyn Any { + &self.raw as &dyn Any + } +} + +// web-sys does not expose the keys api for form data, so we need to manually bind to it +#[wasm_bindgen(inline_js = r#" + export function get_form_data(form) { + let values = new Map(); + const formData = new FormData(form); + + for (let name of formData.keys()) { + values.set(name, formData.getAll(name)); + } + + return values; + } +"#)] +extern "C" { + fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map; +} diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 016d805e3..002bdb916 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -69,6 +69,8 @@ mod cfg; mod dom; #[cfg(feature = "eval")] mod eval; +mod event; +pub use event::*; #[cfg(feature = "file_engine")] mod file_engine; #[cfg(all(feature = "hot_reload", debug_assertions))] From 177db238b9e472311c198230cd2678db7431a104 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 28 Aug 2023 17:55:23 -0500 Subject: [PATCH 30/56] add some inline hints --- packages/web/src/event.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index 8a4a2c204..8767b6a35 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -10,6 +10,7 @@ use web_sys::{Document, Element, Event}; pub(crate) struct WebEventConverter; +#[inline(always)] fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent { event .downcast::() @@ -17,6 +18,7 @@ fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent } impl HtmlEventConverter for WebEventConverter { + #[inline(always)] fn convert_animation_data( &self, event: &dioxus_html::PlatformEventData, @@ -24,6 +26,7 @@ impl HtmlEventConverter for WebEventConverter { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_clipboard_data( &self, event: &dioxus_html::PlatformEventData, @@ -31,6 +34,7 @@ impl HtmlEventConverter for WebEventConverter { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_composition_data( &self, event: &dioxus_html::PlatformEventData, @@ -38,25 +42,30 @@ impl HtmlEventConverter for WebEventConverter { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData { let event = downcast_event(event); FormData::new(WebFormData::new(event.element.clone(), event.raw.clone())) } + #[inline(always)] fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData { let event = downcast_event(event); let error = event.raw.type_() == "error"; ImageData::new(WebImageEvent::new(event.raw.clone(), error)) } + #[inline(always)] fn convert_keyboard_data( &self, event: &dioxus_html::PlatformEventData, @@ -64,11 +73,13 @@ impl HtmlEventConverter for WebEventConverter { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData { downcast_event(event).raw.clone().into() } #[allow(unused_variables)] + #[inline(always)] fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { #[cfg(feature = "mounted")] { @@ -80,10 +91,12 @@ impl HtmlEventConverter for WebEventConverter { } } + #[inline(always)] fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_pointer_data( &self, event: &dioxus_html::PlatformEventData, @@ -91,6 +104,7 @@ impl HtmlEventConverter for WebEventConverter { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_scroll_data( &self, event: &dioxus_html::PlatformEventData, @@ -98,6 +112,7 @@ impl HtmlEventConverter for WebEventConverter { ScrollData::from(downcast_event(event).raw.clone()) } + #[inline(always)] fn convert_selection_data( &self, event: &dioxus_html::PlatformEventData, @@ -105,6 +120,7 @@ impl HtmlEventConverter for WebEventConverter { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_toggle_data( &self, event: &dioxus_html::PlatformEventData, @@ -112,10 +128,12 @@ impl HtmlEventConverter for WebEventConverter { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_transition_data( &self, event: &dioxus_html::PlatformEventData, @@ -123,6 +141,7 @@ impl HtmlEventConverter for WebEventConverter { downcast_event(event).raw.clone().into() } + #[inline(always)] fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData { downcast_event(event).raw.clone().into() } From 4879f65a3ecd263185eda9f9c8a68b126d670c0d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 31 Aug 2023 16:25:26 -0500 Subject: [PATCH 31/56] fix tui events --- packages/dioxus-tui/src/element.rs | 4 ++-- packages/rink/src/hooks.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/dioxus-tui/src/element.rs b/packages/dioxus-tui/src/element.rs index eeb4d5b86..ba496fae1 100644 --- a/packages/dioxus-tui/src/element.rs +++ b/packages/dioxus-tui/src/element.rs @@ -82,8 +82,8 @@ impl RenderedElementBacking for TuiElement { }) } - fn get_raw_element(&self) -> dioxus_html::MountedResult<&dyn std::any::Any> { - Ok(self) + fn as_any(&self) -> &dyn std::any::Any { + self } } diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index 3191bafd8..646ae52df 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -177,10 +177,10 @@ impl InnerInputState { if is_repeating { *k = SerializedKeyboardData::new( - is_repeating, - k.code(), k.key(), + k.code(), k.location(), + is_repeating, k.modifiers(), ); } @@ -777,10 +777,10 @@ fn translate_key_event(event: crossterm::event::KeyEvent) -> Option { let modifiers = modifiers_from_crossterm_modifiers(event.modifiers); Some(EventData::Keyboard(SerializedKeyboardData::new( - false, - code, key, + code, Location::Standard, + false, modifiers, ))) } From f7451c02e867ce87ba344b0c7440f9c2e563ec64 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 31 Aug 2023 16:34:04 -0500 Subject: [PATCH 32/56] port liveview events --- packages/liveview/src/element.rs | 1 + packages/liveview/src/events.rs | 149 +++++++++++++++++++++++++++++++ packages/liveview/src/lib.rs | 1 + packages/liveview/src/pool.rs | 11 ++- 4 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 packages/liveview/src/events.rs diff --git a/packages/liveview/src/element.rs b/packages/liveview/src/element.rs index d813a47d5..eef0fa48d 100644 --- a/packages/liveview/src/element.rs +++ b/packages/liveview/src/element.rs @@ -4,6 +4,7 @@ use dioxus_html::{geometry::euclid::Rect, MountedResult, RenderedElementBacking} use crate::query::QueryEngine; /// A mounted element passed to onmounted events +#[derive(Clone)] pub struct LiveviewElement { id: ElementId, query: QueryEngine, diff --git a/packages/liveview/src/events.rs b/packages/liveview/src/events.rs new file mode 100644 index 000000000..b764bf7cc --- /dev/null +++ b/packages/liveview/src/events.rs @@ -0,0 +1,149 @@ +//! Convert a serialized event to an event trigger + +use dioxus_html::*; + +use crate::element::LiveviewElement; + +pub(crate) struct SerializedHtmlEventConverter; + +impl HtmlEventConverter for SerializedHtmlEventConverter { + fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_drag_data(&self, event: &PlatformEventData) -> DragData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_form_data(&self, event: &PlatformEventData) -> FormData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_image_data(&self, event: &PlatformEventData) -> ImageData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_media_data(&self, event: &PlatformEventData) -> MediaData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData { + event.downcast::().cloned().unwrap().into() + } + + fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } +} diff --git a/packages/liveview/src/lib.rs b/packages/liveview/src/lib.rs index 92cce63e6..306af276a 100644 --- a/packages/liveview/src/lib.rs +++ b/packages/liveview/src/lib.rs @@ -24,6 +24,7 @@ mod query; use futures_util::{SinkExt, StreamExt}; pub use pool::*; mod eval; +mod events; pub trait WebsocketTx: SinkExt {} impl WebsocketTx for T where T: SinkExt {} diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index 2fd06cec6..ed0649c00 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -1,11 +1,12 @@ use crate::{ element::LiveviewElement, eval::init_eval, + events::SerializedHtmlEventConverter, query::{QueryEngine, QueryResult}, LiveViewError, }; use dioxus_core::{prelude::*, Mutations}; -use dioxus_html::{EventData, HtmlEvent, MountedData}; +use dioxus_html::{EventData, HtmlEvent, MountedData, PlatformEventData}; use futures_util::{pin_mut, SinkExt, StreamExt}; use serde::Serialize; use std::{rc::Rc, time::Duration}; @@ -24,6 +25,9 @@ impl Default for LiveViewPool { impl LiveViewPool { pub fn new() -> Self { + // Set the event converter + dioxus_html::set_event_converter(Box::new(SerializedHtmlEventConverter)); + LiveViewPool { pool: LocalPoolHandle::new(16), } @@ -171,12 +175,11 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li let element = LiveviewElement::new(evt.element, query_engine.clone()); vdom.handle_event( &evt.name, - Rc::new(MountedData::new(element)), + Rc::new(PlatformEventData::new(Box::new(MountedData::new(element)))), evt.element, evt.bubbles, ); - } - else{ + } else { vdom.handle_event( &evt.name, evt.data.into_any(), From 3eafb05d92ac06f3012528816a14900f1b327384 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 31 Aug 2023 16:34:27 -0500 Subject: [PATCH 33/56] fix features on rink html dependency --- packages/rink/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rink/Cargo.toml b/packages/rink/Cargo.toml index ba3d549f8..69d9a5c7b 100644 --- a/packages/rink/Cargo.toml +++ b/packages/rink/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["dom", "ui", "gui", "react", "terminal"] license = "MIT OR Apache-2.0" [dependencies] -dioxus-html = { workspace = true } +dioxus-html = { workspace = true, features = ["serialize", "mounted"] } dioxus-native-core = { workspace = true, features = ["layout-attributes"] } dioxus-native-core-macro = { workspace = true } From 3c0159370360c93f6e713b80f255f417f88c9a71 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 31 Aug 2023 16:38:14 -0500 Subject: [PATCH 34/56] document HtmlEventConverter --- packages/html/src/events.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index a24294378..ae2842d28 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -63,24 +63,43 @@ impl PlatformEventData { } } +/// A converter between a platform specific event and a general event. All code in a renderer that has a large binary size should be placed in this trait. Each of these functions should be snipped in high levels of optimization. pub trait HtmlEventConverter: Send + Sync { + /// Convert a general event to an animation data event fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData; + /// Convert a general event to a clipboard data event fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData; + /// Convert a general event to a composition data event fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData; + /// Convert a general event to a drag data event fn convert_drag_data(&self, event: &PlatformEventData) -> DragData; + /// Convert a general event to a focus data event fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData; + /// Convert a general event to a form data event fn convert_form_data(&self, event: &PlatformEventData) -> FormData; + /// Convert a general event to an image data event fn convert_image_data(&self, event: &PlatformEventData) -> ImageData; + /// Convert a general event to a keyboard data event fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData; + /// Convert a general event to a media data event fn convert_media_data(&self, event: &PlatformEventData) -> MediaData; + /// Convert a general event to a mounted data event fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData; + /// Convert a general event to a mouse data event fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData; + /// Convert a general event to a pointer data event fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData; + /// Convert a general event to a scroll data event fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData; + /// Convert a general event to a selection data event fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData; + /// Convert a general event to a toggle data event fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData; + /// Convert a general event to a touch data event fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData; + /// Convert a general event to a transition data event fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData; + /// Convert a general event to a wheel data event fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData; } From bf90b7584e127b058d511e960918142f25fbc1d4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 31 Aug 2023 16:42:33 -0500 Subject: [PATCH 35/56] add some inline hints --- packages/html/src/events.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index ae2842d28..2d27f583c 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -28,10 +28,12 @@ macro_rules! impl_event { static EVENT_CONVERTER: RwLock>> = RwLock::new(None); +#[inline] pub fn set_event_converter(converter: Box) { *EVENT_CONVERTER.write().unwrap() = Some(converter); } +#[inline] pub(crate) fn with_event_converter(f: F) -> R where F: FnOnce(&dyn HtmlEventConverter) -> R, From 364654e1fd3e90afa68f5ccd4128c3a6d9858742 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 31 Aug 2023 16:43:24 -0500 Subject: [PATCH 36/56] fix clippy with events --- packages/html/src/events.rs | 108 ++++++++++++++++++------------------ packages/web/src/event.rs | 2 +- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 2d27f583c..de685b3cc 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -105,111 +105,111 @@ pub trait HtmlEventConverter: Send + Sync { fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData; } -impl Into for &PlatformEventData { - fn into(self) -> AnimationData { - with_event_converter(|c| c.convert_animation_data(self)) +impl From<&PlatformEventData> for AnimationData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_animation_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> ClipboardData { - with_event_converter(|c| c.convert_clipboard_data(self)) +impl From<&PlatformEventData> for ClipboardData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_clipboard_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> CompositionData { - with_event_converter(|c| c.convert_composition_data(self)) +impl From<&PlatformEventData> for CompositionData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_composition_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> DragData { - with_event_converter(|c| c.convert_drag_data(self)) +impl From<&PlatformEventData> for DragData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_drag_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> FocusData { - with_event_converter(|c| c.convert_focus_data(self)) +impl From<&PlatformEventData> for FocusData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_focus_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> FormData { - with_event_converter(|c| c.convert_form_data(self)) +impl From<&PlatformEventData> for FormData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_form_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> ImageData { - with_event_converter(|c| c.convert_image_data(self)) +impl From<&PlatformEventData> for ImageData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_image_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> KeyboardData { - with_event_converter(|c| c.convert_keyboard_data(self)) +impl From<&PlatformEventData> for KeyboardData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_keyboard_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> MediaData { - with_event_converter(|c| c.convert_media_data(self)) +impl From<&PlatformEventData> for MediaData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_media_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> MountedData { - with_event_converter(|c| c.convert_mounted_data(self)) +impl From<&PlatformEventData> for MountedData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_mounted_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> MouseData { - with_event_converter(|c| c.convert_mouse_data(self)) +impl From<&PlatformEventData> for MouseData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_mouse_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> PointerData { - with_event_converter(|c| c.convert_pointer_data(self)) +impl From<&PlatformEventData> for PointerData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_pointer_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> ScrollData { - with_event_converter(|c| c.convert_scroll_data(self)) +impl From<&PlatformEventData> for ScrollData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_scroll_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> SelectionData { - with_event_converter(|c| c.convert_selection_data(self)) +impl From<&PlatformEventData> for SelectionData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_selection_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> ToggleData { - with_event_converter(|c| c.convert_toggle_data(self)) +impl From<&PlatformEventData> for ToggleData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_toggle_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> TouchData { - with_event_converter(|c| c.convert_touch_data(self)) +impl From<&PlatformEventData> for TouchData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_touch_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> TransitionData { - with_event_converter(|c| c.convert_transition_data(self)) +impl From<&PlatformEventData> for TransitionData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_transition_data(val)) } } -impl Into for &PlatformEventData { - fn into(self) -> WheelData { - with_event_converter(|c| c.convert_wheel_data(self)) +impl From<&PlatformEventData> for WheelData { + fn from(val: &PlatformEventData) -> Self { + with_event_converter(|c| c.convert_wheel_data(val)) } } diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index 8767b6a35..c1e28c354 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -162,7 +162,7 @@ impl WebEventExt for dioxus_html::AnimationData { impl WebEventExt for dioxus_html::ClipboardData { fn web_event(&self) -> &web_sys::Event { - &self + self .downcast::() .expect("event should be a web_sys::Event") } From dee92b83be8599161d1982ceaa3b2c35d2cba2e8 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 1 Sep 2023 15:06:30 -0500 Subject: [PATCH 37/56] remove the serde feature flag from enumset --- packages/html/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 7e061855e..5b665d14b 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -56,7 +56,6 @@ serialize = [ "euclid/serde", "keyboard-types/serde", "dioxus-core/serialize", - "enumset/serde", ] mounted = [ "web-sys/Element", From df222c121a22f1ab30846f065c9c46425aeb424f Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 1 Sep 2023 15:38:55 -0500 Subject: [PATCH 38/56] port examples to new events --- docs/guide/examples/hello_world_tui_no_ctrl_c.rs | 4 ++-- docs/guide/examples/input_controlled.rs | 2 +- docs/guide/examples/meme_editor.rs | 2 +- docs/guide/examples/meme_editor_dark_mode.rs | 4 ++-- docs/guide/examples/rendering_lists.rs | 4 ++-- examples/compose.rs | 2 +- examples/counter.rs | 2 +- examples/crm.rs | 6 +++--- examples/file_upload.rs | 4 ++-- examples/form.rs | 4 ++-- examples/login_form.rs | 4 ++-- examples/shared_state.rs | 2 +- examples/textarea.rs | 2 +- examples/todomvc.rs | 6 +++--- examples/window_zoom.rs | 2 +- examples/xss_safety.rs | 2 +- packages/cli/Cargo.toml | 2 +- packages/core/tests/lifecycle.rs | 9 ++++++++- packages/core/tests/miri_full_app.rs | 9 ++++++++- packages/core/tests/suspense.rs | 2 +- packages/desktop/headless_tests/events.rs | 6 +++--- packages/dioxus-tui/examples/widgets.rs | 10 +++++----- packages/html/src/lib.rs | 2 +- packages/html/src/transit.rs | 2 +- packages/router/examples/simple_routes.rs | 2 +- 25 files changed, 55 insertions(+), 41 deletions(-) diff --git a/docs/guide/examples/hello_world_tui_no_ctrl_c.rs b/docs/guide/examples/hello_world_tui_no_ctrl_c.rs index 6afa21e6c..62131f0a3 100644 --- a/docs/guide/examples/hello_world_tui_no_ctrl_c.rs +++ b/docs/guide/examples/hello_world_tui_no_ctrl_c.rs @@ -1,7 +1,7 @@ // todo remove deprecated #![allow(non_snake_case, deprecated)] -use dioxus::events::{KeyCode, KeyboardEvent}; +use dioxus::events::KeyboardEvent; use dioxus::prelude::*; use dioxus_tui::TuiContext; @@ -26,7 +26,7 @@ fn App(cx: Scope) -> Element { background_color: "red", justify_content: "center", align_items: "center", - onkeydown: move |k: KeyboardEvent| if let KeyCode::Q = k.key_code { + onkeydown: move |k: KeyboardEvent| if let Code::KeyQ = k.code() { tui_ctx.quit(); }, diff --git a/docs/guide/examples/input_controlled.rs b/docs/guide/examples/input_controlled.rs index bf92072a3..e9f33ef53 100644 --- a/docs/guide/examples/input_controlled.rs +++ b/docs/guide/examples/input_controlled.rs @@ -14,7 +14,7 @@ fn App(cx: Scope) -> Element { // we tell the component what to render value: "{name}", // and what to do when the value changes - oninput: move |evt| name.set(evt.value.clone()), + oninput: move |evt| name.set(evt.value()), } }) } diff --git a/docs/guide/examples/meme_editor.rs b/docs/guide/examples/meme_editor.rs index c5a473da9..1bf4df0b4 100644 --- a/docs/guide/examples/meme_editor.rs +++ b/docs/guide/examples/meme_editor.rs @@ -28,7 +28,7 @@ fn MemeEditor(cx: Scope) -> Element { }, CaptionEditor { caption: caption, - on_input: move |event: FormEvent| {caption.set(event.value.clone());}, + on_input: move |event: FormEvent| {caption.set(event.value());}, }, } }) diff --git a/docs/guide/examples/meme_editor_dark_mode.rs b/docs/guide/examples/meme_editor_dark_mode.rs index e62a64eb7..893bc2cc8 100644 --- a/docs/guide/examples/meme_editor_dark_mode.rs +++ b/docs/guide/examples/meme_editor_dark_mode.rs @@ -62,7 +62,7 @@ pub fn DarkModeToggle(cx: Scope) -> Element { input { r#type: "checkbox", oninput: move |event| { - let is_enabled = event.value == "true"; + let is_enabled = event.value() == "true"; dark_mode.write().0 = is_enabled; }, }, @@ -97,7 +97,7 @@ fn MemeEditor(cx: Scope) -> Element { }, CaptionEditor { caption: caption, - on_input: move |event: FormEvent| {caption.set(event.value.clone());}, + on_input: move |event: FormEvent| {caption.set(event.value().clone());}, }, } }) diff --git a/docs/guide/examples/rendering_lists.rs b/docs/guide/examples/rendering_lists.rs index b95e712dc..05df11eb7 100644 --- a/docs/guide/examples/rendering_lists.rs +++ b/docs/guide/examples/rendering_lists.rs @@ -40,7 +40,7 @@ cx.render(rsx!( }, input { value: "{comment_field}", - oninput: |event| comment_field.set(event.value.clone()), + oninput: |event| comment_field.set(event.value()), } input { r#type: "submit", @@ -71,7 +71,7 @@ cx.render(rsx!( }, input { value: "{comment_field}", - oninput: |event| comment_field.set(event.value.clone()), + oninput: |event| comment_field.set(event.value()), } input { r#type: "submit", diff --git a/examples/compose.rs b/examples/compose.rs index 3ee6d36ae..5f5b9ebc1 100644 --- a/examples/compose.rs +++ b/examples/compose.rs @@ -73,7 +73,7 @@ fn compose(cx: Scope) -> Element { input { oninput: move |e| { - user_input.set(e.value.clone()); + user_input.set(e.value()); }, value: "{user_input}" } diff --git a/examples/counter.rs b/examples/counter.rs index f51083a95..1de77dc38 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -22,7 +22,7 @@ fn app(cx: Scope) -> Element { input { value: "{counter}", oninput: move |e| { - if let Ok(value) = e.value.parse::() { + if let Ok(value) = e.value().parse::() { counters.make_mut()[i] = value; } } diff --git a/examples/crm.rs b/examples/crm.rs index f3da8ab7a..7ab0c8ad7 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -119,7 +119,7 @@ fn ClientAdd(cx: Scope) -> Element { placeholder: "First Name…", required: "", value: "{first_name}", - oninput: move |e| first_name.set(e.value.clone()) + oninput: move |e| first_name.set(e.value()) } } @@ -135,7 +135,7 @@ fn ClientAdd(cx: Scope) -> Element { placeholder: "Last Name…", required: "", value: "{last_name}", - oninput: move |e| last_name.set(e.value.clone()) + oninput: move |e| last_name.set(e.value()) } } @@ -149,7 +149,7 @@ fn ClientAdd(cx: Scope) -> Element { id: "description", placeholder: "Description…", value: "{description}", - oninput: move |e| description.set(e.value.clone()) + oninput: move |e| description.set(e.value()) } } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index c7358c685..882285ddf 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -16,7 +16,7 @@ fn App(cx: Scope) -> Element { r#type: "checkbox", checked: "{enable_directory_upload}", oninput: move |evt| { - enable_directory_upload.set(evt.value.parse().unwrap()); + enable_directory_upload.set(evt.value().parse().unwrap()); }, }, "Enable directory upload" @@ -30,7 +30,7 @@ fn App(cx: Scope) -> Element { onchange: |evt| { to_owned![files_uploaded]; async move { - if let Some(file_engine) = &evt.files { + if let Some(file_engine) = &evt.files() { let files = file_engine.files(); for file_name in files { sleep(std::time::Duration::from_secs(1)).await; diff --git a/examples/form.rs b/examples/form.rs index a7de142f3..897ffc710 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -14,8 +14,8 @@ fn app(cx: Scope) -> Element { div { h1 { "Form" } form { - onsubmit: move |ev| println!("Submitted {:?}", ev.values), - oninput: move |ev| println!("Input {:?}", ev.values), + onsubmit: move |ev| println!("Submitted {:?}", ev.values()), + oninput: move |ev| println!("Input {:?}", ev.values()), input { r#type: "text", name: "username" } input { r#type: "text", name: "full-name" } input { r#type: "password", name: "password" } diff --git a/examples/login_form.rs b/examples/login_form.rs index 5ee47e4b5..d7371dc50 100644 --- a/examples/login_form.rs +++ b/examples/login_form.rs @@ -13,8 +13,8 @@ fn app(cx: Scope) -> Element { let resp = reqwest::Client::new() .post("http://localhost:8080/login") .form(&[ - ("username", &evt.values["username"]), - ("password", &evt.values["password"]), + ("username", &evt.values()["username"]), + ("password", &evt.values()["password"]), ]) .send() .await; diff --git a/examples/shared_state.rs b/examples/shared_state.rs index d129f75d1..71345dfcb 100644 --- a/examples/shared_state.rs +++ b/examples/shared_state.rs @@ -65,7 +65,7 @@ fn DataEditor(cx: Scope, id: usize) -> Element { fn DataView(cx: Scope, id: usize) -> Element { let cool_data = use_shared_state::(cx).unwrap(); - let oninput = |e: FormEvent| cool_data.write().set(*id, e.value.clone()); + let oninput = |e: FormEvent| cool_data.write().set(*id, e.value()); let cool_data = cool_data.read(); let my_data = &cool_data.view(id).unwrap(); diff --git a/examples/textarea.rs b/examples/textarea.rs index df684aa85..62afc45bb 100644 --- a/examples/textarea.rs +++ b/examples/textarea.rs @@ -17,7 +17,7 @@ fn app(cx: Scope) -> Element { rows: "10", cols: "80", value: "{model}", - oninput: move |e| model.set(e.value.clone()), + oninput: move |e| model.set(e.value().clone()), } }) } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 4b25507a5..d5dff22e8 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -66,7 +66,7 @@ pub fn app(cx: Scope<()>) -> Element { value: "{draft}", autofocus: "true", oninput: move |evt| { - draft.set(evt.value.clone()); + draft.set(evt.value()); }, onkeydown: move |evt| { if evt.key() == Key::Enter && !draft.is_empty() { @@ -176,7 +176,7 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element { id: "cbg-{todo.id}", checked: "{todo.checked}", oninput: move |evt| { - cx.props.todos.make_mut()[&cx.props.id].checked = evt.value.parse().unwrap(); + cx.props.todos.make_mut()[&cx.props.id].checked = evt.value().parse().unwrap(); } } label { @@ -195,7 +195,7 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element { input { class: "edit", value: "{todo.contents}", - oninput: move |evt| cx.props.todos.make_mut()[&cx.props.id].contents = evt.value.clone(), + oninput: move |evt| cx.props.todos.make_mut()[&cx.props.id].contents = evt.value(), autofocus: "true", onfocusout: move |_| is_editing.set(false), onkeydown: move |evt| { diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index e45d71be2..3c4edbb90 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -14,7 +14,7 @@ fn app(cx: Scope) -> Element { r#type: "number", value: "{level}", oninput: |e| { - if let Ok(new_zoom) = e.value.parse::() { + if let Ok(new_zoom) = e.value().parse::() { level.set(new_zoom); window.webview.zoom(new_zoom); } diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index 5272d661b..73da2ff14 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -20,7 +20,7 @@ fn app(cx: Scope) -> Element { input { value: "{contents}", r#type: "text", - oninput: move |e| contents.set(e.value.clone()), + oninput: move |e| contents.set(e.value()), } } }) diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index a37798eee..8299a88a6 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -78,7 +78,7 @@ toml_edit = "0.19.11" # dioxus-rsx = "0.0.1" # bundling -tauri-bundler = { version = "1.2", features = ["native-tls-vendored"] } +tauri-bundler = { version = "=1.2", features = ["native-tls-vendored"] } tauri-utils = "1.3" dioxus-autofmt = { workspace = true } diff --git a/packages/core/tests/lifecycle.rs b/packages/core/tests/lifecycle.rs index 04f6bc3cf..37b966712 100644 --- a/packages/core/tests/lifecycle.rs +++ b/packages/core/tests/lifecycle.rs @@ -56,7 +56,14 @@ fn events_generate() { let mut dom = VirtualDom::new(app); _ = dom.rebuild(); - dom.handle_event("click", Rc::new(MouseData::default()), ElementId(1), true); + dom.handle_event( + "click", + Rc::new(PlatformEventData::new(Box::new(MouseData::new( + SerializedMouseData::default(), + )))), + ElementId(1), + true, + ); dom.mark_dirty(ScopeId(0)); let edits = dom.render_immediate(); diff --git a/packages/core/tests/miri_full_app.rs b/packages/core/tests/miri_full_app.rs index c1c6d52cc..93277b181 100644 --- a/packages/core/tests/miri_full_app.rs +++ b/packages/core/tests/miri_full_app.rs @@ -9,7 +9,14 @@ fn miri_rollover() { _ = dom.rebuild(); for _ in 0..3 { - dom.handle_event("click", Rc::new(MouseData::default()), ElementId(2), true); + dom.handle_event( + "click", + Rc::new(PlatformEventData::new(Box::new(MouseData::new( + SerializedMouseData::default(), + )))), + ElementId(2), + true, + ); dom.process_events(); _ = dom.render_immediate(); } diff --git a/packages/core/tests/suspense.rs b/packages/core/tests/suspense.rs index 7b2516447..abce7f8a6 100644 --- a/packages/core/tests/suspense.rs +++ b/packages/core/tests/suspense.rs @@ -36,7 +36,7 @@ fn suspended_child(cx: Scope) -> Element { cx.spawn(async move { val += 1; }); - return cx.suspend()?; + cx.suspend()?; } render!("child") diff --git a/packages/desktop/headless_tests/events.rs b/packages/desktop/headless_tests/events.rs index ac62cd37b..6765a8199 100644 --- a/packages/desktop/headless_tests/events.rs +++ b/packages/desktop/headless_tests/events.rs @@ -304,7 +304,7 @@ fn app(cx: Scope) -> Element { assert!(event.data.modifiers().is_empty()); assert_eq!(event.data.key().to_string(), "a"); assert_eq!(event.data.code().to_string(), "KeyA"); - assert_eq!(event.data.location, 0); + assert_eq!(event.data.location(), Location::Standard); assert!(event.data.is_auto_repeating()); recieved_events.modify(|x| *x + 1) @@ -317,7 +317,7 @@ fn app(cx: Scope) -> Element { assert!(event.data.modifiers().is_empty()); assert_eq!(event.data.key().to_string(), "a"); assert_eq!(event.data.code().to_string(), "KeyA"); - assert_eq!(event.data.location, 0); + assert_eq!(event.data.location(), Location::Standard); assert!(!event.data.is_auto_repeating()); recieved_events.modify(|x| *x + 1) @@ -330,7 +330,7 @@ fn app(cx: Scope) -> Element { assert!(event.data.modifiers().is_empty()); assert_eq!(event.data.key().to_string(), "a"); assert_eq!(event.data.code().to_string(), "KeyA"); - assert_eq!(event.data.location, 0); + assert_eq!(event.data.location(), Location::Standard); assert!(!event.data.is_auto_repeating()); recieved_events.modify(|x| *x + 1) diff --git a/packages/dioxus-tui/examples/widgets.rs b/packages/dioxus-tui/examples/widgets.rs index b5df35388..2d0a5bf72 100644 --- a/packages/dioxus-tui/examples/widgets.rs +++ b/packages/dioxus-tui/examples/widgets.rs @@ -18,7 +18,7 @@ fn app(cx: Scope) -> Element { justify_content: "center", input { - oninput: |data| if &data.value == "good"{ + oninput: |data| if &data.value()== "good"{ bg_green.set(true); } else{ bg_green.set(false); @@ -30,7 +30,7 @@ fn app(cx: Scope) -> Element { checked: "true", } input { - oninput: |data| if &data.value == "hello world"{ + oninput: |data| if &data.value()== "hello world"{ bg_green.set(true); } else{ bg_green.set(false); @@ -41,7 +41,7 @@ fn app(cx: Scope) -> Element { } input { oninput: |data| { - if (data.value.parse::().unwrap() - 40.0).abs() < 5.0 { + if (data.value().parse::().unwrap() - 40.0).abs() < 5.0 { bg_green.set(true); } else{ bg_green.set(false); @@ -55,7 +55,7 @@ fn app(cx: Scope) -> Element { } input { oninput: |data| { - if data.value == "10"{ + if data.value()== "10"{ bg_green.set(true); } else{ bg_green.set(false); @@ -68,7 +68,7 @@ fn app(cx: Scope) -> Element { } input { oninput: |data| { - if data.value == "hello world"{ + if data.value()== "hello world"{ bg_green.set(true); } else{ bg_green.set(false); diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 55037b2dc..a39dceff2 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -46,5 +46,5 @@ pub mod prelude { pub use crate::eval::*; pub use crate::events::*; pub use crate::point_interaction::PointInteraction; - pub use keyboard_types::{self, Code, Key, Modifiers}; + pub use keyboard_types::{self, Code, Key, Location, Modifiers}; } diff --git a/packages/html/src/transit.rs b/packages/html/src/transit.rs index 8998b5fd7..4b97f8c05 100644 --- a/packages/html/src/transit.rs +++ b/packages/html/src/transit.rs @@ -225,7 +225,7 @@ impl EventData { fn test_back_and_forth() { let data = HtmlEvent { element: ElementId(0), - data: EventData::Mouse(SerializedMouseData::default().into()), + data: EventData::Mouse(SerializedMouseData::default()), name: "click".to_string(), bubbles: true, }; diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index 673658870..46dd26eaf 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -80,7 +80,7 @@ fn Route3(cx: Scope, dynamic: String) -> Element { render! { input { oninput: move |evt| { - *current_route_str.write() = evt.value.clone(); + *current_route_str.write() = evt.value(); }, value: "{current_route_str.read()}" } From 18fd40a5c2af4d8e7daee2bb9c4dd059836e33fa Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 4 Sep 2023 08:31:31 -0500 Subject: [PATCH 39/56] pin tauri bundler version --- packages/cli/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 28753f598..b1b47dd95 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -77,7 +77,7 @@ cargo-generate = "0.18" toml_edit = "0.19.11" # bundling -tauri-bundler = { version = "=1.2", features = ["native-tls-vendored"] } +tauri-bundler = { version = "=1.3.0", features = ["native-tls-vendored"] } tauri-utils = "1.3" dioxus-autofmt = { workspace = true } From bd38cddc9fcda1a8174514d48927a059f4d5b4c8 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 4 Sep 2023 19:17:43 -0500 Subject: [PATCH 40/56] fix dioxus core event tests --- packages/core/Cargo.toml | 1 + packages/core/tests/lifecycle.rs | 6 +- packages/core/tests/miri_full_app.rs | 7 +- packages/html/src/transit.rs | 145 +++++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 4 deletions(-) diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 71afe5825..c2d387d33 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -36,6 +36,7 @@ serde = { version = "1", features = ["derive"], optional = true } [dev-dependencies] tokio = { workspace = true, features = ["full"] } dioxus = { workspace = true } +dioxus-html = { workspace = true, features = ["serialize"] } pretty_assertions = "1.3.0" rand = "0.8.5" dioxus-ssr = { workspace = true } diff --git a/packages/core/tests/lifecycle.rs b/packages/core/tests/lifecycle.rs index 261ea0935..0f5bc99dd 100644 --- a/packages/core/tests/lifecycle.rs +++ b/packages/core/tests/lifecycle.rs @@ -4,6 +4,7 @@ //! Tests for the lifecycle of components. use dioxus::core::{ElementId, Mutation::*}; use dioxus::prelude::*; +use dioxus_html::SerializedHtmlEventConverter; use std::rc::Rc; use std::sync::{Arc, Mutex}; @@ -39,6 +40,7 @@ fn manual_diffing() { #[test] fn events_generate() { + set_event_converter(Box::new(SerializedHtmlEventConverter)); fn app(cx: Scope) -> Element { let count = cx.use_hook(|| 0); @@ -58,9 +60,9 @@ fn events_generate() { dom.handle_event( "click", - Rc::new(PlatformEventData::new(Box::new(MouseData::new( + Rc::new(PlatformEventData::new(Box::new( SerializedMouseData::default(), - )))), + ))), ElementId(1), true, ); diff --git a/packages/core/tests/miri_full_app.rs b/packages/core/tests/miri_full_app.rs index 93277b181..8698b01b1 100644 --- a/packages/core/tests/miri_full_app.rs +++ b/packages/core/tests/miri_full_app.rs @@ -1,9 +1,12 @@ +use crate::dioxus_elements::SerializedMouseData; use dioxus::prelude::*; use dioxus_core::ElementId; +use dioxus_elements::SerializedHtmlEventConverter; use std::rc::Rc; #[test] fn miri_rollover() { + set_event_converter(Box::new(SerializedHtmlEventConverter)); let mut dom = VirtualDom::new(app); _ = dom.rebuild(); @@ -11,9 +14,9 @@ fn miri_rollover() { for _ in 0..3 { dom.handle_event( "click", - Rc::new(PlatformEventData::new(Box::new(MouseData::new( + Rc::new(PlatformEventData::new(Box::new( SerializedMouseData::default(), - )))), + ))), ElementId(2), true, ); diff --git a/packages/html/src/transit.rs b/packages/html/src/transit.rs index 4b97f8c05..06a91b46e 100644 --- a/packages/html/src/transit.rs +++ b/packages/html/src/transit.rs @@ -260,3 +260,148 @@ fn test_back_and_forth() { assert_eq!(data, p); } + +/// A trait for converting from a serialized event to a concrete event type. +pub struct SerializedHtmlEventConverter; + +impl HtmlEventConverter for SerializedHtmlEventConverter { + fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_drag_data(&self, event: &PlatformEventData) -> DragData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_form_data(&self, event: &PlatformEventData) -> FormData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_image_data(&self, event: &PlatformEventData) -> ImageData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_media_data(&self, event: &PlatformEventData) -> MediaData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData { + event.downcast::<()>().cloned().unwrap().into() + } + + fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } + + fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData { + event + .downcast::() + .cloned() + .unwrap() + .into() + } +} From 0ab7a02777ce076f206d3d501888bfca44bdac40 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 5 Sep 2023 21:30:20 -0500 Subject: [PATCH 41/56] fix clippy --- packages/core/src/diff.rs | 3 +-- packages/core/tests/lifecycle.rs | 4 +--- packages/html/src/transit.rs | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index dd4cf8147..58fceaa48 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -577,8 +577,7 @@ impl<'b> VirtualDom { } // 4. Compute the LIS of this list - let mut lis_sequence = Vec::default(); - lis_sequence.reserve(new_index_to_old_index.len()); + let mut lis_sequence = Vec::with_capacity(new_index_to_old_index.len()); let mut predecessors = vec![0; new_index_to_old_index.len()]; let mut starts = vec![0; new_index_to_old_index.len()]; diff --git a/packages/core/tests/lifecycle.rs b/packages/core/tests/lifecycle.rs index 0f5bc99dd..6db294653 100644 --- a/packages/core/tests/lifecycle.rs +++ b/packages/core/tests/lifecycle.rs @@ -60,9 +60,7 @@ fn events_generate() { dom.handle_event( "click", - Rc::new(PlatformEventData::new(Box::new( - SerializedMouseData::default(), - ))), + Rc::new(PlatformEventData::new(Box::::default())), ElementId(1), true, ); diff --git a/packages/html/src/transit.rs b/packages/html/src/transit.rs index 06a91b46e..17a8a5f2d 100644 --- a/packages/html/src/transit.rs +++ b/packages/html/src/transit.rs @@ -337,8 +337,8 @@ impl HtmlEventConverter for SerializedHtmlEventConverter { .into() } - fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData { - event.downcast::<()>().cloned().unwrap().into() + fn convert_mounted_data(&self, _: &PlatformEventData) -> MountedData { + MountedData::from(()) } fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData { From d06e64e5ad13c03bc085810002b2c3fa8a61ce29 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 6 Sep 2023 12:51:02 -0500 Subject: [PATCH 42/56] fix dioxus-tui events --- packages/core/tests/miri_full_app.rs | 4 +- packages/dioxus-tui/src/element.rs | 8 +- packages/dioxus-tui/src/events.rs | 108 ++++++++++++++++++++++++ packages/dioxus-tui/src/lib.rs | 14 ++- packages/html/src/events/animation.rs | 11 +++ packages/html/src/events/clipboard.rs | 2 + packages/html/src/events/composition.rs | 8 ++ packages/html/src/events/drag.rs | 7 ++ packages/html/src/events/focus.rs | 7 ++ packages/html/src/events/keyboard.rs | 7 ++ packages/html/src/events/media.rs | 7 ++ packages/html/src/events/scroll.rs | 7 ++ packages/html/src/events/selection.rs | 7 ++ packages/html/src/events/toggle.rs | 7 ++ packages/html/src/events/touch.rs | 7 ++ packages/html/src/events/transition.rs | 7 ++ packages/html/src/events/wheel.rs | 7 ++ packages/rink/examples/widgets.rs | 3 +- packages/rink/src/hooks.rs | 14 +-- 19 files changed, 221 insertions(+), 21 deletions(-) create mode 100644 packages/dioxus-tui/src/events.rs diff --git a/packages/core/tests/miri_full_app.rs b/packages/core/tests/miri_full_app.rs index 8698b01b1..534c3e418 100644 --- a/packages/core/tests/miri_full_app.rs +++ b/packages/core/tests/miri_full_app.rs @@ -14,9 +14,7 @@ fn miri_rollover() { for _ in 0..3 { dom.handle_event( "click", - Rc::new(PlatformEventData::new(Box::new( - SerializedMouseData::default(), - ))), + Rc::new(PlatformEventData::new(Box::::default())), ElementId(2), true, ); diff --git a/packages/dioxus-tui/src/element.rs b/packages/dioxus-tui/src/element.rs index ba496fae1..8fdb60b9f 100644 --- a/packages/dioxus-tui/src/element.rs +++ b/packages/dioxus-tui/src/element.rs @@ -1,7 +1,6 @@ use std::{ any::Any, fmt::{Display, Formatter}, - rc::Rc, }; use dioxus_core::{ElementId, Mutations, VirtualDom}; @@ -30,7 +29,7 @@ pub(crate) fn find_mount_events(mutations: &Mutations) -> Vec { // We need to queue the mounted events to give rink time to rendere and resolve the layout of elements after they are created pub(crate) fn create_mounted_events( vdom: &VirtualDom, - events: &mut Vec<(ElementId, &'static str, Rc, bool)>, + events: &mut Vec<(ElementId, &'static str, Box, bool)>, mount_events: impl Iterator, ) { let query: Query = vdom @@ -42,11 +41,12 @@ pub(crate) fn create_mounted_events( query: query.clone(), id: node_id, }; - events.push((id, "mounted", Rc::new(MountedData::new(element)), false)); + events.push((id, "mounted", Box::new(MountedData::new(element)), false)); } } -struct TuiElement { +#[derive(Clone)] +pub(crate) struct TuiElement { query: Query, id: NodeId, } diff --git a/packages/dioxus-tui/src/events.rs b/packages/dioxus-tui/src/events.rs new file mode 100644 index 000000000..3e7b6fd2a --- /dev/null +++ b/packages/dioxus-tui/src/events.rs @@ -0,0 +1,108 @@ +use core::panic; + +use dioxus_html::*; + +use crate::element::TuiElement; + +fn downcast(event: &PlatformEventData) -> plasmo::EventData { + event + .downcast::() + .expect("event should be of type EventData") + .clone() +} + +pub(crate) struct SerializedHtmlEventConverter; + +impl HtmlEventConverter for SerializedHtmlEventConverter { + fn convert_animation_data(&self, _: &PlatformEventData) -> AnimationData { + panic!("animation events not supported") + } + + fn convert_clipboard_data(&self, _: &PlatformEventData) -> ClipboardData { + panic!("clipboard events not supported") + } + + fn convert_composition_data(&self, _: &PlatformEventData) -> CompositionData { + panic!("composition events not supported") + } + + fn convert_drag_data(&self, _: &PlatformEventData) -> DragData { + panic!("drag events not supported") + } + + fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData { + if let plasmo::EventData::Focus(event) = downcast(event) { + FocusData::new(event) + } else { + panic!("event should be of type Focus") + } + } + + fn convert_form_data(&self, event: &PlatformEventData) -> FormData { + if let plasmo::EventData::Form(event) = downcast(event) { + FormData::new(event) + } else { + panic!("event should be of type Form") + } + } + + fn convert_image_data(&self, _: &PlatformEventData) -> ImageData { + panic!("image events not supported") + } + + fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData { + if let plasmo::EventData::Keyboard(event) = downcast(event) { + KeyboardData::new(event) + } else { + panic!("event should be of type Keyboard") + } + } + + fn convert_media_data(&self, _: &PlatformEventData) -> MediaData { + panic!("media events not supported") + } + + fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData { + event.downcast::().cloned().unwrap().into() + } + + fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData { + if let plasmo::EventData::Mouse(event) = downcast(event) { + MouseData::new(event) + } else { + panic!("event should be of type Mouse") + } + } + + fn convert_pointer_data(&self, _: &PlatformEventData) -> PointerData { + panic!("pointer events not supported") + } + + fn convert_scroll_data(&self, _: &PlatformEventData) -> ScrollData { + panic!("scroll events not supported") + } + + fn convert_selection_data(&self, _: &PlatformEventData) -> SelectionData { + panic!("selection events not supported") + } + + fn convert_toggle_data(&self, _: &PlatformEventData) -> ToggleData { + panic!("toggle events not supported") + } + + fn convert_touch_data(&self, _: &PlatformEventData) -> TouchData { + panic!("touch events not supported") + } + + fn convert_transition_data(&self, _: &PlatformEventData) -> TransitionData { + panic!("transition events not supported") + } + + fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData { + if let plasmo::EventData::Wheel(event) = downcast(event) { + WheelData::new(event) + } else { + panic!("event should be of type Wheel") + } + } +} diff --git a/packages/dioxus-tui/src/lib.rs b/packages/dioxus-tui/src/lib.rs index fd5b63501..6fafa47ef 100644 --- a/packages/dioxus-tui/src/lib.rs +++ b/packages/dioxus-tui/src/lib.rs @@ -1,4 +1,5 @@ mod element; +mod events; use std::{ any::Any, @@ -8,6 +9,7 @@ use std::{ }; use dioxus_core::{Component, ElementId, VirtualDom}; +use dioxus_html::PlatformEventData; use dioxus_native_core::dioxus::{DioxusState, NodeImmutableDioxusExt}; use dioxus_native_core::prelude::*; @@ -24,6 +26,8 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) { } pub fn launch_cfg_with_props(app: Component, props: Props, cfg: Config) { + dioxus_html::set_event_converter(Box::new(events::SerializedHtmlEventConverter)); + render(cfg, |rdom, taffy, event_tx| { let dioxus_state = { let mut rdom = rdom.write().unwrap(); @@ -81,7 +85,7 @@ struct DioxusRenderer { vdom: VirtualDom, dioxus_state: Rc>, // Events that are queued up to be sent to the vdom next time the vdom is polled - queued_events: Vec<(ElementId, &'static str, Rc, bool)>, + queued_events: Vec<(ElementId, &'static str, Box, bool)>, #[cfg(all(feature = "hot-reload", debug_assertions))] hot_reload_rx: tokio::sync::mpsc::UnboundedReceiver, } @@ -122,15 +126,19 @@ impl Driver for DioxusRenderer { let id = { rdom.read().unwrap().get(id).unwrap().mounted_id() }; if let Some(id) = id { let inner_value = value.deref().clone(); + let boxed_event = Box::new(inner_value); + let platform_event = PlatformEventData::new(boxed_event); self.vdom - .handle_event(event, inner_value.into_any(), id, bubbles); + .handle_event(event, Rc::new(platform_event), id, bubbles); } } fn poll_async(&mut self) -> std::pin::Pin + '_>> { // Add any queued events for (id, event, value, bubbles) in self.queued_events.drain(..) { - self.vdom.handle_event(event, value, id, bubbles); + let platform_event = PlatformEventData::new(value); + self.vdom + .handle_event(event, Rc::new(platform_event), id, bubbles); } #[cfg(all(feature = "hot-reload", debug_assertions))] diff --git a/packages/html/src/events/animation.rs b/packages/html/src/events/animation.rs index 02612bc77..26db06f9d 100644 --- a/packages/html/src/events/animation.rs +++ b/packages/html/src/events/animation.rs @@ -7,18 +7,29 @@ pub struct AnimationData { } impl AnimationData { + /// Create a new AnimationData + pub fn new(inner: impl HasAnimationData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + + /// The name of the animation pub fn animation_name(&self) -> String { self.inner.animation_name() } + /// The name of the pseudo-element the animation runs on pub fn pseudo_element(&self) -> String { self.inner.pseudo_element() } + /// The amount of time the animation has been running pub fn elapsed_time(&self) -> f32 { self.inner.elapsed_time() } + /// Downcast this event to a concrete event type pub fn downcast(&self) -> Option<&T> { self.inner.as_ref().as_any().downcast_ref::() } diff --git a/packages/html/src/events/clipboard.rs b/packages/html/src/events/clipboard.rs index 0469c4f1c..c0180c728 100644 --- a/packages/html/src/events/clipboard.rs +++ b/packages/html/src/events/clipboard.rs @@ -25,12 +25,14 @@ impl PartialEq for ClipboardData { } impl ClipboardData { + /// Create a new ClipboardData pub fn new(inner: impl HasClipboardData) -> Self { Self { inner: Box::new(inner), } } + /// Downcast this event to a concrete event type pub fn downcast(&self) -> Option<&T> { self.inner.as_ref().as_any().downcast_ref::() } diff --git a/packages/html/src/events/composition.rs b/packages/html/src/events/composition.rs index 18ef4f5d1..4d161436e 100644 --- a/packages/html/src/events/composition.rs +++ b/packages/html/src/events/composition.rs @@ -27,6 +27,14 @@ impl PartialEq for CompositionData { } impl CompositionData { + /// Create a new CompositionData + pub fn new(inner: impl HasCompositionData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + + /// The characters generated by the input method that raised the event pub fn data(&self) -> String { self.inner.data() } diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index 67da07721..3ebd5fdbe 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -44,6 +44,13 @@ impl PartialEq for DragData { } impl DragData { + /// Create a new DragData + pub fn new(inner: impl HasDragData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// Downcast this event data to a specific type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() diff --git a/packages/html/src/events/focus.rs b/packages/html/src/events/focus.rs index f96c1eaee..f121feb7b 100644 --- a/packages/html/src/events/focus.rs +++ b/packages/html/src/events/focus.rs @@ -25,6 +25,13 @@ impl PartialEq for FocusData { } impl FocusData { + /// Create a new FocusData + pub fn new(inner: impl HasFocusData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// Downcast this event data to a specific type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() diff --git a/packages/html/src/events/keyboard.rs b/packages/html/src/events/keyboard.rs index c4c6115c1..a762f7ee9 100644 --- a/packages/html/src/events/keyboard.rs +++ b/packages/html/src/events/keyboard.rs @@ -46,6 +46,13 @@ impl PartialEq for KeyboardData { } impl KeyboardData { + /// Create a new KeyboardData + pub fn new(inner: impl HasKeyboardData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// The value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout. pub fn key(&self) -> Key { self.inner.key() diff --git a/packages/html/src/events/media.rs b/packages/html/src/events/media.rs index 1abcab21d..7f3180cf7 100644 --- a/packages/html/src/events/media.rs +++ b/packages/html/src/events/media.rs @@ -24,6 +24,13 @@ impl PartialEq for MediaData { } impl MediaData { + /// Create a new MediaData + pub fn new(inner: impl HasMediaData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// Downcast this event to a concrete event type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() diff --git a/packages/html/src/events/scroll.rs b/packages/html/src/events/scroll.rs index 4655afbcc..3693cc144 100644 --- a/packages/html/src/events/scroll.rs +++ b/packages/html/src/events/scroll.rs @@ -13,6 +13,13 @@ impl From for ScrollData { } impl ScrollData { + /// Create a new ScrollData + pub fn new(inner: impl HasScrollData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// Downcast this event to a concrete event type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() diff --git a/packages/html/src/events/selection.rs b/packages/html/src/events/selection.rs index de10b06c4..30c3d9bf0 100644 --- a/packages/html/src/events/selection.rs +++ b/packages/html/src/events/selection.rs @@ -7,6 +7,13 @@ pub struct SelectionData { } impl SelectionData { + /// Create a new SelectionData + pub fn new(inner: impl HasSelectionData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// Downcast this event to a concrete event type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() diff --git a/packages/html/src/events/toggle.rs b/packages/html/src/events/toggle.rs index 7de8be36d..e929a6a64 100644 --- a/packages/html/src/events/toggle.rs +++ b/packages/html/src/events/toggle.rs @@ -25,6 +25,13 @@ impl PartialEq for ToggleData { } impl ToggleData { + /// Create a new ToggleData + pub fn new(inner: impl HasToggleData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// Downcast this event to a concrete event type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() diff --git a/packages/html/src/events/touch.rs b/packages/html/src/events/touch.rs index 7e9009ab6..12d8a5de1 100644 --- a/packages/html/src/events/touch.rs +++ b/packages/html/src/events/touch.rs @@ -32,6 +32,13 @@ impl PartialEq for TouchData { } impl TouchData { + /// Create a new TouchData + pub fn new(inner: impl HasTouchData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// Returns true if the "ALT" key was down when the touch event was fired. pub fn alt_key(&self) -> bool { self.inner.alt_key() diff --git a/packages/html/src/events/transition.rs b/packages/html/src/events/transition.rs index c6dc65ae1..4e680d650 100644 --- a/packages/html/src/events/transition.rs +++ b/packages/html/src/events/transition.rs @@ -31,6 +31,13 @@ impl PartialEq for TransitionData { } impl TransitionData { + /// Create a new TransitionData + pub fn new(inner: impl HasTransitionData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// Downcast this event to a concrete event type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() diff --git a/packages/html/src/events/wheel.rs b/packages/html/src/events/wheel.rs index 157a8dc06..2661eee2b 100644 --- a/packages/html/src/events/wheel.rs +++ b/packages/html/src/events/wheel.rs @@ -30,6 +30,13 @@ impl PartialEq for WheelData { } impl WheelData { + /// Create a new WheelData + pub fn new(inner: impl HasWheelData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + /// The amount of wheel movement #[allow(deprecated)] pub fn delta(&self) -> WheelDelta { diff --git a/packages/rink/examples/widgets.rs b/packages/rink/examples/widgets.rs index e1f71fe42..42dc3723d 100644 --- a/packages/rink/examples/widgets.rs +++ b/packages/rink/examples/widgets.rs @@ -1,3 +1,4 @@ +use dioxus_html::HasFormData; use dioxus_native_core::{ prelude::*, real_dom::{NodeImmutable, NodeTypeMut}, @@ -79,7 +80,7 @@ impl Driver for Counter { if event_type == "input" { // when the button is clicked, increment the counter if let EventData::Form(input_event) = &*event { - if let Ok(value) = input_event.value.parse::() { + if let Ok(value) = input_event.value().parse::() { self.count = value; } } diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index 646ae52df..c5378bfbf 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -57,18 +57,18 @@ impl EventData { EventData::Keyboard(k) => Rc::new(k), EventData::Focus(f) => Rc::new(f), EventData::Wheel(w) => Rc::new(w), - EventData::Form(f) => Rc::new(f.into_html()), + EventData::Form(f) => Rc::new(f), } } } #[derive(Clone, Debug, PartialEq)] pub struct FormData { - pub value: String, + pub(crate) value: String, - pub values: HashMap>, + pub(crate) values: HashMap>, - pub files: Option, + pub(crate) files: Option, } impl HasFormData for FormData { @@ -85,12 +85,6 @@ impl HasFormData for FormData { } } -impl FormData { - fn into_html(self) -> dioxus_html::FormData { - dioxus_html::FormData::new(self) - } -} - #[derive(Clone, Debug, PartialEq)] pub struct Files { files: FxHashMap, From 3a02520ffac43f5743f0ec3bde51debe75ae63f1 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 8 Sep 2023 10:32:48 -0500 Subject: [PATCH 43/56] fix web onmount event --- packages/web/src/dom.rs | 3 +-- packages/web/src/event.rs | 14 +++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 9799b50be..9cdabed3f 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -253,8 +253,7 @@ impl WebsysDom { let node = dioxus_interpreter_js::get_node(id.0 as u32); if let Some(element) = node.dyn_ref::() { log::info!("mounted event fired: {}", id.0); - let data: dioxus_html::MountedData = element.into(); - let data = PlatformEventData::new(Box::new(data)); + let data = PlatformEventData::new(Box::new(element.clone())); let _ = self.event_channel.unbounded_send(UiEvent { name: "mounted".to_string(), bubbles: false, diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index e9d6e4afb..d1eed67a6 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -83,7 +83,11 @@ impl HtmlEventConverter for WebEventConverter { fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { #[cfg(feature = "mounted")] { - MountedData::from(downcast_event(event).element.clone()) + MountedData::from( + event + .downcast::() + .expect("event should be a web_sys::Element"), + ) } #[cfg(not(feature = "mounted"))] { @@ -216,10 +220,10 @@ impl WebEventExt for dioxus_html::MediaData { } } -impl WebEventExt for MountedData { - fn web_event(&self) -> &MountedData { - self.downcast::() - .expect("event should be a WebMountedEvent") +impl WebEventExt for MountedData { + fn web_event(&self) -> &web_sys::Element { + self.downcast::() + .expect("event should be a web_sys::Element") } } From a9bb04722f63f7798878e6147c6a8a787070eed3 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 12 Sep 2023 10:01:53 -0500 Subject: [PATCH 44/56] split the point interaction trait into smaller traits for touch compatibility --- packages/html/src/events/drag.rs | 18 +++++-- packages/html/src/events/mouse.rs | 20 +++++-- packages/html/src/events/pointer.rs | 20 +++++-- packages/html/src/lib.rs | 2 +- packages/html/src/point_interaction.rs | 69 ++++++++++++++---------- packages/html/src/web_sys_bind/events.rs | 12 ++++- packages/rink/src/hooks.rs | 2 +- packages/rink/src/widgets/slider.rs | 4 +- packages/rink/src/widgets/text_like.rs | 4 +- 9 files changed, 101 insertions(+), 50 deletions(-) diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index 3ebd5fdbe..a21f33f25 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -1,6 +1,6 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; use crate::input_data::{MouseButton, MouseButtonSet}; -use crate::prelude::PointInteraction; +use crate::prelude::*; use dioxus_core::Event; use keyboard_types::Modifiers; @@ -17,7 +17,7 @@ pub struct DragData { inner: Box, } -impl From for DragData { +impl From for DragData { fn from(e: E) -> Self { Self { inner: Box::new(e) } } @@ -57,7 +57,7 @@ impl DragData { } } -impl PointInteraction for DragData { +impl InteractionLocation for DragData { fn client_coordinates(&self) -> ClientPoint { self.inner.client_coordinates() } @@ -77,11 +77,15 @@ impl PointInteraction for DragData { fn coordinates(&self) -> Coordinates { self.inner.coordinates() } +} +impl ModifiersInteraction for DragData { fn modifiers(&self) -> Modifiers { self.inner.modifiers() } +} +impl PointerInteraction for DragData { fn held_buttons(&self) -> MouseButtonSet { self.inner.held_buttons() } @@ -119,7 +123,7 @@ impl HasMouseData for SerializedDragData { } #[cfg(feature = "serialize")] -impl crate::prelude::PointInteraction for SerializedDragData { +impl InteractionLocation for SerializedDragData { fn client_coordinates(&self) -> ClientPoint { self.mouse.client_coordinates() } @@ -139,11 +143,17 @@ impl crate::prelude::PointInteraction for SerializedDragData { fn coordinates(&self) -> Coordinates { self.mouse.coordinates() } +} +#[cfg(feature = "serialize")] +impl ModifiersInteraction for SerializedDragData { fn modifiers(&self) -> Modifiers { self.mouse.modifiers() } +} +#[cfg(feature = "serialize")] +impl PointerInteraction for SerializedDragData { fn held_buttons(&self) -> MouseButtonSet { self.mouse.held_buttons() } diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index c6a98e25e..d7f328043 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -1,6 +1,6 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; use crate::input_data::{MouseButton, MouseButtonSet}; -use crate::point_interaction::PointInteraction; +use crate::prelude::*; use dioxus_core::Event; use keyboard_types::Modifiers; @@ -12,7 +12,7 @@ pub struct MouseData { inner: Box, } -impl From for MouseData { +impl From for MouseData { fn from(e: E) -> Self { Self { inner: Box::new(e) } } @@ -39,7 +39,7 @@ impl PartialEq for MouseData { } /// A trait for any object that has the data for a mouse event -pub trait HasMouseData: PointInteraction { +pub trait HasMouseData: PointerInteraction { /// return self as Any fn as_any(&self) -> &dyn std::any::Any; } @@ -119,7 +119,7 @@ impl MouseData { } } -impl PointInteraction for MouseData { +impl InteractionLocation for MouseData { /// The event's coordinates relative to the application's viewport (as opposed to the coordinate within the page). /// /// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally. @@ -149,12 +149,16 @@ impl PointInteraction for MouseData { fn coordinates(&self) -> Coordinates { self.inner.coordinates() } +} +impl ModifiersInteraction for MouseData { /// The set of modifier keys which were pressed when the event occurred fn modifiers(&self) -> Modifiers { self.inner.modifiers() } +} +impl PointerInteraction for MouseData { /// The set of mouse buttons which were held when the event occurred. fn held_buttons(&self) -> MouseButtonSet { self.inner.held_buttons() @@ -224,7 +228,7 @@ impl HasMouseData for SerializedMouseData { } #[cfg(feature = "serialize")] -impl PointInteraction for SerializedMouseData { +impl InteractionLocation for SerializedMouseData { fn client_coordinates(&self) -> ClientPoint { self.point_data.client_coordinates() } @@ -240,11 +244,17 @@ impl PointInteraction for SerializedMouseData { fn screen_coordinates(&self) -> ScreenPoint { self.point_data.screen_coordinates() } +} +#[cfg(feature = "serialize")] +impl ModifiersInteraction for SerializedMouseData { fn modifiers(&self) -> Modifiers { self.point_data.modifiers() } +} +#[cfg(feature = "serialize")] +impl PointerInteraction for SerializedMouseData { fn held_buttons(&self) -> MouseButtonSet { self.point_data.held_buttons() } diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index ec351d3dc..6c96136ef 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -1,7 +1,7 @@ use dioxus_core::Event; use keyboard_types::Modifiers; -use crate::{geometry::*, input_data::*, point_interaction::PointInteraction}; +use crate::{geometry::*, input_data::*, prelude::*}; /// A synthetic event that wraps a web-style [`PointerEvent`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) pub type PointerEvent = Event; @@ -22,7 +22,7 @@ impl PointerData { } } -impl From for PointerData { +impl From for PointerData { fn from(e: E) -> Self { Self { inner: Box::new(e) } } @@ -69,7 +69,7 @@ impl PartialEq for PointerData { } /// A trait for any object that has the data for a pointer event -pub trait HasPointerData: PointInteraction { +pub trait HasPointerData: PointerInteraction { /// Gets the unique identifier of the pointer causing the event. fn pointer_id(&self) -> i32; @@ -189,7 +189,7 @@ impl PointerData { } } -impl PointInteraction for PointerData { +impl InteractionLocation for PointerData { fn client_coordinates(&self) -> ClientPoint { self.inner.client_coordinates() } @@ -205,11 +205,15 @@ impl PointInteraction for PointerData { fn page_coordinates(&self) -> PagePoint { self.inner.page_coordinates() } +} +impl ModifiersInteraction for PointerData { fn modifiers(&self) -> Modifiers { self.inner.modifiers() } +} +impl PointerInteraction for PointerData { fn held_buttons(&self) -> MouseButtonSet { self.inner.held_buttons() } @@ -306,7 +310,7 @@ impl HasPointerData for SerializedPointerData { } #[cfg(feature = "serialize")] -impl PointInteraction for SerializedPointerData { +impl InteractionLocation for SerializedPointerData { fn client_coordinates(&self) -> ClientPoint { self.point_data.client_coordinates() } @@ -322,11 +326,17 @@ impl PointInteraction for SerializedPointerData { fn page_coordinates(&self) -> PagePoint { self.point_data.page_coordinates() } +} +#[cfg(feature = "serialize")] +impl ModifiersInteraction for SerializedPointerData { fn modifiers(&self) -> Modifiers { self.point_data.modifiers() } +} +#[cfg(feature = "serialize")] +impl PointerInteraction for SerializedPointerData { fn held_buttons(&self) -> MouseButtonSet { self.point_data.held_buttons() } diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index a39dceff2..e8ee3ffb8 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -45,6 +45,6 @@ pub mod prelude { #[cfg(feature = "eval")] pub use crate::eval::*; pub use crate::events::*; - pub use crate::point_interaction::PointInteraction; + pub use crate::point_interaction::*; pub use keyboard_types::{self, Code, Key, Location, Modifiers}; } diff --git a/packages/html/src/point_interaction.rs b/packages/html/src/point_interaction.rs index f26917201..9204f968e 100644 --- a/packages/html/src/point_interaction.rs +++ b/packages/html/src/point_interaction.rs @@ -5,8 +5,9 @@ use crate::{ input_data::{MouseButton, MouseButtonSet}, }; -pub trait PointInteraction: std::any::Any { - /// Gets the coordinates of the pointer event. +/// A interaction that contains data about the location of the event. +pub trait InteractionLocation { + /// Gets the coordinates of the event. fn coordinates(&self) -> Coordinates { Coordinates::new( self.screen_coordinates(), @@ -16,26 +17,32 @@ pub trait PointInteraction: std::any::Any { ) } - /// Gets the coordinates of the pointer event relative to the browser viewport. + /// Gets the coordinates of the event relative to the browser viewport. fn client_coordinates(&self) -> ClientPoint; - /// Gets the coordinates of the pointer event relative to the screen. + /// Gets the coordinates of the event relative to the screen. fn screen_coordinates(&self) -> ScreenPoint; - /// Gets the coordinates of the pointer event relative to the target element. + /// Gets the coordinates of the event relative to the target element. fn element_coordinates(&self) -> ElementPoint; - /// Gets the coordinates of the pointer event relative to the page. + /// Gets the coordinates of the event relative to the page. fn page_coordinates(&self) -> PagePoint; +} - /// Gets the modifiers of the pointer event. - fn modifiers(&self) -> Modifiers; +/// A interaction that contains data about the pointer button(s) that triggered the event. +pub trait PointerInteraction: InteractionLocation + ModifiersInteraction { + /// Gets the button that triggered the event. + fn trigger_button(&self) -> Option; /// Gets the buttons that are currently held down. fn held_buttons(&self) -> MouseButtonSet; +} - /// Gets the button that triggered the event. - fn trigger_button(&self) -> Option; +/// A interaction that contains data about the current state of the keyboard modifiers. +pub trait ModifiersInteraction { + /// Gets the modifiers of the pointer event. + fn modifiers(&self) -> Modifiers; } #[cfg(feature = "serialize")] @@ -138,7 +145,7 @@ impl SerializedPointInteraction { } #[cfg(feature = "serialize")] -impl From<&E> for SerializedPointInteraction { +impl From<&E> for SerializedPointInteraction { fn from(data: &E) -> Self { let trigger_button = data.trigger_button(); let held_buttons = data.held_buttons(); @@ -149,23 +156,18 @@ impl From<&E> for SerializedPointInteraction { } #[cfg(feature = "serialize")] -impl PointInteraction for SerializedPointInteraction { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.client_x.into(), self.client_y.into()) +impl PointerInteraction for SerializedPointInteraction { + fn held_buttons(&self) -> MouseButtonSet { + crate::input_data::decode_mouse_button_set(self.buttons) } - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.screen_x.into(), self.screen_y.into()) - } - - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x.into(), self.offset_y.into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.page_x.into(), self.page_y.into()) + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.button)) } +} +#[cfg(feature = "serialize")] +impl ModifiersInteraction for SerializedPointInteraction { fn modifiers(&self) -> Modifiers { let mut modifiers = Modifiers::empty(); @@ -184,12 +186,23 @@ impl PointInteraction for SerializedPointInteraction { modifiers } +} - fn held_buttons(&self) -> MouseButtonSet { - crate::input_data::decode_mouse_button_set(self.buttons) +#[cfg(feature = "serialize")] +impl InteractionLocation for SerializedPointInteraction { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.client_x.into(), self.client_y.into()) } - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.button)) + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.screen_x.into(), self.screen_y.into()) + } + + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.offset_x.into(), self.offset_y.into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.page_x.into(), self.page_y.into()) } } diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index 643a06045..148a30c88 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -103,7 +103,7 @@ impl HasKeyboardData for KeyboardEvent { impl HasDragData for MouseEvent {} -impl PointInteraction for MouseEvent { +impl InteractionLocation for MouseEvent { fn client_coordinates(&self) -> ClientPoint { ClientPoint::new(self.client_x().into(), self.client_y().into()) } @@ -119,7 +119,9 @@ impl PointInteraction for MouseEvent { fn screen_coordinates(&self) -> ScreenPoint { ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) } +} +impl ModifiersInteraction for MouseEvent { fn modifiers(&self) -> Modifiers { let mut modifiers = Modifiers::empty(); @@ -138,7 +140,9 @@ impl PointInteraction for MouseEvent { modifiers } +} +impl PointerInteraction for MouseEvent { fn held_buttons(&self) -> crate::input_data::MouseButtonSet { decode_mouse_button_set(self.buttons()) } @@ -222,7 +226,7 @@ impl HasPointerData for PointerEvent { } } -impl PointInteraction for PointerEvent { +impl InteractionLocation for PointerEvent { fn client_coordinates(&self) -> ClientPoint { ClientPoint::new(self.client_x().into(), self.client_y().into()) } @@ -238,7 +242,9 @@ impl PointInteraction for PointerEvent { fn page_coordinates(&self) -> PagePoint { PagePoint::new(self.page_x().into(), self.page_y().into()) } +} +impl ModifiersInteraction for PointerEvent { fn modifiers(&self) -> Modifiers { let mut modifiers = Modifiers::empty(); @@ -257,7 +263,9 @@ impl PointInteraction for PointerEvent { modifiers } +} +impl PointerInteraction for PointerEvent { fn held_buttons(&self) -> crate::input_data::MouseButtonSet { decode_mouse_button_set(self.buttons()) } diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index c5378bfbf..1ffa361f7 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -18,7 +18,7 @@ use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers}; use dioxus_html::input_data::{ MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons, }; -use dioxus_html::{event_bubbles, prelude::PointInteraction}; +use dioxus_html::{event_bubbles, prelude::*}; use std::any::Any; use std::collections::HashMap; use std::{ diff --git a/packages/rink/src/widgets/slider.rs b/packages/rink/src/widgets/slider.rs index 5cef645cf..b3d6d24da 100644 --- a/packages/rink/src/widgets/slider.rs +++ b/packages/rink/src/widgets/slider.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use dioxus_html::{ - input_data::keyboard_types::Key, prelude::PointInteraction, HasKeyboardData, - SerializedKeyboardData, SerializedMouseData, + input_data::keyboard_types::Key, prelude::*, HasKeyboardData, SerializedKeyboardData, + SerializedMouseData, }; use dioxus_native_core::{ custom_element::CustomElement, diff --git a/packages/rink/src/widgets/text_like.rs b/packages/rink/src/widgets/text_like.rs index e7869600a..3231a9e29 100644 --- a/packages/rink/src/widgets/text_like.rs +++ b/packages/rink/src/widgets/text_like.rs @@ -2,8 +2,8 @@ use std::{collections::HashMap, io::stdout}; use crossterm::{cursor::MoveTo, execute}; use dioxus_html::{ - input_data::keyboard_types::Key, prelude::PointInteraction, HasKeyboardData, - SerializedKeyboardData, SerializedMouseData, + input_data::keyboard_types::Key, prelude::*, HasKeyboardData, SerializedKeyboardData, + SerializedMouseData, }; use dioxus_native_core::{ custom_element::CustomElement, From c10700c962367bbacfa46ef8c06dc6c7136fd652 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 12 Sep 2023 17:28:55 -0500 Subject: [PATCH 45/56] add touch data to touch events --- packages/html/Cargo.toml | 3 +- packages/html/src/events/drag.rs | 21 ++- packages/html/src/events/keyboard.rs | 45 ++--- packages/html/src/events/mouse.rs | 31 ++-- packages/html/src/events/pointer.rs | 21 ++- packages/html/src/events/touch.rs | 219 +++++++++++++++++------ packages/html/src/point_interaction.rs | 34 ++-- packages/html/src/web_sys_bind/events.rs | 125 +++++++++---- 8 files changed, 339 insertions(+), 160 deletions(-) diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 5b665d14b..00df6040a 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -29,12 +29,13 @@ serde_json = { version = "1", optional = true } optional = true version = "0.3.56" features = [ + "Touch", + "TouchList", "TouchEvent", "MouseEvent", "InputEvent", "ClipboardEvent", "KeyboardEvent", - "TouchEvent", "WheelEvent", "AnimationEvent", "TransitionEvent", diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index a21f33f25..e58c20e22 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -62,10 +62,6 @@ impl InteractionLocation for DragData { self.inner.client_coordinates() } - fn element_coordinates(&self) -> ElementPoint { - self.inner.element_coordinates() - } - fn page_coordinates(&self) -> PagePoint { self.inner.page_coordinates() } @@ -73,6 +69,12 @@ impl InteractionLocation for DragData { fn screen_coordinates(&self) -> ScreenPoint { self.inner.screen_coordinates() } +} + +impl InteractionElementOffset for DragData { + fn element_coordinates(&self) -> ElementPoint { + self.inner.element_coordinates() + } fn coordinates(&self) -> Coordinates { self.inner.coordinates() @@ -128,10 +130,6 @@ impl InteractionLocation for SerializedDragData { self.mouse.client_coordinates() } - fn element_coordinates(&self) -> ElementPoint { - self.mouse.element_coordinates() - } - fn page_coordinates(&self) -> PagePoint { self.mouse.page_coordinates() } @@ -139,6 +137,13 @@ impl InteractionLocation for SerializedDragData { fn screen_coordinates(&self) -> ScreenPoint { self.mouse.screen_coordinates() } +} + +#[cfg(feature = "serialize")] +impl InteractionElementOffset for SerializedDragData { + fn element_coordinates(&self) -> ElementPoint { + self.mouse.element_coordinates() + } fn coordinates(&self) -> Coordinates { self.mouse.coordinates() diff --git a/packages/html/src/events/keyboard.rs b/packages/html/src/events/keyboard.rs index a762f7ee9..feb0ab313 100644 --- a/packages/html/src/events/keyboard.rs +++ b/packages/html/src/events/keyboard.rs @@ -2,6 +2,8 @@ use dioxus_core::Event; use keyboard_types::{Code, Key, Location, Modifiers}; use std::fmt::Debug; +use crate::prelude::ModifiersInteraction; + #[cfg(feature = "serialize")] fn resilient_deserialize_code<'de, D>(deserializer: D) -> Result where @@ -63,11 +65,6 @@ impl KeyboardData { self.inner.code() } - /// The set of modifier keys which were pressed when the event occurred - pub fn modifiers(&self) -> Modifiers { - self.inner.modifiers() - } - /// The location of the key on the keyboard or other input device. pub fn location(&self) -> Location { self.inner.location() @@ -84,6 +81,12 @@ impl KeyboardData { } } +impl ModifiersInteraction for KeyboardData { + fn modifiers(&self) -> Modifiers { + self.inner.modifiers() + } +} + #[cfg(feature = "serialize")] /// A serialized version of KeyboardData #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] @@ -155,6 +158,21 @@ impl HasKeyboardData for SerializedKeyboardData { self.code } + fn location(&self) -> Location { + crate::input_data::decode_key_location(self.location) + } + + fn is_auto_repeating(&self) -> bool { + self.repeat + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl ModifiersInteraction for SerializedKeyboardData { fn modifiers(&self) -> Modifiers { let mut modifiers = Modifiers::empty(); @@ -173,18 +191,6 @@ impl HasKeyboardData for SerializedKeyboardData { modifiers } - - fn location(&self) -> Location { - crate::input_data::decode_key_location(self.location) - } - - fn is_auto_repeating(&self) -> bool { - self.repeat - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } } #[cfg(feature = "serialize")] @@ -217,16 +223,13 @@ impl_event! { onkeyup } -pub trait HasKeyboardData: std::any::Any { +pub trait HasKeyboardData: ModifiersInteraction + std::any::Any { /// The value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout. fn key(&self) -> Key; /// A physical key on the keyboard (as opposed to the character generated by pressing the key). In other words, this property returns a value that isn't altered by keyboard layout or the state of the modifier keys. fn code(&self) -> Code; - /// The set of modifier keys which were pressed when the event occurred - fn modifiers(&self) -> Modifiers; - /// The location of the key on the keyboard or other input device. fn location(&self) -> Location; diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index d7f328043..0fbaf2c9e 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -120,31 +120,23 @@ impl MouseData { } impl InteractionLocation for MouseData { - /// The event's coordinates relative to the application's viewport (as opposed to the coordinate within the page). - /// - /// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally. fn client_coordinates(&self) -> ClientPoint { self.inner.client_coordinates() } - /// The event's coordinates relative to the padding edge of the target element - /// - /// For example, clicking in the top left corner of an element will result in element coordinates (0., 0.) - fn element_coordinates(&self) -> ElementPoint { - self.inner.element_coordinates() - } - - /// The event's coordinates relative to the entire document. This includes any portion of the document not currently visible. - /// - /// For example, if the page is scrolled 200 pixels to the right and 300 pixels down, clicking in the top left corner of the viewport would result in page coordinates (200., 300.) fn page_coordinates(&self) -> PagePoint { self.inner.page_coordinates() } - /// The event's coordinates relative to the entire screen. This takes into account the window's offset. fn screen_coordinates(&self) -> ScreenPoint { self.inner.screen_coordinates() } +} + +impl InteractionElementOffset for MouseData { + fn element_coordinates(&self) -> ElementPoint { + self.inner.element_coordinates() + } fn coordinates(&self) -> Coordinates { self.inner.coordinates() @@ -233,10 +225,6 @@ impl InteractionLocation for SerializedMouseData { self.point_data.client_coordinates() } - fn element_coordinates(&self) -> ElementPoint { - self.point_data.element_coordinates() - } - fn page_coordinates(&self) -> PagePoint { self.point_data.page_coordinates() } @@ -246,6 +234,13 @@ impl InteractionLocation for SerializedMouseData { } } +#[cfg(feature = "serialize")] +impl InteractionElementOffset for SerializedMouseData { + fn element_coordinates(&self) -> ElementPoint { + self.point_data.element_coordinates() + } +} + #[cfg(feature = "serialize")] impl ModifiersInteraction for SerializedMouseData { fn modifiers(&self) -> Modifiers { diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 6c96136ef..9556502d3 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -198,15 +198,17 @@ impl InteractionLocation for PointerData { self.inner.screen_coordinates() } - fn element_coordinates(&self) -> ElementPoint { - self.inner.element_coordinates() - } - fn page_coordinates(&self) -> PagePoint { self.inner.page_coordinates() } } +impl InteractionElementOffset for PointerData { + fn element_coordinates(&self) -> ElementPoint { + self.inner.element_coordinates() + } +} + impl ModifiersInteraction for PointerData { fn modifiers(&self) -> Modifiers { self.inner.modifiers() @@ -319,15 +321,18 @@ impl InteractionLocation for SerializedPointerData { self.point_data.screen_coordinates() } - fn element_coordinates(&self) -> ElementPoint { - self.point_data.element_coordinates() - } - fn page_coordinates(&self) -> PagePoint { self.point_data.page_coordinates() } } +#[cfg(feature = "serialize")] +impl InteractionElementOffset for SerializedPointerData { + fn element_coordinates(&self) -> ElementPoint { + self.point_data.element_coordinates() + } +} + #[cfg(feature = "serialize")] impl ModifiersInteraction for SerializedPointerData { fn modifiers(&self) -> Modifiers { diff --git a/packages/html/src/events/touch.rs b/packages/html/src/events/touch.rs index 12d8a5de1..83fdbb0b4 100644 --- a/packages/html/src/events/touch.rs +++ b/packages/html/src/events/touch.rs @@ -1,4 +1,8 @@ use dioxus_core::Event; +use keyboard_types::Modifiers; + +use crate::geometry::*; +use crate::prelude::{InteractionLocation, ModifiersInteraction}; pub type TouchEvent = Event; pub struct TouchData { @@ -14,20 +18,14 @@ impl From for TouchData { impl std::fmt::Debug for TouchData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TouchData") - .field("alt_key", &self.alt_key()) - .field("ctrl_key", &self.ctrl_key()) - .field("meta_key", &self.meta_key()) - .field("shift_key", &self.shift_key()) + .field("modifiers", &self.modifiers()) .finish() } } impl PartialEq for TouchData { fn eq(&self, other: &Self) -> bool { - self.alt_key() == other.alt_key() - && self.ctrl_key() == other.ctrl_key() - && self.meta_key() == other.meta_key() - && self.shift_key() == other.shift_key() + self.modifiers() == other.modifiers() } } @@ -39,24 +37,9 @@ impl TouchData { } } - /// Returns true if the "ALT" key was down when the touch event was fired. - pub fn alt_key(&self) -> bool { - self.inner.alt_key() - } - - /// Returns true if the "CTRL" key was down when the touch event was fired. - pub fn ctrl_key(&self) -> bool { - self.inner.ctrl_key() - } - - /// Returns true if the "META" key was down when the touch event was fired. - pub fn meta_key(&self) -> bool { - self.inner.meta_key() - } - - /// Returns true if the "SHIFT" key was down when the touch event was fired. - pub fn shift_key(&self) -> bool { - self.inner.shift_key() + /// Get the pointers that are currently down + pub fn touches(&self) -> Vec { + self.inner.touches() } /// Downcast this event to a concrete event type @@ -65,6 +48,12 @@ impl TouchData { } } +impl ModifiersInteraction for TouchData { + fn modifiers(&self) -> Modifiers { + self.inner.modifiers() + } +} + #[cfg(feature = "serialize")] /// A serialized version of TouchData #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] @@ -73,36 +62,47 @@ pub struct SerializedTouchData { ctrl_key: bool, meta_key: bool, shift_key: bool, + touches: Vec, } #[cfg(feature = "serialize")] impl From<&TouchData> for SerializedTouchData { fn from(data: &TouchData) -> Self { + let modifiers = data.modifiers(); Self { - alt_key: data.alt_key(), - ctrl_key: data.ctrl_key(), - meta_key: data.meta_key(), - shift_key: data.shift_key(), + alt_key: modifiers.contains(Modifiers::ALT), + ctrl_key: modifiers.contains(Modifiers::CONTROL), + meta_key: modifiers.contains(Modifiers::META), + shift_key: modifiers.contains(Modifiers::SHIFT), + touches: data.touches().iter().map(|t| t.into()).collect(), } } } +#[cfg(feature = "serialize")] +impl ModifiersInteraction for SerializedTouchData { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::default(); + if self.alt_key { + modifiers.insert(Modifiers::ALT); + } + if self.ctrl_key { + modifiers.insert(Modifiers::CONTROL); + } + if self.meta_key { + modifiers.insert(Modifiers::META); + } + if self.shift_key { + modifiers.insert(Modifiers::SHIFT); + } + modifiers + } +} + #[cfg(feature = "serialize")] impl HasTouchData for SerializedTouchData { - fn alt_key(&self) -> bool { - self.alt_key - } - - fn ctrl_key(&self) -> bool { - self.ctrl_key - } - - fn meta_key(&self) -> bool { - self.meta_key - } - - fn shift_key(&self) -> bool { - self.shift_key + fn touches(&self) -> Vec { + Vec::new() } fn as_any(&self) -> &dyn std::any::Any { @@ -127,23 +127,130 @@ impl<'de> serde::Deserialize<'de> for TouchData { } } -pub trait HasTouchData: std::any::Any { - /// Returns true if the "ALT" key was down when the touch event was fired. - fn alt_key(&self) -> bool; - - /// Returns true if the "CTRL" key was down when the touch event was fired. - fn ctrl_key(&self) -> bool; - - /// Returns true if the "META" key was down when the touch event was fired. - fn meta_key(&self) -> bool; - - /// Returns true if the "SHIFT" key was down when the touch event was fired. - fn shift_key(&self) -> bool; +pub trait HasTouchData: ModifiersInteraction + std::any::Any { + /// Get the pointers that are currently down + fn touches(&self) -> Vec; /// return self as Any fn as_any(&self) -> &dyn std::any::Any; } +pub struct TouchPoint { + inner: Box, +} + +impl From for TouchPoint { + fn from(e: E) -> Self { + Self { inner: Box::new(e) } + } +} + +impl TouchPoint { + /// Create a new TouchPoint + pub fn new(inner: impl HasTouchPointData + 'static) -> Self { + Self { + inner: Box::new(inner), + } + } + + /// A unique identifier for this touch point that will be the same for the duration of the touch + fn identifier(&self) -> i32 { + self.inner.identifier() + } + + /// the pressure of the touch + fn force(&self) -> f64 { + self.inner.force() + } + + /// the radius of the touch + fn radius(&self) -> ScreenPoint { + self.inner.radius() + } + + /// the rotation of the touch in degrees between 0 and 90 + fn rotation(&self) -> f64 { + self.inner.rotation() + } + + /// Downcast this event to a concrete event type + pub fn downcast(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } +} + +impl InteractionLocation for TouchPoint { + fn client_coordinates(&self) -> ClientPoint { + self.inner.client_coordinates() + } + + fn page_coordinates(&self) -> PagePoint { + self.inner.page_coordinates() + } + + fn screen_coordinates(&self) -> ScreenPoint { + self.inner.screen_coordinates() + } +} + +/// A trait for touch point data +pub trait HasTouchPointData: InteractionLocation + std::any::Any { + /// A unique identifier for this touch point that will be the same for the duration of the touch + fn identifier(&self) -> i32; + + /// the pressure of the touch + fn force(&self) -> f64; + + /// the radius of the touch + fn radius(&self) -> ScreenPoint; + + /// the rotation of the touch in degrees between 0 and 90 + fn rotation(&self) -> f64; + + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any; +} + +#[cfg(feature = "serialize")] +/// A serialized version of TouchPoint +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] +struct SerializedTouchPoint { + identifier: i32, + client_x: f64, + client_y: f64, + page_x: f64, + page_y: f64, + screen_x: f64, + screen_y: f64, + force: f64, + radius_x: f64, + radius_y: f64, + rotation_angle: f64, +} + +#[cfg(feature = "serialize")] +impl From<&TouchPoint> for SerializedTouchPoint { + fn from(point: &TouchPoint) -> Self { + let client_coordinates = point.client_coordinates(); + + let page_coordinates = point.page_coordinates(); + let screen_coordinates = point.screen_coordinates(); + Self { + identifier: point.identifier(), + client_x: client_coordinates.x, + client_y: client_coordinates.y, + page_x: page_coordinates.x, + page_y: page_coordinates.y, + screen_x: screen_coordinates.x, + screen_y: screen_coordinates.y, + force: point.force(), + radius_x: point.radius().x, + radius_y: point.radius().y, + rotation_angle: point.rotation(), + } + } +} + impl_event! { TouchData; /// touchstart diff --git a/packages/html/src/point_interaction.rs b/packages/html/src/point_interaction.rs index 9204f968e..9aa4e0f31 100644 --- a/packages/html/src/point_interaction.rs +++ b/packages/html/src/point_interaction.rs @@ -7,6 +7,18 @@ use crate::{ /// A interaction that contains data about the location of the event. pub trait InteractionLocation { + /// Gets the coordinates of the event relative to the browser viewport. + fn client_coordinates(&self) -> ClientPoint; + + /// Gets the coordinates of the event relative to the screen. + fn screen_coordinates(&self) -> ScreenPoint; + + /// Gets the coordinates of the event relative to the page. + fn page_coordinates(&self) -> PagePoint; +} + +/// A interaction that contains data about the location of the event. +pub trait InteractionElementOffset: InteractionLocation { /// Gets the coordinates of the event. fn coordinates(&self) -> Coordinates { Coordinates::new( @@ -17,21 +29,12 @@ pub trait InteractionLocation { ) } - /// Gets the coordinates of the event relative to the browser viewport. - fn client_coordinates(&self) -> ClientPoint; - - /// Gets the coordinates of the event relative to the screen. - fn screen_coordinates(&self) -> ScreenPoint; - /// Gets the coordinates of the event relative to the target element. fn element_coordinates(&self) -> ElementPoint; - - /// Gets the coordinates of the event relative to the page. - fn page_coordinates(&self) -> PagePoint; } /// A interaction that contains data about the pointer button(s) that triggered the event. -pub trait PointerInteraction: InteractionLocation + ModifiersInteraction { +pub trait PointerInteraction: InteractionElementOffset + ModifiersInteraction { /// Gets the button that triggered the event. fn trigger_button(&self) -> Option; @@ -198,11 +201,14 @@ impl InteractionLocation for SerializedPointInteraction { ScreenPoint::new(self.screen_x.into(), self.screen_y.into()) } - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x.into(), self.offset_y.into()) - } - fn page_coordinates(&self) -> PagePoint { PagePoint::new(self.page_x.into(), self.page_y.into()) } } + +#[cfg(feature = "serialize")] +impl InteractionElementOffset for SerializedPointInteraction { + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.offset_x.into(), self.offset_y.into()) + } +} diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index 148a30c88..055d1cc12 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -1,6 +1,6 @@ use crate::events::HasKeyboardData; use crate::events::{ - AnimationData, CompositionData, HasTouchData, KeyboardData, MouseData, PointerData, TouchData, + AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData, TransitionData, WheelData, }; use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}; @@ -10,8 +10,8 @@ use keyboard_types::{Code, Key, Modifiers}; use std::str::FromStr; use wasm_bindgen::{JsCast, JsValue}; use web_sys::{ - AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, TouchEvent, - TransitionEvent, WheelEvent, + AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, Touch, + TouchEvent, TransitionEvent, WheelEvent, }; macro_rules! uncheck_convert { @@ -69,6 +69,20 @@ impl HasKeyboardData for KeyboardEvent { Code::from_str(self.code().as_str()).unwrap_or(Code::Unidentified) } + fn location(&self) -> keyboard_types::Location { + decode_key_location(self.location() as usize) + } + + fn is_auto_repeating(&self) -> bool { + self.repeat() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl ModifiersInteraction for KeyboardEvent { fn modifiers(&self) -> Modifiers { let mut modifiers = Modifiers::empty(); @@ -87,18 +101,6 @@ impl HasKeyboardData for KeyboardEvent { modifiers } - - fn location(&self) -> keyboard_types::Location { - decode_key_location(self.location() as usize) - } - - fn is_auto_repeating(&self) -> bool { - self.repeat() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } } impl HasDragData for MouseEvent {} @@ -108,10 +110,6 @@ impl InteractionLocation for MouseEvent { ClientPoint::new(self.client_x().into(), self.client_y().into()) } - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x().into(), self.offset_y().into()) - } - fn page_coordinates(&self) -> PagePoint { PagePoint::new(self.page_x().into(), self.page_y().into()) } @@ -121,6 +119,12 @@ impl InteractionLocation for MouseEvent { } } +impl InteractionElementOffset for MouseEvent { + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.offset_x().into(), self.offset_y().into()) + } +} + impl ModifiersInteraction for MouseEvent { fn modifiers(&self) -> Modifiers { let mut modifiers = Modifiers::empty(); @@ -158,21 +162,36 @@ impl HasMouseData for MouseEvent { } } -impl HasTouchData for TouchEvent { - fn alt_key(&self) -> bool { - self.alt_key() - } +impl ModifiersInteraction for TouchEvent { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); - fn ctrl_key(&self) -> bool { - self.ctrl_key() - } + if self.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } - fn meta_key(&self) -> bool { - self.meta_key() + modifiers } +} - fn shift_key(&self) -> bool { - self.shift_key() +impl crate::events::HasTouchData for TouchEvent { + fn touches(&self) -> Vec { + let touches = TouchEvent::touches(self); + let mut result = Vec::with_capacity(touches.length() as usize); + for i in 0..touches.length() { + let touch = touches.get(i).unwrap(); + result.push(TouchPoint::new(touch)); + } + result } fn as_any(&self) -> &dyn std::any::Any { @@ -180,6 +199,42 @@ impl HasTouchData for TouchEvent { } } +impl HasTouchPointData for Touch { + fn identifier(&self) -> i32 { + self.identifier() + } + + fn radius(&self) -> ScreenPoint { + ScreenPoint::new(self.radius_x().into(), self.radius_y().into()) + } + + fn rotation(&self) -> f64 { + self.rotation_angle() as f64 + } + + fn force(&self) -> f64 { + self.force() as f64 + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl InteractionLocation for Touch { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.client_x().into(), self.client_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.page_x().into(), self.page_y().into()) + } +} + impl HasPointerData for PointerEvent { fn pointer_id(&self) -> i32 { self.pointer_id() @@ -235,15 +290,17 @@ impl InteractionLocation for PointerEvent { ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) } - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x().into(), self.offset_y().into()) - } - fn page_coordinates(&self) -> PagePoint { PagePoint::new(self.page_x().into(), self.page_y().into()) } } +impl InteractionElementOffset for PointerEvent { + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.offset_x().into(), self.offset_y().into()) + } +} + impl ModifiersInteraction for PointerEvent { fn modifiers(&self) -> Modifiers { let mut modifiers = Modifiers::empty(); From 27f4b3f3451c1550e2df376fbf80559c024bb8ee Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 12 Sep 2023 17:45:07 -0500 Subject: [PATCH 46/56] add touches_changed and target_touches --- packages/html/src/events/touch.rs | 103 ++++++++++++++++++++++- packages/html/src/web_sys_bind/events.rs | 20 +++++ 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/packages/html/src/events/touch.rs b/packages/html/src/events/touch.rs index 83fdbb0b4..b30ace17c 100644 --- a/packages/html/src/events/touch.rs +++ b/packages/html/src/events/touch.rs @@ -19,6 +19,9 @@ impl std::fmt::Debug for TouchData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TouchData") .field("modifiers", &self.modifiers()) + .field("touches", &self.touches()) + .field("touches_changed", &self.touches_changed()) + .field("target_touches", &self.target_touches()) .finish() } } @@ -42,6 +45,16 @@ impl TouchData { self.inner.touches() } + /// Get the touches that have changed since the last event + pub fn touches_changed(&self) -> Vec { + self.inner.touches_changed() + } + + /// Get the touches that started and stayed on the element that triggered this event + pub fn target_touches(&self) -> Vec { + self.inner.target_touches() + } + /// Downcast this event to a concrete event type pub fn downcast(&self) -> Option<&T> { self.inner.as_any().downcast_ref::() @@ -63,6 +76,8 @@ pub struct SerializedTouchData { meta_key: bool, shift_key: bool, touches: Vec, + changed_touches: Vec, + target_touches: Vec, } #[cfg(feature = "serialize")] @@ -75,6 +90,8 @@ impl From<&TouchData> for SerializedTouchData { meta_key: modifiers.contains(Modifiers::META), shift_key: modifiers.contains(Modifiers::SHIFT), touches: data.touches().iter().map(|t| t.into()).collect(), + changed_touches: data.touches_changed().iter().map(|t| t.into()).collect(), + target_touches: data.target_touches().iter().map(|t| t.into()).collect(), } } } @@ -102,7 +119,23 @@ impl ModifiersInteraction for SerializedTouchData { #[cfg(feature = "serialize")] impl HasTouchData for SerializedTouchData { fn touches(&self) -> Vec { - Vec::new() + self.touches.clone().into_iter().map(Into::into).collect() + } + + fn touches_changed(&self) -> Vec { + self.changed_touches + .clone() + .into_iter() + .map(Into::into) + .collect() + } + + fn target_touches(&self) -> Vec { + self.target_touches + .clone() + .into_iter() + .map(Into::into) + .collect() } fn as_any(&self) -> &dyn std::any::Any { @@ -128,9 +161,15 @@ impl<'de> serde::Deserialize<'de> for TouchData { } pub trait HasTouchData: ModifiersInteraction + std::any::Any { - /// Get the pointers that are currently down + /// Get the touches that are currently down fn touches(&self) -> Vec; + /// Get the touches that have changed since the last event + fn touches_changed(&self) -> Vec; + + /// Get the touches that started and stayed on the element that triggered this event + fn target_touches(&self) -> Vec; + /// return self as Any fn as_any(&self) -> &dyn std::any::Any; } @@ -179,6 +218,20 @@ impl TouchPoint { } } +impl std::fmt::Debug for TouchPoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TouchPoint") + .field("client_coordinates", &self.client_coordinates()) + .field("page_coordinates", &self.page_coordinates()) + .field("screen_coordinates", &self.screen_coordinates()) + .field("identifier", &self.identifier()) + .field("force", &self.force()) + .field("radius", &self.radius()) + .field("rotation", &self.rotation()) + .finish() + } +} + impl InteractionLocation for TouchPoint { fn client_coordinates(&self) -> ClientPoint { self.inner.client_coordinates() @@ -251,6 +304,52 @@ impl From<&TouchPoint> for SerializedTouchPoint { } } +#[cfg(feature = "serialize")] +impl HasTouchPointData for SerializedTouchPoint { + /// A unique identifier for this touch point that will be the same for the duration of the touch + fn identifier(&self) -> i32 { + self.identifier + } + + /// the pressure of the touch + fn force(&self) -> f64 { + self.force + } + + /// the radius of the touch + fn radius(&self) -> ScreenPoint { + ScreenPoint::new(self.radius_x, self.radius_y) + } + + /// the rotation of the touch in degrees between 0 and 90 + fn rotation(&self) -> f64 { + self.rotation_angle + } + + /// return self as Any + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[cfg(feature = "serialize")] +impl InteractionLocation for SerializedTouchPoint { + /// Gets the coordinates of the event relative to the browser viewport. + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.client_x, self.client_y) + } + + /// Gets the coordinates of the event relative to the screen. + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.screen_x, self.screen_y) + } + + /// Gets the coordinates of the event relative to the page. + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.page_x, self.page_y) + } +} + impl_event! { TouchData; /// touchstart diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index 055d1cc12..b1e6c5341 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -194,6 +194,26 @@ impl crate::events::HasTouchData for TouchEvent { result } + fn touches_changed(&self) -> Vec { + let touches = self.changed_touches(); + let mut result = Vec::with_capacity(touches.length() as usize); + for i in 0..touches.length() { + let touch = touches.get(i).unwrap(); + result.push(TouchPoint::new(touch)); + } + result + } + + fn target_touches(&self) -> Vec { + let touches = self.target_touches(); + let mut result = Vec::with_capacity(touches.length() as usize); + for i in 0..touches.length() { + let touch = touches.get(i).unwrap(); + result.push(TouchPoint::new(touch)); + } + result + } + fn as_any(&self) -> &dyn std::any::Any { self } From fc5c53810b363c2f96c2ad7748e7eecfa79f695d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 13 Sep 2023 10:08:06 -0500 Subject: [PATCH 47/56] fix rink imports --- packages/rink/src/widgets/button.rs | 2 +- packages/rink/src/widgets/checkbox.rs | 2 +- packages/rink/src/widgets/slider.rs | 3 ++- packages/rink/src/widgets/text_like.rs | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/rink/src/widgets/button.rs b/packages/rink/src/widgets/button.rs index d07dd79f0..25f0e07ef 100644 --- a/packages/rink/src/widgets/button.rs +++ b/packages/rink/src/widgets/button.rs @@ -11,7 +11,7 @@ use dioxus_native_core::{ }; use shipyard::UniqueView; -use crate::FormData; +use crate::hooks::FormData; use super::{RinkWidget, WidgetContext}; diff --git a/packages/rink/src/widgets/checkbox.rs b/packages/rink/src/widgets/checkbox.rs index fe878e5b8..a8a8253a0 100644 --- a/packages/rink/src/widgets/checkbox.rs +++ b/packages/rink/src/widgets/checkbox.rs @@ -11,7 +11,7 @@ use dioxus_native_core::{ }; use shipyard::UniqueView; -use crate::FormData; +use crate::hooks::FormData; use super::{RinkWidget, WidgetContext}; diff --git a/packages/rink/src/widgets/slider.rs b/packages/rink/src/widgets/slider.rs index b3d6d24da..bb0639610 100644 --- a/packages/rink/src/widgets/slider.rs +++ b/packages/rink/src/widgets/slider.rs @@ -15,7 +15,8 @@ use dioxus_native_core::{ use shipyard::UniqueView; use super::{RinkWidget, WidgetContext}; -use crate::{query::get_layout, Event, EventData, FormData, Query}; +use crate::hooks::FormData; +use crate::{query::get_layout, Event, EventData, Query}; #[derive(Debug)] pub(crate) struct Slider { diff --git a/packages/rink/src/widgets/text_like.rs b/packages/rink/src/widgets/text_like.rs index 3231a9e29..770dd9d33 100644 --- a/packages/rink/src/widgets/text_like.rs +++ b/packages/rink/src/widgets/text_like.rs @@ -17,7 +17,8 @@ use dioxus_native_core::{ use shipyard::UniqueView; use taffy::geometry::Point; -use crate::{query::get_layout, Event, EventData, FormData, Query}; +use crate::hooks::FormData; +use crate::{query::get_layout, Event, EventData, Query}; use super::{RinkWidget, WidgetContext}; From c933bfaed3b5a02550f619fb7cf694d92c6d59d5 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 17 Sep 2023 20:04:40 -0500 Subject: [PATCH 48/56] fix formatting --- packages/fullstack/src/adapters/warp_adapter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fullstack/src/adapters/warp_adapter.rs b/packages/fullstack/src/adapters/warp_adapter.rs index 5afcb5077..316d70e56 100644 --- a/packages/fullstack/src/adapters/warp_adapter.rs +++ b/packages/fullstack/src/adapters/warp_adapter.rs @@ -143,7 +143,7 @@ pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl R let req = warp::hyper::Request::from_parts(parts, bytes.into()); service.run(req).await.map_err(|err| { tracing::error!("Server function error: {}", err); - + struct WarpServerFnError(String); impl std::fmt::Debug for WarpServerFnError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -152,7 +152,7 @@ pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl R } impl warp::reject::Reject for WarpServerFnError {} - + warp::reject::custom(WarpServerFnError(err.to_string())) }) } From ff7aca761761f5dd287a2ec4e243a604f4bb9e7e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 31 Oct 2023 13:56:27 -0500 Subject: [PATCH 49/56] fix todomvc --- examples/todomvc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/todomvc.rs b/examples/todomvc.rs index ff691b4cb..212c4d807 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -110,7 +110,7 @@ pub fn TodoHeader<'a>(cx: Scope<'a, TodoHeaderProps<'a>>) -> Element { value: "{draft}", autofocus: "true", oninput: move |evt| { - draft.set(evt.value.clone()); + draft.set(evt.value().clone()); }, onkeydown: move |evt| { if evt.key() == Key::Enter && !draft.is_empty() { From 334e7e6f22342e302fa3a02b68c9d149f3449222 Mon Sep 17 00:00:00 2001 From: Teague Lasser <58143782+kraemahz@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:41:05 -0800 Subject: [PATCH 50/56] Updated packages and comments referencing old CLI location (#1786) --- examples/tailwind/README.md | 2 +- packages/cli/src/assets/autoreload.js | 4 ++-- packages/hot-reload/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/tailwind/README.md b/examples/tailwind/README.md index 552ab402f..95d01f408 100644 --- a/examples/tailwind/README.md +++ b/examples/tailwind/README.md @@ -7,7 +7,7 @@ This example shows how an app might be styled with TailwindCSS. 1. Install the Dioxus CLI: ```bash -cargo install --git https://github.com/DioxusLabs/cli +cargo install dioxus-cli ``` 2. Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm diff --git a/packages/cli/src/assets/autoreload.js b/packages/cli/src/assets/autoreload.js index fffb3e26c..cc8fef166 100644 --- a/packages/cli/src/assets/autoreload.js +++ b/packages/cli/src/assets/autoreload.js @@ -1,5 +1,5 @@ // Dioxus-CLI -// https://github.com/DioxusLabs/cli +// https://github.com/DioxusLabs/dioxus/tree/master/packages/cli (function () { var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; @@ -22,4 +22,4 @@ } }; ws.onclose = reload_upon_connect; -})() \ No newline at end of file +})() diff --git a/packages/hot-reload/README.md b/packages/hot-reload/README.md index d7418d4b1..d2428e9e9 100644 --- a/packages/hot-reload/README.md +++ b/packages/hot-reload/README.md @@ -68,7 +68,7 @@ rsx! { ## Usage -> This crate implements hot reloading for native compilation targets not WASM. For hot relaoding with the web renderer, see the [dioxus-cli](https://github.com/DioxusLabs/cli) project. +> This crate implements hot reloading for native compilation targets not WASM. For hot relaoding with the web renderer, see the [dioxus-cli](https://github.com/DioxusLabs/dioxus/tree/master/packages/cli) project. Add this to the top of your main function on any renderer that supports hot reloading to start the hot reloading server: From 0b40878fce155884a3b6b27986be1ef477138500 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 4 Jan 2024 19:11:32 -0600 Subject: [PATCH 51/56] fix cargo check --- examples/crm.rs | 82 ++++++++++----------------------- packages/core/src/scopes.rs | 1 - packages/desktop/src/webview.rs | 1 - packages/web/src/dom.rs | 7 +-- 4 files changed, 28 insertions(+), 63 deletions(-) diff --git a/examples/crm.rs b/examples/crm.rs index b0f1aabe2..818c190bb 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -35,14 +35,16 @@ fn App(cx: Scope) -> Element { rel: "stylesheet", href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css", integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", - crossorigin: "anonymous", + crossorigin: "anonymous" } - style { " + style { + " .red {{ background-color: rgb(202, 60, 60) !important; }} - " } + " + } h1 { "Dioxus CRM Example" } @@ -57,22 +59,14 @@ fn ClientList(cx: Scope) -> Element { cx.render(rsx! { h2 { "List of Clients" } - Link { - to: Route::ClientAdd {}, - class: "pure-button pure-button-primary", - "Add Client" - } - Link { - to: Route::Settings {}, - class: "pure-button", - "Settings" - } + Link { to: Route::ClientAdd {}, class: "pure-button pure-button-primary", "Add Client" } + Link { to: Route::Settings {}, class: "pure-button", "Settings" } clients.read().iter().map(|client| rsx! { div { class: "client", style: "margin-bottom: 50px", - + p { "Name: {client.first_name} {client.last_name}" } p { "Description: {client.description}" } } @@ -94,23 +88,18 @@ fn ClientAdd(cx: Scope) -> Element { class: "pure-form pure-form-aligned", onsubmit: move |_| { let mut clients = clients.write(); - - clients.push(Client { - first_name: first_name.to_string(), - last_name: last_name.to_string(), - description: description.to_string(), - }); - + clients + .push(Client { + first_name: first_name.to_string(), + last_name: last_name.to_string(), + description: description.to_string(), + }); dioxus_router::router().push(Route::ClientList {}); }, fieldset { - div { - class: "pure-control-group", - label { - "for": "first_name", - "First Name" - } + div { class: "pure-control-group", + label { "for": "first_name", "First Name" } input { id: "first_name", "type": "text", @@ -121,12 +110,8 @@ fn ClientAdd(cx: Scope) -> Element { } } - div { - class: "pure-control-group", - label { - "for": "last_name", - "Last Name" - } + div { class: "pure-control-group", + label { "for": "last_name", "Last Name" } input { id: "last_name", "type": "text", @@ -137,12 +122,8 @@ fn ClientAdd(cx: Scope) -> Element { } } - div { - class: "pure-control-group", - label { - "for": "description", - "Description" - } + div { class: "pure-control-group", + label { "for": "description", "Description" } textarea { id: "description", placeholder: "Description…", @@ -151,22 +132,11 @@ fn ClientAdd(cx: Scope) -> Element { } } - div { - class: "pure-controls", - button { - "type": "submit", - class: "pure-button pure-button-primary", - "Save" - } - Link { - to: Route::ClientList {}, - class: "pure-button pure-button-primary red", - "Cancel" - } + div { class: "pure-controls", + button { "type": "submit", class: "pure-button pure-button-primary", "Save" } + Link { to: Route::ClientList {}, class: "pure-button pure-button-primary red", "Cancel" } } } - - } }) } @@ -187,10 +157,6 @@ fn Settings(cx: Scope) -> Element { "Remove all Clients" } - Link { - to: Route::ClientList {}, - class: "pure-button", - "Go back" - } + Link { to: Route::ClientList {}, class: "pure-button", "Go back" } }) } diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 1a26d3847..ef4a5771f 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -2,7 +2,6 @@ use crate::{ any_props::AnyProps, any_props::VProps, bump_frame::BumpFrame, - innerlude::ErrorBoundary, innerlude::{DynamicNode, EventHandler, VComponent, VNodeId, VText}, lazynodes::LazyNodes, nodes::{IntoAttributeValue, IntoDynNode, RenderReturn}, diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index de5ff121c..f4bc4c4ed 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -5,7 +5,6 @@ use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget}; pub use wry; pub use wry::application as tao; use wry::application::window::Window; -use wry::http::Response; use wry::webview::{WebContext, WebView, WebViewBuilder}; pub(crate) fn build( diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 575fa1260..118b3eddb 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -6,10 +6,11 @@ //! - tests to ensure dyn_into works for various event types. //! - Partial delegation? +use dioxus_interpreter_js::get_node; use dioxus_core::{ BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode, }; -use dioxus_html::{event_bubbles, PlatformEventData}; +use dioxus_html::{event_bubbles, PlatformEventData, MountedData}; use dioxus_interpreter_js::{minimal_bindings, save_template, Channel}; use futures_channel::mpsc; use rustc_hash::FxHashMap; @@ -267,12 +268,12 @@ impl WebsysDom { let node = get_node(id.0 as u32); if let Some(element) = node.dyn_ref::() { let data: MountedData = element.into(); - let data = Rc::new(data); + let data = Box::new(data); let _ = self.event_channel.unbounded_send(UiEvent { name: "mounted".to_string(), bubbles: false, element: id, - data, + data: PlatformEventData::new(data), }); } } From 0388a5c1f425b4522c37c3d73a21b9a9ee7c880d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 4 Jan 2024 19:21:06 -0600 Subject: [PATCH 52/56] fix formatting --- examples/crm.rs | 2 +- packages/core/src/virtual_dom.rs | 4 +- packages/desktop/headless_tests/events.rs | 62 +++++++++++++++++------ packages/desktop/src/lib.rs | 2 +- packages/liveview/src/pool.rs | 2 +- packages/router/examples/simple_routes.rs | 2 +- packages/web/src/dom.rs | 4 +- 7 files changed, 55 insertions(+), 23 deletions(-) diff --git a/examples/crm.rs b/examples/crm.rs index 818c190bb..8af49b401 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -66,7 +66,7 @@ fn ClientList(cx: Scope) -> Element { div { class: "client", style: "margin-bottom: 50px", - + p { "Name: {client.first_name} {client.last_name}" } p { "Description: {client.description}" } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index f469cd9e2..9327e8f29 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -16,7 +16,9 @@ use crate::{ use futures_util::{pin_mut, StreamExt}; use rustc_hash::{FxHashMap, FxHashSet}; use slab::Slab; -use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, ptr::NonNull, rc::Rc, sync::Arc}; +use std::{ + any::Any, cell::Cell, collections::BTreeSet, future::Future, ptr::NonNull, rc::Rc, sync::Arc, +}; /// A virtual node system that progresses user events and diffs UI trees. /// diff --git a/packages/desktop/headless_tests/events.rs b/packages/desktop/headless_tests/events.rs index a1744fb96..f957dd321 100644 --- a/packages/desktop/headless_tests/events.rs +++ b/packages/desktop/headless_tests/events.rs @@ -221,7 +221,7 @@ fn app(cx: Scope) -> Element { desktop_context.close(); } - cx.render(rsx! { + render! { div { button { id: "button", @@ -229,14 +229,11 @@ fn app(cx: Scope) -> Element { println!("{:?}", event.data); assert!(event.data.modifiers().is_empty()); assert!(event.data.held_buttons().is_empty()); - assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Primary)); - received_events.modify(|x| *x + 1) - }, assert_eq!( event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Primary), ); - recieved_events.modify(|x| *x + 1) + received_events.modify(|x| *x + 1) } } div { @@ -244,7 +241,12 @@ fn app(cx: Scope) -> Element { onmousemove: move |event| { println!("{:?}", event.data); assert!(event.data.modifiers().is_empty()); - assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary)); + assert!( + event + .data + .held_buttons() + .contains(dioxus_html::input_data::MouseButton::Secondary), + ); received_events.modify(|x| *x + 1) } } @@ -253,8 +255,16 @@ fn app(cx: Scope) -> Element { onclick: move |event| { println!("{:?}", event.data); assert!(event.data.modifiers().is_empty()); - assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary)); - assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Secondary)); + assert!( + event + .data + .held_buttons() + .contains(dioxus_html::input_data::MouseButton::Secondary), + ); + assert_eq!( + event.data.trigger_button(), + Some(dioxus_html::input_data::MouseButton::Secondary), + ); received_events.modify(|x| *x + 1) } } @@ -263,9 +273,19 @@ fn app(cx: Scope) -> Element { ondoubleclick: move |event| { println!("{:?}", event.data); assert!(event.data.modifiers().is_empty()); - assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary)); - assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary)); - assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Secondary)); + assert!( + event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary), + ); + assert!( + event + .data + .held_buttons() + .contains(dioxus_html::input_data::MouseButton::Secondary), + ); + assert_eq!( + event.data.trigger_button(), + Some(dioxus_html::input_data::MouseButton::Secondary), + ); received_events.modify(|x| *x + 1) } } @@ -274,8 +294,16 @@ fn app(cx: Scope) -> Element { onmousedown: move |event| { println!("{:?}", event.data); assert!(event.data.modifiers().is_empty()); - assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary)); - assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Secondary)); + assert!( + event + .data + .held_buttons() + .contains(dioxus_html::input_data::MouseButton::Secondary), + ); + assert_eq!( + event.data.trigger_button(), + Some(dioxus_html::input_data::MouseButton::Secondary), + ); received_events.modify(|x| *x + 1) } } @@ -285,7 +313,10 @@ fn app(cx: Scope) -> Element { println!("{:?}", event.data); assert!(event.data.modifiers().is_empty()); assert!(event.data.held_buttons().is_empty()); - assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Primary)); + assert_eq!( + event.data.trigger_button(), + Some(dioxus_html::input_data::MouseButton::Primary), + ); received_events.modify(|x| *x + 1) } } @@ -312,7 +343,6 @@ fn app(cx: Scope) -> Element { assert_eq!(event.data.location(), Location::Standard); assert!(event.data.is_auto_repeating()); received_events.modify(|x| *x + 1) - } } input { @@ -354,5 +384,5 @@ fn app(cx: Scope) -> Element { } } } - }) + } } diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 83e8105bc..519c198a9 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -28,8 +28,8 @@ pub use desktop_context::{ }; use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers}; use dioxus_core::*; -use dioxus_html::{native_bind::NativeFileEngine, HtmlEvent}; use dioxus_html::{event_bubbles, FileEngine, HasFormData, MountedData, PlatformEventData}; +use dioxus_html::{native_bind::NativeFileEngine, HtmlEvent}; use dioxus_interpreter_js::binary_protocol::Channel; use element::DesktopElement; use eval::init_eval; diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index 130ac9b31..6fac0abfe 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -6,7 +6,7 @@ use crate::{ LiveViewError, }; use dioxus_core::{prelude::*, BorrowedAttributeValue, Mutations}; -use dioxus_html::{event_bubbles,EventData, HtmlEvent, MountedData, PlatformEventData}; +use dioxus_html::{event_bubbles, EventData, HtmlEvent, MountedData, PlatformEventData}; use dioxus_interpreter_js::binary_protocol::Channel; use futures_util::{pin_mut, SinkExt, StreamExt}; use rustc_hash::FxHashMap; diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index 74ccded8e..6c33c275b 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -127,7 +127,7 @@ fn Route3(cx: Scope, dynamic: String) -> Element { .collect::>(); let navigator = use_navigator(cx); - + render! { input { oninput: move |evt| { diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 118b3eddb..da46e5012 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -6,11 +6,11 @@ //! - tests to ensure dyn_into works for various event types. //! - Partial delegation? -use dioxus_interpreter_js::get_node; use dioxus_core::{ BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode, }; -use dioxus_html::{event_bubbles, PlatformEventData, MountedData}; +use dioxus_html::{event_bubbles, MountedData, PlatformEventData}; +use dioxus_interpreter_js::get_node; use dioxus_interpreter_js::{minimal_bindings, save_template, Channel}; use futures_channel::mpsc; use rustc_hash::FxHashMap; From fae81e2364ba7bdd75670cc1af609dcfdd149251 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 4 Jan 2024 19:32:00 -0600 Subject: [PATCH 53/56] fix event tests in core --- examples/compose.rs | 2 +- packages/core/tests/event_propagation.rs | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/examples/compose.rs b/examples/compose.rs index 23f703bd2..133192281 100644 --- a/examples/compose.rs +++ b/examples/compose.rs @@ -26,7 +26,7 @@ fn app(cx: Scope) -> Element { button { onclick: move |_| { let dom = VirtualDom::new_with_props(compose, ComposeProps { app_tx: tx.clone() }); - window.new_window(dom, Default::default()); + dioxus_desktop::window().new_window(dom, Default::default()); }, "Click to compose a new email" } diff --git a/packages/core/tests/event_propagation.rs b/packages/core/tests/event_propagation.rs index d694b1ff1..1c294aa37 100644 --- a/packages/core/tests/event_propagation.rs +++ b/packages/core/tests/event_propagation.rs @@ -10,7 +10,7 @@ fn events_propagate() { _ = dom.rebuild(); // Top-level click is registered - dom.handle_event("click", Rc::new(MouseData::default()), ElementId(1), true); + dom.handle_event("click", Rc::new(PlatformEventData::new(Box::::default())), ElementId(1), true); assert_eq!(*CLICKS.lock().unwrap(), 1); // break reference.... @@ -20,7 +20,7 @@ fn events_propagate() { } // Lower click is registered - dom.handle_event("click", Rc::new(MouseData::default()), ElementId(2), true); + dom.handle_event("click", Rc::new(PlatformEventData::new(Box::::default())), ElementId(2), true); assert_eq!(*CLICKS.lock().unwrap(), 3); // break reference.... @@ -30,14 +30,13 @@ fn events_propagate() { } // Stop propagation occurs - dom.handle_event("click", Rc::new(MouseData::default()), ElementId(2), true); + dom.handle_event("click", Rc::new(PlatformEventData::new(Box::::default())), ElementId(2), true); assert_eq!(*CLICKS.lock().unwrap(), 3); } fn app(cx: Scope) -> Element { render! { - div { - onclick: move |_| { + div { onclick: move |_| { println!("top clicked"); *CLICKS.lock().unwrap() += 1; }, @@ -53,17 +52,14 @@ fn app(cx: Scope) -> Element { fn problematic_child(cx: Scope) -> Element { render! { - button { - onclick: move |evt| { + button { onclick: move |evt| { println!("bottom clicked"); let mut clicks = CLICKS.lock().unwrap(); - if *clicks == 3 { evt.stop_propagation(); } else { *clicks += 1; } - } - } + } } } } From 7ffb204eacebcabc5dcd52497b1fb8b5d3f1a1d6 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 4 Jan 2024 19:36:10 -0600 Subject: [PATCH 54/56] fix event tests in core --- examples/compose.rs | 2 +- packages/core/tests/event_propagation.rs | 18 ++++++++---------- packages/ssr/Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/examples/compose.rs b/examples/compose.rs index 23f703bd2..133192281 100644 --- a/examples/compose.rs +++ b/examples/compose.rs @@ -26,7 +26,7 @@ fn app(cx: Scope) -> Element { button { onclick: move |_| { let dom = VirtualDom::new_with_props(compose, ComposeProps { app_tx: tx.clone() }); - window.new_window(dom, Default::default()); + dioxus_desktop::window().new_window(dom, Default::default()); }, "Click to compose a new email" } diff --git a/packages/core/tests/event_propagation.rs b/packages/core/tests/event_propagation.rs index d694b1ff1..d9d96ac32 100644 --- a/packages/core/tests/event_propagation.rs +++ b/packages/core/tests/event_propagation.rs @@ -6,11 +6,13 @@ static CLICKS: Mutex = Mutex::new(0); #[test] fn events_propagate() { + set_event_converter(Box::new(dioxus_html::SerializedHtmlEventConverter)); + let mut dom = VirtualDom::new(app); _ = dom.rebuild(); // Top-level click is registered - dom.handle_event("click", Rc::new(MouseData::default()), ElementId(1), true); + dom.handle_event("click", Rc::new(PlatformEventData::new(Box::::default())), ElementId(1), true); assert_eq!(*CLICKS.lock().unwrap(), 1); // break reference.... @@ -20,7 +22,7 @@ fn events_propagate() { } // Lower click is registered - dom.handle_event("click", Rc::new(MouseData::default()), ElementId(2), true); + dom.handle_event("click", Rc::new(PlatformEventData::new(Box::::default())), ElementId(2), true); assert_eq!(*CLICKS.lock().unwrap(), 3); // break reference.... @@ -30,14 +32,13 @@ fn events_propagate() { } // Stop propagation occurs - dom.handle_event("click", Rc::new(MouseData::default()), ElementId(2), true); + dom.handle_event("click", Rc::new(PlatformEventData::new(Box::::default())), ElementId(2), true); assert_eq!(*CLICKS.lock().unwrap(), 3); } fn app(cx: Scope) -> Element { render! { - div { - onclick: move |_| { + div { onclick: move |_| { println!("top clicked"); *CLICKS.lock().unwrap() += 1; }, @@ -53,17 +54,14 @@ fn app(cx: Scope) -> Element { fn problematic_child(cx: Scope) -> Element { render! { - button { - onclick: move |evt| { + button { onclick: move |evt| { println!("bottom clicked"); let mut clicks = CLICKS.lock().unwrap(); - if *clicks == 3 { evt.stop_propagation(); } else { *clicks += 1; } - } - } + } } } } diff --git a/packages/ssr/Cargo.toml b/packages/ssr/Cargo.toml index 71f5b26a0..1a8d475ee 100644 --- a/packages/ssr/Cargo.toml +++ b/packages/ssr/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr"] [dependencies] dioxus-core = { workspace = true, features = ["serialize"] } -dioxus-html = { workspace = true } +dioxus-html = { workspace = true, features = ["eval"]} askama_escape = "0.10.3" thiserror = "1.0.23" rustc-hash = "1.1.0" From bb8341241d085c57e9e6d1c4a85ae24fb912504d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 4 Jan 2024 19:57:02 -0600 Subject: [PATCH 55/56] bump sledgehammer --- packages/interpreter/Cargo.toml | 2 +- packages/interpreter/src/sledgehammer_bindings.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml index bac04d496..63113872d 100644 --- a/packages/interpreter/Cargo.toml +++ b/packages/interpreter/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] wasm-bindgen = { workspace = true, optional = true } js-sys = { version = "0.3.56", optional = true } web-sys = { version = "0.3.56", optional = true, features = ["Element", "Node"] } -sledgehammer_bindgen = { versio = "0.3.0", default-features = false, optional = true } +sledgehammer_bindgen = { versio = "0.3.1", default-features = false, optional = true } sledgehammer_utils = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } diff --git a/packages/interpreter/src/sledgehammer_bindings.rs b/packages/interpreter/src/sledgehammer_bindings.rs index a42824e15..23831f3e6 100644 --- a/packages/interpreter/src/sledgehammer_bindings.rs +++ b/packages/interpreter/src/sledgehammer_bindings.rs @@ -11,7 +11,7 @@ pub const SLEDGEHAMMER_JS: &str = GENERATED_JS; #[cfg(feature = "web")] #[bindgen(module)] mod js { - const JS_FILE: &str = "./packages/interpreter/src/common.js"; + const JS_FILE: &str = "./src/common.js"; const JS: &str = r#" class ListenerMap { constructor(root) { @@ -260,8 +260,8 @@ pub mod binary_protocol { #[bindgen] mod protocol_js { - const JS_FILE: &str = "./packages/interpreter/src/interpreter.js"; - const JS_FILE: &str = "./packages/interpreter/src/common.js"; + const JS_FILE: &str = "./src/interpreter.js"; + const JS_FILE: &str = "./src/common.js"; fn mount_to_root() { "{AppendChildren(root, stack.length-1);}" From 5bd0abcfddee2bb65511d67ab2ca5b10113d5b68 Mon Sep 17 00:00:00 2001 From: Pascal Date: Fri, 5 Jan 2024 14:46:41 +0000 Subject: [PATCH 56/56] fix typo which resolves #1789 (#1790) --- packages/interpreter/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml index 63113872d..31e31a37f 100644 --- a/packages/interpreter/Cargo.toml +++ b/packages/interpreter/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] wasm-bindgen = { workspace = true, optional = true } js-sys = { version = "0.3.56", optional = true } web-sys = { version = "0.3.56", optional = true, features = ["Element", "Node"] } -sledgehammer_bindgen = { versio = "0.3.1", default-features = false, optional = true } +sledgehammer_bindgen = { version = "0.3.1", default-features = false, optional = true } sledgehammer_utils = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"], optional = true }