From d7e4fcda801aea7d455d1f281afc520375c97879 Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Thu, 12 May 2022 10:54:38 +0300 Subject: [PATCH 01/39] Implement idiomatic accessors for WheelData --- examples/{mouse_event.rs => event_mouse.rs} | 0 examples/event_wheel.rs | 39 +++++++++++++ packages/html/src/events.rs | 56 ++++++++++++++++++- packages/html/src/geometry.rs | 61 +++++++++++++++++++++ packages/tui/src/hooks.rs | 20 +++---- 5 files changed, 165 insertions(+), 11 deletions(-) rename examples/{mouse_event.rs => event_mouse.rs} (100%) create mode 100644 examples/event_wheel.rs diff --git a/examples/mouse_event.rs b/examples/event_mouse.rs similarity index 100% rename from examples/mouse_event.rs rename to examples/event_mouse.rs diff --git a/examples/event_wheel.rs b/examples/event_wheel.rs new file mode 100644 index 000000000..763c64d02 --- /dev/null +++ b/examples/event_wheel.rs @@ -0,0 +1,39 @@ +use dioxus::prelude::*; +use dioxus_core::UiEvent; +use dioxus_html::on::WheelData; + +fn main() { + dioxus::desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + let delta = use_state(&cx, || "".to_string()); + + let container_style = r#" + display: flex; + flex-direction: column; + align-items: center; + "#; + let rect_style = r#" + background: deepskyblue; + height: 50vh; + width: 50vw; + "#; + + let handle_event = move |event: UiEvent| { + let wheel_data = event.data; + delta.set(format!("{:?}", wheel_data.delta())); + }; + + cx.render(rsx! ( + div { + style: "{container_style}", + "Scroll mouse wheel over rectangle:", + div { + style: "{rect_style}", + onwheel: handle_event, + } + div {"Delta: {delta}"}, + } + )) +} diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 6d7cbe06e..38ae75407 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -5,10 +5,14 @@ use dioxus_core::*; pub mod on { //! Input events and associated data - use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; + use crate::geometry::{ + ClientPoint, Coordinates, ElementPoint, LinesVector, PagePoint, PagesVector, PixelsVector, + ScreenPoint, WheelDelta, + }; use crate::input_data::{ decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet, }; + use euclid::UnknownUnit; use keyboard_types::Modifiers; use std::collections::HashMap; @@ -740,12 +744,62 @@ pub mod on { #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone)] 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, } + impl WheelData { + 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, + } + } + + 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, + } + } + + #[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), + } + } + } + pub type MediaEvent = UiEvent; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone)] diff --git a/packages/html/src/geometry.rs b/packages/html/src/geometry.rs index 5a7c8112e..97ab068d3 100644 --- a/packages/html/src/geometry.rs +++ b/packages/html/src/geometry.rs @@ -25,6 +25,67 @@ pub struct PageSpace; /// A point in PageSpace pub type PagePoint = Point2D; +/// A pixel unit: one unit corresponds to 1 pixel +pub struct Pixels; +/// A vector expressed in Pixels +pub type PixelsVector = Vector3D; + +/// A unit in terms of Lines +/// +/// One unit is relative to the size of one line +pub struct Lines; +/// A vector expressed in Lines +pub type LinesVector = Vector3D; + +/// A unit in terms of Screens: +/// +/// One unit is relative to the size of a page +pub struct Pages; +/// A vector expressed in Pages +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)] +pub enum WheelDelta { + /// Movement in Pixels + Pixels(PixelsVector), + /// Movement in Lines + Lines(LinesVector), + /// Movement in Pages + Pages(PagesVector), +} + +impl WheelDelta { + /// 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)) + } + + /// Convenience function for constructing a WheelDelta with line units + pub fn lines(x: f64, y: f64, z: f64) -> Self { + WheelDelta::Lines(LinesVector::new(x, y, z)) + } + + /// Convenience function for constructing a WheelDelta with page units + pub fn pages(x: f64, y: f64, z: f64) -> Self { + WheelDelta::Pages(PagesVector::new(x, y, z)) + } + + pub fn is_zero(&self) -> bool { + self.strip_units() == Vector3D::new(0., 0., 0.) + } + + pub fn strip_units(&self) -> Vector3D { + match self { + WheelDelta::Pixels(v) => v.cast_unit(), + WheelDelta::Lines(v) => v.cast_unit(), + WheelDelta::Pages(v) => v.cast_unit(), + } + } +} + /// Coordinates of a point in the app's interface pub struct Coordinates { screen: ScreenPoint, diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 5481d6b1c..bb32c1375 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -5,7 +5,9 @@ use dioxus_core::*; use fxhash::{FxHashMap, FxHashSet}; use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D}; -use dioxus_html::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; +use dioxus_html::geometry::{ + ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint, WheelDelta, +}; use dioxus_html::input_data::keyboard_types::Modifiers; use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons; use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet}; @@ -248,7 +250,10 @@ impl InnerInputState { // a mouse button is released if a button was down and is now not down let was_released = !(previous_buttons - mouse_data.held_buttons()).is_empty(); - let wheel_delta = self.wheel.as_ref().map_or(0.0, |w| w.delta_y); + let was_scrolled = self + .wheel + .as_ref() + .map_or(false, |data| !data.delta().is_zero()); let wheel_data = &self.wheel; { @@ -412,7 +417,7 @@ impl InnerInputState { { // wheel if let Some(w) = wheel_data { - if wheel_delta != 0.0 { + if was_scrolled { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("wheel") { let node_layout = @@ -638,13 +643,8 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> { }; let get_wheel_data = |up| { - // from https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent - EventData::Wheel(WheelData { - delta_mode: 0x01, - delta_x: 0.0, - delta_y: if up { -1.0 } else { 1.0 }, - delta_z: 0.0, - }) + let y = if up { -1.0 } else { 1.0 }; + EventData::Wheel(WheelData::new(WheelDelta::lines(0., y, 0.))) }; match m.kind { From 1bb8b04d875f10871af4a4136ca4a05df2c2156f Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Thu, 12 May 2022 11:03:51 +0300 Subject: [PATCH 02/39] Update examples to avoid deprecated fields --- examples/tui_border.rs | 2 +- examples/tui_hover.rs | 8 ++++---- examples/tui_keys.rs | 2 +- examples/tui_text.rs | 2 +- packages/html/src/web_sys_bind/events.rs | 7 +------ 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/examples/tui_border.rs b/examples/tui_border.rs index 041df4a8b..3e1c01849 100644 --- a/examples/tui_border.rs +++ b/examples/tui_border.rs @@ -14,7 +14,7 @@ fn app(cx: Scope) -> Element { justify_content: "center", align_items: "center", background_color: "hsl(248, 53%, 58%)", - onwheel: move |w| radius.modify(|r| (r + w.delta_y as i8).abs()), + onwheel: move |w| radius.modify(|r| (r + w.delta().strip_units().y as i8).abs()), border_style: "solid none solid double", border_width: "thick", diff --git a/examples/tui_hover.rs b/examples/tui_hover.rs index 136213c25..5c3b2d832 100644 --- a/examples/tui_hover.rs +++ b/examples/tui_hover.rs @@ -65,7 +65,7 @@ fn app(cx: Scope) -> Element { onmouseenter: move |m| q1_color.set([get_brightness(m.data), 0, 0]), onmousedown: move |m| q1_color.set([get_brightness(m.data), 0, 0]), onmouseup: move |m| q1_color.set([get_brightness(m.data), 0, 0]), - onwheel: move |w| q1_color.set([q1_color[0] + (10.0*w.delta_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" @@ -79,7 +79,7 @@ fn app(cx: Scope) -> Element { onmouseenter: move |m| q2_color.set([get_brightness(m.data); 3]), onmousedown: move |m| q2_color.set([get_brightness(m.data); 3]), onmouseup: move |m| q2_color.set([get_brightness(m.data); 3]), - onwheel: move |w| q2_color.set([q2_color[0] + (10.0*w.delta_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" @@ -99,7 +99,7 @@ fn app(cx: Scope) -> Element { onmouseenter: move |m| q3_color.set([0, get_brightness(m.data), 0]), onmousedown: move |m| q3_color.set([0, get_brightness(m.data), 0]), onmouseup: move |m| q3_color.set([0, get_brightness(m.data), 0]), - onwheel: move |w| q3_color.set([0, q3_color[1] + (10.0*w.delta_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" @@ -113,7 +113,7 @@ fn app(cx: Scope) -> Element { onmouseenter: move |m| q4_color.set([0, 0, get_brightness(m.data)]), onmousedown: move |m| q4_color.set([0, 0, get_brightness(m.data)]), onmouseup: move |m| q4_color.set([0, 0, get_brightness(m.data)]), - onwheel: move |w| q4_color.set([0, 0, q4_color[2] + (10.0*w.delta_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" diff --git a/examples/tui_keys.rs b/examples/tui_keys.rs index 138eec2fe..b4a7ce5d1 100644 --- a/examples/tui_keys.rs +++ b/examples/tui_keys.rs @@ -35,7 +35,7 @@ fn app(cx: Scope) -> Element { key.set(format!("{:?} repeating: {:?}", evt.key, evt.repeat)); }, onwheel: move |evt: WheelEvent| { - count.set(count + evt.data.delta_y as i64); + count.set(count + evt.data.delta().strip_units().y as i64); }, ondrag: move |evt: MouseEvent| { mouse.set(evt.data.screen_coordinates()); diff --git a/examples/tui_text.rs b/examples/tui_text.rs index b8f04d99a..a2992a8a0 100644 --- a/examples/tui_text.rs +++ b/examples/tui_text.rs @@ -12,7 +12,7 @@ fn app(cx: Scope) -> Element { width: "100%", height: "100%", flex_direction: "column", - onwheel: move |evt| alpha.set((**alpha + evt.data.delta_y as i64).min(100).max(0)), + onwheel: move |evt| alpha.set((**alpha + evt.data.delta().strip_units().y as i64).min(100).max(0)), p { background_color: "black", diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index a69ac5fef..fbbb34d9e 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -146,12 +146,7 @@ impl From<&PointerEvent> for PointerData { impl From<&WheelEvent> for WheelData { fn from(e: &WheelEvent) -> Self { - Self { - delta_x: e.delta_x(), - delta_y: e.delta_y(), - delta_z: e.delta_z(), - delta_mode: e.delta_mode(), - } + WheelData::from_web_attributes(e.delta_mode(), e.delta_x(), e.delta_y(), e.delta_z()) } } From addb0703b10a477616729075d633079490bdfbda Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Thu, 12 May 2022 11:12:34 +0300 Subject: [PATCH 03/39] Write docs for wheel events --- packages/html/src/events.rs | 3 +++ packages/html/src/geometry.rs | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 38ae75407..4cb7c6fb1 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -755,6 +755,7 @@ pub mod on { } 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::()), @@ -771,6 +772,7 @@ pub mod on { } } + /// Construct from the attributes of the web wheel event pub fn from_web_attributes( delta_mode: u32, delta_x: f64, @@ -786,6 +788,7 @@ pub mod on { } } + /// The amount of wheel movement #[allow(deprecated)] pub fn delta(&self) -> WheelDelta { let x = self.delta_x; diff --git a/packages/html/src/geometry.rs b/packages/html/src/geometry.rs index 97ab068d3..027922a85 100644 --- a/packages/html/src/geometry.rs +++ b/packages/html/src/geometry.rs @@ -48,6 +48,7 @@ pub type PagesVector = Vector3D; /// /// This may be expressed in Pixels, Lines or Pages #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum WheelDelta { /// Movement in Pixels Pixels(PixelsVector), @@ -73,10 +74,18 @@ impl WheelDelta { WheelDelta::Pages(PagesVector::new(x, y, z)) } + /// Returns true iff there is no wheel movement + /// + /// i.e. the x, y and z delta is zero (disregards units) pub fn is_zero(&self) -> bool { self.strip_units() == Vector3D::new(0., 0., 0.) } + /// A Vector3D proportional to the amount scrolled + /// + /// Note that this disregards the 3 possible units: this could be expressed in terms of pixels, lines, or pages. + /// + /// In most cases, to properly handle scrolling, you should handle all 3 possible enum variants instead of stripping units. Otherwise, if you assume that the units will always be pixels, the user may experience some unexpectedly slow scrolling if their mouse/OS sends values expressed in lines or pages. pub fn strip_units(&self) -> Vector3D { match self { WheelDelta::Pixels(v) => v.cast_unit(), From 7ee02bc0d8804b25a01f99157ad9c9137cada033 Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Thu, 12 May 2022 11:38:27 +0300 Subject: [PATCH 04/39] Enable `euclid/serde` when needed --- 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 ac5cd340f..046ab9acc 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -40,5 +40,5 @@ features = [ [features] default = [] -serialize = ["serde", "serde_repr"] +serialize = ["serde", "serde_repr", "euclid/serde"] wasm-bind = ["web-sys", "wasm-bindgen"] From ed34e339fcfa9dd266a82778659f4ce90494298e Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Thu, 12 May 2022 14:10:25 +0300 Subject: [PATCH 05/39] Tui: construct keyboard data with new api --- packages/html/Cargo.toml | 2 +- packages/html/src/events.rs | 144 +++++++++---- packages/html/src/input_data.rs | 23 +++ packages/tui/src/hooks.rs | 348 ++++++++++++++++++++------------ 4 files changed, 342 insertions(+), 175 deletions(-) diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 046ab9acc..8464189ea 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -40,5 +40,5 @@ features = [ [features] default = [] -serialize = ["serde", "serde_repr", "euclid/serde"] +serialize = ["serde", "serde_repr", "euclid/serde", "keyboard-types/serde"] wasm-bind = ["web-sys", "wasm-bindgen"] diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 4cb7c6fb1..373e46414 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -10,11 +10,15 @@ pub mod on { ScreenPoint, WheelDelta, }; use crate::input_data::{ - decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet, + decode_key_location, decode_mouse_button_set, encode_key_location, encode_mouse_button_set, + MouseButton, MouseButtonSet, }; use euclid::UnknownUnit; - use keyboard_types::Modifiers; + use keyboard_types::Key::Alt; + use keyboard_types::{Code, Key, Location, Modifiers}; use std::collections::HashMap; + use std::convert::TryInto; + use std::str::FromStr; use super::*; macro_rules! event_directory { @@ -425,69 +429,128 @@ pub mod on { #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone)] pub struct KeyboardData { + #[deprecated( + since = "0.3.0", + note = "This may not work in all environments. Use key() instead." + )] pub char_code: u32, /// Identify which "key" was entered. - /// - /// This is the best method to use for all languages. They key gets mapped to a String sequence which you can match on. - /// The key isn't an enum because there are just so many context-dependent keys. - /// - /// A full list on which keys to use is available at: - /// - /// - /// # Example - /// - /// ```rust, ignore - /// match event.key().as_str() { - /// "Esc" | "Escape" => {} - /// "ArrowDown" => {} - /// "ArrowLeft" => {} - /// _ => {} - /// } - /// ``` - /// + #[deprecated(since = "0.3.0", note = "use key() instead")] pub key: String, /// Get the key code as an enum Variant. - /// - /// This is intended for things like arrow keys, escape keys, function keys, and other non-international keys. - /// To match on unicode sequences, use the [`KeyboardEvent::key`] method - this will return a string identifier instead of a limited enum. - /// - /// - /// ## Example - /// - /// ```rust, ignore - /// use dioxus::KeyCode; - /// match event.key_code() { - /// KeyCode::Escape => {} - /// KeyCode::LeftArrow => {} - /// KeyCode::RightArrow => {} - /// _ => {} - /// } - /// ``` - /// + #[deprecated( + since = "0.3.0", + note = "This may not work in all environments. Use code() instead." + )] pub key_code: KeyCode, + /// the physical key on the keyboard + code: Code, + /// 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, /// 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, /// 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, /// 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, - pub locale: String, - + #[deprecated(since = "0.3.0", note = "use location() instead")] pub location: usize, + #[deprecated(since = "0.3.0", note = "use is_auto_repeating() instead")] pub repeat: bool, + #[deprecated(since = "0.3.0", note = "use code() or key() instead")] pub which: usize, - // get_modifier_state: bool, + } + + 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"), + } + } + + /// 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).expect("could not parse") + } + + /// 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 + } + + /// 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 location of the key on the keyboard or other input device. + pub fn location(&self) -> Location { + #[allow(deprecated)] + decode_key_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 + } } pub type FocusEvent = UiEvent; @@ -845,6 +908,7 @@ pub mod on { )] #[derive(Clone, Copy, Debug, PartialEq)] #[repr(u8)] +#[deprecated(since = "0.3.0", note = "use keyboard_types::Code instead")] pub enum KeyCode { // That key has no keycode, = 0 // break, = 3 diff --git a/packages/html/src/input_data.rs b/packages/html/src/input_data.rs index b5bc73a44..6b994d5f0 100644 --- a/packages/html/src/input_data.rs +++ b/packages/html/src/input_data.rs @@ -3,6 +3,7 @@ use enumset::{EnumSet, EnumSetType}; /// A re-export of keyboard_types pub use keyboard_types; +use keyboard_types::Location; /// A mouse button type (such as Primary/Secondary) // note: EnumSetType also derives Copy and Clone for some reason @@ -118,3 +119,25 @@ pub fn encode_mouse_button_set(set: MouseButtonSet) -> u16 { code } + +pub fn decode_key_location(code: usize) -> Location { + match code { + 0 => Location::Standard, + 1 => Location::Left, + 2 => Location::Right, + 3 => Location::Numpad, + // keyboard_types doesn't yet support mobile/joystick locations + 4 | 5 => Location::Standard, + // unknown location; Standard seems better than panicking + _ => Location::Standard, + } +} + +pub fn encode_key_location(location: Location) -> usize { + match location { + Location::Standard => 0, + Location::Left => 1, + Location::Right => 2, + Location::Numpad => 3, + } +} diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index bb32c1375..b8b076730 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -8,10 +8,11 @@ use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D}; use dioxus_html::geometry::{ ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint, WheelDelta, }; -use dioxus_html::input_data::keyboard_types::Modifiers; +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::{on::*, KeyCode}; +use std::str::FromStr; use std::{ any::Any, cell::RefCell, @@ -663,142 +664,221 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> { } fn translate_key_event(event: crossterm::event::KeyEvent) -> Option { - let (code, key_str); - if let TermKeyCode::Char(c) = event.code { - code = match c { + let key = key_from_crossterm_key_code(event.code); + // crossterm does not provide code. we make a guess as to which key might have been pressed + // this is probably garbage if the user has a custom keyboard layout + 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, + false, + modifiers, + ))) +} + +/// The crossterm key_code nicely represents the meaning of the key and we can mostly convert it without any issues +/// +/// Exceptions: +/// BackTab and Null are converted to Key::Unidentified +fn key_from_crossterm_key_code(key_code: TermKeyCode) -> Key { + match key_code { + TermKeyCode::Backspace => Key::Backspace, + TermKeyCode::Enter => Key::Enter, + TermKeyCode::Left => Key::ArrowLeft, + TermKeyCode::Right => Key::ArrowRight, + TermKeyCode::Up => Key::ArrowUp, + TermKeyCode::Down => Key::ArrowDown, + TermKeyCode::Home => Key::Home, + TermKeyCode::End => Key::End, + TermKeyCode::PageUp => Key::PageUp, + TermKeyCode::PageDown => Key::PageDown, + TermKeyCode::Tab => Key::Tab, + // ? no corresponding Key + TermKeyCode::BackTab => Key::Unidentified, + TermKeyCode::Delete => Key::Delete, + TermKeyCode::Insert => Key::Insert, + TermKeyCode::F(1) => Key::F1, + TermKeyCode::F(2) => Key::F2, + TermKeyCode::F(3) => Key::F3, + TermKeyCode::F(4) => Key::F4, + TermKeyCode::F(5) => Key::F5, + TermKeyCode::F(6) => Key::F6, + TermKeyCode::F(7) => Key::F7, + TermKeyCode::F(8) => Key::F8, + TermKeyCode::F(9) => Key::F9, + TermKeyCode::F(10) => Key::F10, + TermKeyCode::F(11) => Key::F11, + TermKeyCode::F(12) => Key::F12, + TermKeyCode::F(13) => Key::F13, + TermKeyCode::F(14) => Key::F14, + TermKeyCode::F(15) => Key::F15, + TermKeyCode::F(16) => Key::F16, + TermKeyCode::F(17) => Key::F17, + TermKeyCode::F(18) => Key::F18, + TermKeyCode::F(19) => Key::F19, + TermKeyCode::F(20) => Key::F20, + TermKeyCode::F(21) => Key::F21, + TermKeyCode::F(22) => Key::F22, + TermKeyCode::F(23) => Key::F23, + TermKeyCode::F(24) => Key::F24, + TermKeyCode::F(other) => { + panic!("Unexpected function key: {other:?}") + } + TermKeyCode::Char(c) => Key::Character(c.to_string()), + TermKeyCode::Null => Key::Unidentified, + TermKeyCode::Esc => Key::Escape, + } +} + +// Crossterm does not provide a way to get the `code` (physical key on keyboard) +// So we make a guess based on their `key_code`, but this is probably going to break on anything other than a very standard european keyboard +// It may look fine, but it's a horrible hack. +fn guess_code_from_crossterm_key_code(key_code: TermKeyCode) -> Option { + let code = match key_code { + TermKeyCode::Backspace => Code::Backspace, + TermKeyCode::Enter => Code::Enter, + TermKeyCode::Left => Code::ArrowLeft, + TermKeyCode::Right => Code::ArrowRight, + TermKeyCode::Up => Code::ArrowUp, + TermKeyCode::Down => Code::ArrowDown, + TermKeyCode::Home => Code::Home, + TermKeyCode::End => Code::End, + TermKeyCode::PageUp => Code::PageUp, + TermKeyCode::PageDown => Code::PageDown, + TermKeyCode::Tab => Code::Tab, + // ? no corresponding Code + TermKeyCode::BackTab => Code::Tab, + TermKeyCode::Delete => Code::Delete, + TermKeyCode::Insert => Code::Insert, + TermKeyCode::F(1) => Code::F1, + TermKeyCode::F(2) => Code::F2, + TermKeyCode::F(3) => Code::F3, + TermKeyCode::F(4) => Code::F4, + TermKeyCode::F(5) => Code::F5, + TermKeyCode::F(6) => Code::F6, + TermKeyCode::F(7) => Code::F7, + TermKeyCode::F(8) => Code::F8, + TermKeyCode::F(9) => Code::F9, + TermKeyCode::F(10) => Code::F10, + TermKeyCode::F(11) => Code::F11, + TermKeyCode::F(12) => Code::F12, + TermKeyCode::F(13) => Code::F13, + TermKeyCode::F(14) => Code::F14, + TermKeyCode::F(15) => Code::F15, + TermKeyCode::F(16) => Code::F16, + TermKeyCode::F(17) => Code::F17, + TermKeyCode::F(18) => Code::F18, + TermKeyCode::F(19) => Code::F19, + TermKeyCode::F(20) => Code::F20, + TermKeyCode::F(21) => Code::F21, + TermKeyCode::F(22) => Code::F22, + TermKeyCode::F(23) => Code::F23, + TermKeyCode::F(24) => Code::F24, + TermKeyCode::F(other) => { + panic!("Unexpected function key: {other:?}") + } + // this is a horrible way for crossterm to represent keys but we have to deal with it + TermKeyCode::Char(c) => match c { 'A'..='Z' | 'a'..='z' => match c.to_ascii_uppercase() { - 'A' => KeyCode::A, - 'B' => KeyCode::B, - 'C' => KeyCode::C, - 'D' => KeyCode::D, - 'E' => KeyCode::E, - 'F' => KeyCode::F, - 'G' => KeyCode::G, - 'H' => KeyCode::H, - 'I' => KeyCode::I, - 'J' => KeyCode::J, - 'K' => KeyCode::K, - 'L' => KeyCode::L, - 'M' => KeyCode::M, - 'N' => KeyCode::N, - 'O' => KeyCode::O, - 'P' => KeyCode::P, - 'Q' => KeyCode::Q, - 'R' => KeyCode::R, - 'S' => KeyCode::S, - 'T' => KeyCode::T, - 'U' => KeyCode::U, - 'V' => KeyCode::V, - 'W' => KeyCode::W, - 'X' => KeyCode::X, - 'Y' => KeyCode::Y, - 'Z' => KeyCode::Z, - _ => return None, + 'A' => Code::KeyA, + 'B' => Code::KeyB, + 'C' => Code::KeyC, + 'D' => Code::KeyD, + 'E' => Code::KeyE, + 'F' => Code::KeyF, + 'G' => Code::KeyG, + 'H' => Code::KeyH, + 'I' => Code::KeyI, + 'J' => Code::KeyJ, + 'K' => Code::KeyK, + 'L' => Code::KeyL, + 'M' => Code::KeyM, + 'N' => Code::KeyN, + 'O' => Code::KeyO, + 'P' => Code::KeyP, + 'Q' => Code::KeyQ, + 'R' => Code::KeyR, + 'S' => Code::KeyS, + 'T' => Code::KeyT, + 'U' => Code::KeyU, + 'V' => Code::KeyV, + 'W' => Code::KeyW, + 'X' => Code::KeyX, + 'Y' => Code::KeyY, + 'Z' => Code::KeyZ, + _ => unreachable!(), }, - ' ' => KeyCode::Space, - '[' => KeyCode::OpenBracket, - '{' => KeyCode::OpenBracket, - ']' => KeyCode::CloseBraket, - '}' => KeyCode::CloseBraket, - ';' => KeyCode::Semicolon, - ':' => KeyCode::Semicolon, - ',' => KeyCode::Comma, - '<' => KeyCode::Comma, - '.' => KeyCode::Period, - '>' => KeyCode::Period, - '1' => KeyCode::Num1, - '2' => KeyCode::Num2, - '3' => KeyCode::Num3, - '4' => KeyCode::Num4, - '5' => KeyCode::Num5, - '6' => KeyCode::Num6, - '7' => KeyCode::Num7, - '8' => KeyCode::Num8, - '9' => KeyCode::Num9, - '0' => KeyCode::Num0, - '!' => KeyCode::Num1, - '@' => KeyCode::Num2, - '#' => KeyCode::Num3, - '$' => KeyCode::Num4, - '%' => KeyCode::Num5, - '^' => KeyCode::Num6, - '&' => KeyCode::Num7, - '*' => KeyCode::Num8, - '(' => KeyCode::Num9, - ')' => KeyCode::Num0, + ' ' => Code::Space, + '[' | '{' => Code::BracketLeft, + ']' | '}' => Code::BracketRight, + ';' => Code::Semicolon, + ':' => Code::Semicolon, + ',' => Code::Comma, + '<' => Code::Comma, + '.' => Code::Period, + '>' => Code::Period, + '1' => Code::Digit1, + '2' => Code::Digit2, + '3' => Code::Digit3, + '4' => Code::Digit4, + '5' => Code::Digit5, + '6' => Code::Digit6, + '7' => Code::Digit7, + '8' => Code::Digit8, + '9' => Code::Digit9, + '0' => Code::Digit0, + '!' => Code::Digit1, + '@' => Code::Digit2, + '#' => Code::Digit3, + '$' => Code::Digit4, + '%' => Code::Digit5, + '^' => Code::Digit6, + '&' => Code::Digit7, + '*' => Code::Digit8, + '(' => Code::Digit9, + ')' => Code::Digit0, // numpad charicter are ambiguous to tui - // '*' => KeyCode::Multiply, - // '/' => KeyCode::Divide, - // '-' => KeyCode::Subtract, - // '+' => KeyCode::Add, - '+' => KeyCode::EqualSign, - '-' => KeyCode::Dash, - '_' => KeyCode::Dash, - '\'' => KeyCode::SingleQuote, - '"' => KeyCode::SingleQuote, - '\\' => KeyCode::BackSlash, - '|' => KeyCode::BackSlash, - '/' => KeyCode::ForwardSlash, - '?' => KeyCode::ForwardSlash, - '=' => KeyCode::EqualSign, - '`' => KeyCode::GraveAccent, - '~' => KeyCode::GraveAccent, + // '*' => Code::Multiply, + // '/' => Code::Divide, + // '-' => Code::Subtract, + // '+' => Code::Add, + '+' => Code::Equal, + '-' | '_' => Code::Minus, + '\'' => Code::Quote, + '"' => Code::Quote, + '\\' => Code::Backslash, + '|' => Code::Backslash, + '/' => Code::Slash, + '?' => Code::Slash, + '=' => Code::Equal, + '`' => Code::Backquote, + '~' => Code::Backquote, _ => return None, - }; - key_str = c.to_string(); - } else { - code = match event.code { - TermKeyCode::Esc => KeyCode::Escape, - TermKeyCode::Backspace => KeyCode::Backspace, - TermKeyCode::Enter => KeyCode::Enter, - TermKeyCode::Left => KeyCode::LeftArrow, - TermKeyCode::Right => KeyCode::RightArrow, - TermKeyCode::Up => KeyCode::UpArrow, - TermKeyCode::Down => KeyCode::DownArrow, - TermKeyCode::Home => KeyCode::Home, - TermKeyCode::End => KeyCode::End, - TermKeyCode::PageUp => KeyCode::PageUp, - TermKeyCode::PageDown => KeyCode::PageDown, - TermKeyCode::Tab => KeyCode::Tab, - TermKeyCode::Delete => KeyCode::Delete, - TermKeyCode::Insert => KeyCode::Insert, - TermKeyCode::F(fn_num) => match fn_num { - 1 => KeyCode::F1, - 2 => KeyCode::F2, - 3 => KeyCode::F3, - 4 => KeyCode::F4, - 5 => KeyCode::F5, - 6 => KeyCode::F6, - 7 => KeyCode::F7, - 8 => KeyCode::F8, - 9 => KeyCode::F9, - 10 => KeyCode::F10, - 11 => KeyCode::F11, - 12 => KeyCode::F12, - _ => return None, - }, - TermKeyCode::BackTab => return None, - TermKeyCode::Null => return None, - _ => return None, - }; - key_str = if let KeyCode::BackSlash = code { - "\\".to_string() - } else { - format!("{code:?}") - } + }, + TermKeyCode::Null => return None, + TermKeyCode::Esc => Code::Escape, }; - // from https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent - Some(EventData::Keyboard(KeyboardData { - char_code: code.raw_code(), - key: key_str, - key_code: code, - alt_key: event.modifiers.contains(KeyModifiers::ALT), - ctrl_key: event.modifiers.contains(KeyModifiers::CONTROL), - meta_key: false, - shift_key: event.modifiers.contains(KeyModifiers::SHIFT), - locale: Default::default(), - location: 0x00, - repeat: Default::default(), - which: Default::default(), - })) + + Some(code) +} + +fn modifiers_from_crossterm_modifiers(src: KeyModifiers) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if src.contains(KeyModifiers::SHIFT) { + modifiers.insert(Modifiers::SHIFT); + } + + if src.contains(KeyModifiers::ALT) { + modifiers.insert(Modifiers::ALT); + } + + if src.contains(KeyModifiers::CONTROL) { + modifiers.insert(Modifiers::CONTROL); + } + + modifiers } From aaf9d4665f8285fbb4cdc5115f3547736ec7a02b Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Thu, 12 May 2022 14:36:52 +0300 Subject: [PATCH 06/39] Update interpreter and example --- examples/calculator.rs | 50 ++++++++++++++----------- examples/tui_keys.rs | 27 ++++++++----- packages/html/src/events.rs | 2 - packages/interpreter/src/interpreter.js | 2 + packages/tui/src/hooks.rs | 17 +++++---- 5 files changed, 57 insertions(+), 41 deletions(-) diff --git a/examples/calculator.rs b/examples/calculator.rs index f63b8a754..75da8f1d3 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -5,6 +5,7 @@ This calculator version uses React-style state management. All state is held as use dioxus::events::*; use dioxus::prelude::*; +use dioxus_html::input_data::keyboard_types::Key; fn main() { use dioxus::desktop::tao::dpi::LogicalSize; @@ -29,33 +30,38 @@ fn app(cx: Scope) -> Element { let input_operator = move |key: &str| val.make_mut().push_str(key); + let handle_key_down_event = move |evt: KeyboardEvent| match evt.key() { + Key::Backspace => { + if !val.len() != 0 { + val.make_mut().pop(); + } + } + Key::Character(character) => match character.as_str() { + "+" => input_operator("+"), + "-" => input_operator("-"), + "/" => input_operator("/"), + "*" => input_operator("*"), + "0" => input_digit(0), + "1" => input_digit(1), + "2" => input_digit(2), + "3" => input_digit(3), + "4" => input_digit(4), + "5" => input_digit(5), + "6" => input_digit(6), + "7" => input_digit(7), + "8" => input_digit(8), + "9" => input_digit(9), + _ => {} + }, + _ => {} + }; + cx.render(rsx!( style { [include_str!("./assets/calculator.css")] } div { id: "wrapper", div { class: "app", div { class: "calculator", - onkeydown: move |evt| match evt.key_code { - KeyCode::Add => input_operator("+"), - KeyCode::Subtract => input_operator("-"), - KeyCode::Divide => input_operator("/"), - KeyCode::Multiply => input_operator("*"), - KeyCode::Num0 => input_digit(0), - KeyCode::Num1 => input_digit(1), - KeyCode::Num2 => input_digit(2), - KeyCode::Num3 => input_digit(3), - KeyCode::Num4 => input_digit(4), - KeyCode::Num5 => input_digit(5), - KeyCode::Num6 => input_digit(6), - KeyCode::Num7 => input_digit(7), - KeyCode::Num8 => input_digit(8), - KeyCode::Num9 => input_digit(9), - KeyCode::Backspace => { - if !val.len() != 0 { - val.make_mut().pop(); - } - } - _ => {} - }, + onkeydown: handle_key_down_event, div { class: "calculator-display", [val.to_string()] } div { class: "calculator-keypad", div { class: "input-keys", diff --git a/examples/tui_keys.rs b/examples/tui_keys.rs index b4a7ce5d1..f53818234 100644 --- a/examples/tui_keys.rs +++ b/examples/tui_keys.rs @@ -1,6 +1,7 @@ use dioxus::events::WheelEvent; use dioxus::prelude::*; use dioxus_html::geometry::ScreenPoint; +use dioxus_html::input_data::keyboard_types::Code; use dioxus_html::input_data::MouseButtonSet; use dioxus_html::on::{KeyboardEvent, MouseEvent}; use dioxus_html::KeyCode; @@ -16,6 +17,21 @@ fn app(cx: Scope) -> Element { let buttons = use_state(&cx, || MouseButtonSet::empty()); let mouse_clicked = use_state(&cx, || false); + let key_down_handler = move |evt: KeyboardEvent| { + match evt.data.code() { + Code::ArrowLeft => count.set(count + 1), + Code::ArrowRight => count.set(count - 1), + Code::ArrowUp => count.set(count + 10), + Code::ArrowDown => count.set(count - 10), + _ => {} + } + key.set(format!( + "{:?} repeating: {:?}", + evt.key(), + evt.is_auto_repeating() + )); + }; + cx.render(rsx! { div { width: "100%", @@ -24,16 +40,7 @@ fn app(cx: Scope) -> Element { justify_content: "center", align_items: "center", flex_direction: "column", - onkeydown: move |evt: KeyboardEvent| { - match evt.data.key_code { - KeyCode::LeftArrow => count.set(count + 1), - KeyCode::RightArrow => count.set(count - 1), - KeyCode::UpArrow => count.set(count + 10), - KeyCode::DownArrow => count.set(count - 10), - _ => {}, - } - key.set(format!("{:?} repeating: {:?}", evt.key, evt.repeat)); - }, + onkeydown: key_down_handler, onwheel: move |evt: WheelEvent| { count.set(count + evt.data.delta().strip_units().y as i64); }, diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 373e46414..114317428 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -14,7 +14,6 @@ pub mod on { MouseButton, MouseButtonSet, }; use euclid::UnknownUnit; - use keyboard_types::Key::Alt; use keyboard_types::{Code, Key, Location, Modifiers}; use std::collections::HashMap; use std::convert::TryInto; @@ -908,7 +907,6 @@ pub mod on { )] #[derive(Clone, Copy, Debug, PartialEq)] #[repr(u8)] -#[deprecated(since = "0.3.0", note = "use keyboard_types::Code instead")] pub enum KeyCode { // That key has no keycode, = 0 // break, = 3 diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index a908ad74c..c1b70ff2a 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -334,6 +334,7 @@ export function serialize_event(event) { location, repeat, which, + code, } = event; return { char_code: charCode, @@ -347,6 +348,7 @@ export function serialize_event(event) { repeat: repeat, which: which, locale: "locale", + code, }; } case "focus": diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index b8b076730..877e0b524 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -11,8 +11,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::{on::*, KeyCode}; -use std::str::FromStr; +use dioxus_html::on::*; use std::{ any::Any, cell::RefCell, @@ -140,14 +139,18 @@ impl InnerInputState { EventData::Wheel(ref w) => self.wheel = Some(w.clone()), EventData::Screen(ref s) => self.screen = Some(*s), EventData::Keyboard(ref mut k) => { - let repeat = self + let is_repeating = self .last_key_pressed .as_ref() - .filter(|k2| k2.0.key == k.key && k2.1.elapsed() < MAX_REPEAT_TIME) + // heuristic for guessing which presses are auto-repeating. not necessarily accurate + .filter(|k2| k2.0.key() == k.key() && k2.1.elapsed() < MAX_REPEAT_TIME) .is_some(); - k.repeat = repeat; - let new = k.clone(); - self.last_key_pressed = Some((new, Instant::now())); + if is_repeating { + let new = k.clone(); + self.last_key_pressed = Some((new, Instant::now())); + + *k = KeyboardData::new(k.key(), k.code(), k.location(), true, k.modifiers()); + } } } } From 45980f9a1e92cf47bbd489ad412a3d50d7da9047 Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Thu, 12 May 2022 15:00:43 +0300 Subject: [PATCH 07/39] Update examples to avoid deprecated API --- .../src/advanced-guides/custom-renderer.md | 1 - docs/reference/src/guide/custom_renderer.md | 1 - examples/pattern_model.rs | 37 +++++++++++-------- examples/todomvc.rs | 7 ++-- examples/tui_keys.rs | 1 - packages/html/src/web_sys_bind/events.rs | 1 - packages/interpreter/src/interpreter.js | 1 - packages/liveview/src/interpreter.js | 1 - packages/web/src/olddom.rs | 1 - 9 files changed, 25 insertions(+), 26 deletions(-) diff --git a/docs/guide/src/advanced-guides/custom-renderer.md b/docs/guide/src/advanced-guides/custom-renderer.md index d2aa40bc9..a8bec614c 100644 --- a/docs/guide/src/advanced-guides/custom-renderer.md +++ b/docs/guide/src/advanced-guides/custom-renderer.md @@ -204,7 +204,6 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent { fn ctrl_key(&self) -> bool { self.0.ctrl_key() } fn key(&self) -> String { self.0.key() } fn key_code(&self) -> usize { self.0.key_code() } - fn locale(&self) -> String { self.0.locale() } fn location(&self) -> usize { self.0.location() } fn meta_key(&self) -> bool { self.0.meta_key() } fn repeat(&self) -> bool { self.0.repeat() } diff --git a/docs/reference/src/guide/custom_renderer.md b/docs/reference/src/guide/custom_renderer.md index 9a73dd7f7..35187e2e3 100644 --- a/docs/reference/src/guide/custom_renderer.md +++ b/docs/reference/src/guide/custom_renderer.md @@ -206,7 +206,6 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent { ctrl_key: event.ctrl_key(), meta_key: event.meta_key(), shift_key: event.shift_key(), - locale: "".to_string(), location: event.location(), repeat: event.repeat(), which: event.which(), diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs index a7b821750..c88bc78fb 100644 --- a/examples/pattern_model.rs +++ b/examples/pattern_model.rs @@ -20,6 +20,7 @@ use dioxus::desktop::wry::application::dpi::LogicalSize; use dioxus::events::*; use dioxus::prelude::*; +use dioxus_html::input_data::keyboard_types::Key; fn main() { dioxus::desktop::launch_cfg(app, |cfg| { @@ -212,22 +213,26 @@ impl Calculator { self.waiting_for_operand = true; } fn handle_keydown(&mut self, evt: KeyboardEvent) { - match evt.key_code { - KeyCode::Backspace => self.backspace(), - KeyCode::Num0 => self.input_digit(0), - KeyCode::Num1 => self.input_digit(1), - KeyCode::Num2 => self.input_digit(2), - KeyCode::Num3 => self.input_digit(3), - KeyCode::Num4 => self.input_digit(4), - KeyCode::Num5 => self.input_digit(5), - KeyCode::Num6 => self.input_digit(6), - KeyCode::Num7 => self.input_digit(7), - KeyCode::Num8 => self.input_digit(8), - KeyCode::Num9 => self.input_digit(9), - KeyCode::Add => self.operator = Some(Operator::Add), - KeyCode::Subtract => self.operator = Some(Operator::Sub), - KeyCode::Divide => self.operator = Some(Operator::Div), - KeyCode::Multiply => self.operator = Some(Operator::Mul), + match evt.key() { + Key::Backspace => self.backspace(), + Key::Character(c) => match c.as_str() { + "0" => self.input_digit(0), + "1" => self.input_digit(1), + "2" => self.input_digit(2), + "3" => self.input_digit(3), + "4" => self.input_digit(4), + "5" => self.input_digit(5), + "6" => self.input_digit(6), + "7" => self.input_digit(7), + "8" => self.input_digit(8), + "9" => self.input_digit(9), + "+" => self.operator = Some(Operator::Add), + "-" => self.operator = Some(Operator::Sub), + "/" => self.operator = Some(Operator::Div), + "*" => self.operator = Some(Operator::Mul), + _ => {} + }, + _ => {} } } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 99cf67a69..cf9a49410 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -1,4 +1,5 @@ use dioxus::prelude::*; +use dioxus_elements::input_data::keyboard_types::Key; fn main() { dioxus::desktop::launch(app); @@ -56,7 +57,7 @@ pub fn app(cx: Scope<()>) -> Element { autofocus: "true", oninput: move |evt| draft.set(evt.value.clone()), onkeydown: move |evt| { - if evt.key == "Enter" && !draft.is_empty() { + if evt.key() == Key::Enter && !draft.is_empty() { todos.make_mut().insert( **todo_id, TodoItem { @@ -148,8 +149,8 @@ pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element { autofocus: "true", onfocusout: move |_| is_editing.set(false), onkeydown: move |evt| { - match evt.key.as_str() { - "Enter" | "Escape" | "Tab" => is_editing.set(false), + match evt.key() { + Key::Enter | Key::Escape | Key::Tab => is_editing.set(false), _ => {} } }, diff --git a/examples/tui_keys.rs b/examples/tui_keys.rs index f53818234..2417a3077 100644 --- a/examples/tui_keys.rs +++ b/examples/tui_keys.rs @@ -4,7 +4,6 @@ use dioxus_html::geometry::ScreenPoint; use dioxus_html::input_data::keyboard_types::Code; use dioxus_html::input_data::MouseButtonSet; use dioxus_html::on::{KeyboardEvent, MouseEvent}; -use dioxus_html::KeyCode; fn main() { dioxus::tui::launch(app); diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index fbbb34d9e..4a9f086b3 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -62,7 +62,6 @@ impl From<&KeyboardEvent> for KeyboardData { key: e.key(), key_code: KeyCode::from_raw_code(e.key_code() as u8), ctrl_key: e.ctrl_key(), - locale: "not implemented".to_string(), location: e.location() as usize, meta_key: e.meta_key(), repeat: e.repeat(), diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index c1b70ff2a..f1060266c 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -347,7 +347,6 @@ export function serialize_event(event) { location: location, repeat: repeat, which: which, - locale: "locale", code, }; } diff --git a/packages/liveview/src/interpreter.js b/packages/liveview/src/interpreter.js index 383f33a5c..f1eec3fde 100644 --- a/packages/liveview/src/interpreter.js +++ b/packages/liveview/src/interpreter.js @@ -358,7 +358,6 @@ function serialize_event(event) { location: location, repeat: repeat, which: which, - locale: "locale", }; } case "focus": diff --git a/packages/web/src/olddom.rs b/packages/web/src/olddom.rs index 8654ced90..d6035d5ab 100644 --- a/packages/web/src/olddom.rs +++ b/packages/web/src/olddom.rs @@ -521,7 +521,6 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc Date: Thu, 12 May 2022 18:24:13 +0300 Subject: [PATCH 08/39] Update web_sys to avoid deprecated API --- packages/html/src/web_sys_bind/events.rs | 43 ++++++++++++++++-------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index 4a9f086b3..e7bdcf5d7 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -1,11 +1,12 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; -use crate::input_data::{decode_mouse_button_set, MouseButton}; +use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; use crate::on::{ AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData, TransitionData, WheelData, }; -use crate::KeyCode; -use keyboard_types::Modifiers; +use keyboard_types::{Code, Key, Modifiers}; +use std::convert::TryInto; +use std::str::FromStr; use wasm_bindgen::JsCast; use web_sys::{ AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, TouchEvent, @@ -56,18 +57,32 @@ impl From<&CompositionEvent> for CompositionData { impl From<&KeyboardEvent> for KeyboardData { fn from(e: &KeyboardEvent) -> Self { - Self { - alt_key: e.alt_key(), - char_code: e.char_code(), - key: e.key(), - key_code: KeyCode::from_raw_code(e.key_code() as u8), - ctrl_key: e.ctrl_key(), - location: e.location() as usize, - meta_key: e.meta_key(), - repeat: e.repeat(), - shift_key: e.shift_key(), - which: e.which() as usize, + 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); + } + + 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, + ) } } From 61b4bb2f19bf7d14b003186ce9256da162e09658 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 22 Jun 2022 13:57:05 -0500 Subject: [PATCH 09/39] handle bubbling events --- packages/core/src/events.rs | 3 + packages/core/src/scopes.rs | 6 +- packages/desktop/src/events.rs | 4 +- packages/html/src/events.rs | 88 ++++++++ packages/interpreter/src/bindings.rs | 10 +- packages/interpreter/src/interpreter.js | 257 ++++++++++++++++++++++-- packages/tui/src/hooks.rs | 4 +- packages/web/src/dom.rs | 16 +- packages/web/src/rehydrate.rs | 2 + 9 files changed, 363 insertions(+), 27 deletions(-) diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 0b24f5563..92c86dec8 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -63,6 +63,9 @@ pub struct UserEvent { /// The event type IE "onclick" or "onmouseover" pub name: &'static str, + /// If the event is bubbles up through the vdom + pub bubbles: bool, + /// The event data to be passed onto the event handler pub data: Arc, } diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 8db5efe56..ba6afcfe2 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -335,7 +335,7 @@ impl ScopeArena { log::trace!("calling listener {:?}", listener.event); if state.canceled.get() { // stop bubbling if canceled - break; + return; } let mut cb = listener.callback.borrow_mut(); @@ -349,6 +349,10 @@ impl ScopeArena { data: event.data.clone(), }); } + + if !event.bubbles { + return; + } } } diff --git a/packages/desktop/src/events.rs b/packages/desktop/src/events.rs index fd0675f10..803767a09 100644 --- a/packages/desktop/src/events.rs +++ b/packages/desktop/src/events.rs @@ -4,6 +4,7 @@ use std::any::Any; use std::sync::Arc; use dioxus_core::{ElementId, EventPriority, UserEvent}; +use dioxus_html::event_bubbles; use dioxus_html::on::*; #[derive(serde::Serialize, serde::Deserialize)] @@ -47,8 +48,8 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent { } = serde_json::from_value(val).unwrap(); let mounted_dom_id = Some(ElementId(mounted_dom_id as usize)); - let name = event_name_from_type(&event); + let event = make_synthetic_event(&event, contents); UserEvent { @@ -56,6 +57,7 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent { priority: EventPriority::Low, scope_id: None, element: mounted_dom_id, + bubbles: event_bubbles(name), data: event, } } diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index beac81167..67522e87a 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -1324,3 +1324,91 @@ pub(crate) fn _event_meta(event: &UserEvent) -> (bool, EventPriority) { _ => (true, Low), } } + +pub fn event_bubbles(evt: &str) -> bool { + match evt { + "copy" => true, + "cut" => true, + "paste" => true, + "compositionend" => true, + "compositionstart" => true, + "compositionupdate" => true, + "keydown" => true, + "keypress" => true, + "keyup" => true, + "focus" => false, + "focusout" => true, + "focusin" => true, + "blur" => false, + "change" => true, + "input" => true, + "invalid" => true, + "reset" => true, + "submit" => true, + "click" => true, + "contextmenu" => true, + "doubleclick" => true, + "dblclick" => true, + "drag" => true, + "dragend" => true, + "dragenter" => false, + "dragexit" => false, + "dragleave" => true, + "dragover" => true, + "dragstart" => true, + "drop" => true, + "mousedown" => true, + "mouseenter" => false, + "mouseleave" => false, + "mousemove" => true, + "mouseout" => true, + "scroll" => false, + "mouseover" => true, + "mouseup" => true, + "pointerdown" => true, + "pointermove" => true, + "pointerup" => true, + "pointercancel" => true, + "gotpointercapture" => true, + "lostpointercapture" => true, + "pointerenter" => false, + "pointerleave" => false, + "pointerover" => true, + "pointerout" => true, + "select" => true, + "touchcancel" => true, + "touchend" => true, + "touchmove" => true, + "touchstart" => true, + "wheel" => true, + "abort" => false, + "canplay" => true, + "canplaythrough" => true, + "durationchange" => true, + "emptied" => true, + "encrypted" => true, + "ended" => true, + "error" => false, + "loadeddata" => true, + "loadedmetadata" => true, + "loadstart" => false, + "pause" => true, + "play" => true, + "playing" => true, + "progress" => false, + "ratechange" => true, + "seeked" => true, + "seeking" => true, + "stalled" => true, + "suspend" => true, + "timeupdate" => true, + "volumechange" => true, + "waiting" => true, + "animationstart" => true, + "animationend" => true, + "animationiteration" => true, + "transitionend" => true, + "toggle" => true, + _ => panic!("unsupported event type {:?}", evt), + } +} diff --git a/packages/interpreter/src/bindings.rs b/packages/interpreter/src/bindings.rs index a08ef9c13..80df47c2c 100644 --- a/packages/interpreter/src/bindings.rs +++ b/packages/interpreter/src/bindings.rs @@ -48,10 +48,16 @@ extern "C" { pub fn CreatePlaceholder(this: &Interpreter, root: u64); #[wasm_bindgen(method)] - pub fn NewEventListener(this: &Interpreter, name: &str, root: u64, handler: &Function); + pub fn NewEventListener( + this: &Interpreter, + name: &str, + root: u64, + handler: &Function, + bubbles: bool, + ); #[wasm_bindgen(method)] - pub fn RemoveEventListener(this: &Interpreter, root: u64, name: &str); + pub fn RemoveEventListener(this: &Interpreter, root: u64, name: &str, bubbles: bool); #[wasm_bindgen(method)] pub fn SetText(this: &Interpreter, root: u64, text: JsValue); diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 0c129efda..759e83c9f 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -5,11 +5,64 @@ export function main() { window.ipc.postMessage(serializeIpcMessage("initialize")); } } + +class ListenerMap { + constructor(root) { + // bubbling events can listen at the root element + this.global = {}; + // non bubbling events listen at the element the listener was created at + this.local = {}; + this.root = root; + } + + createBubbling(event_name, handler) { + if (this.global[event_name] === undefined) { + this.global[event_name] = {}; + this.global[event_name].active = 1; + this.global[event_name].callback = handler; + this.root.addEventListener(event_name, handler); + } else { + this.global[event_name].active++; + } + } + + createNonBubbling(event_name, element, handler) { + const id = element.getAttribute("data-dioxus-id"); + if (!this.local[id]) { + this.local[id] = {}; + } + this.local[id][event_name] = handler; + element.addEventListener(event_name, handler); + } + + removeBubbling(event_name) { + this.global[event_name].active--; + if (this.global[event_name].active === 0) { + this.root.removeEventListener(event_name, this.global[event_name].callback); + delete this.global[event_name]; + } + } + + removeNonBubbling(element, event_name) { + const id = element.getAttribute("data-dioxus-id"); + delete this.local[id][event_name]; + if (this.local[id].length === 0) { + delete this.local[id]; + } + element.removeEventListener(event_name, handler); + } + + removeAllNonBubbling(element) { + const id = element.getAttribute("data-dioxus-id"); + delete this.local[id]; + } +} + export class Interpreter { constructor(root) { this.root = root; this.stack = [root]; - this.listeners = {}; + this.listeners = new ListenerMap(root); this.handlers = {}; this.lastNodeWasText = false; this.nodes = [root]; @@ -40,6 +93,7 @@ export class Interpreter { ReplaceWith(root_id, m) { let root = this.nodes[root_id]; let els = this.stack.splice(this.stack.length - m); + this.listeners.removeAllNonBubbling(root); root.replaceWith(...els); } InsertAfter(root, n) { @@ -54,6 +108,7 @@ export class Interpreter { } Remove(root) { let node = this.nodes[root]; + this.listeners.removeAllNonBubbling(node); if (node !== undefined) { node.remove(); } @@ -79,25 +134,24 @@ export class Interpreter { this.stack.push(el); this.nodes[root] = el; } - NewEventListener(event_name, root, handler) { + NewEventListener(event_name, root, handler, bubbles) { const element = this.nodes[root]; element.setAttribute("data-dioxus-id", `${root}`); - if (this.listeners[event_name] === undefined) { - this.listeners[event_name] = 1; - this.handlers[event_name] = handler; - this.root.addEventListener(event_name, handler); - } else { - this.listeners[event_name]++; + if (bubbles) { + this.listeners.createBubbling(event_name, handler); + } + else { + this.listeners.createNonBubbling(event_name, element, handler); } } - RemoveEventListener(root, event_name) { + RemoveEventListener(root, event_name, bubbles) { const element = this.nodes[root]; element.removeAttribute(`data-dioxus-id`); - this.listeners[event_name]--; - if (this.listeners[event_name] === 0) { - this.root.removeEventListener(event_name, this.handlers[event_name]); - delete this.listeners[event_name]; - delete this.handlers[event_name]; + if (bubbles) { + this.listeners.removeBubbling(event_name) + } + else { + this.listeners.removeNonBubbling(element, event_name); } } SetText(root, text) { @@ -198,12 +252,9 @@ export class Interpreter { this.RemoveEventListener(edit.root, edit.event_name); break; case "NewEventListener": - console.log(this.listeners); - // this handler is only provided on desktop implementations since this // method is not used by the web implementation let handler = (event) => { - console.log(event); let target = event.target; if (target != null) { @@ -292,7 +343,8 @@ export class Interpreter { ); } }; - this.NewEventListener(edit.event_name, edit.root, handler); + this.NewEventListener(edit.event_name, edit.root, handler, event_bubbles(edit.event_name)); + break; case "SetText": this.SetText(edit.root, edit.text); @@ -607,3 +659,172 @@ const bool_attrs = { selected: true, truespeed: true, }; + +function event_bubbles(event) { + switch (event) { + case "copy": + return true; + case "cut": + return true; + case "paste": + return true; + case "compositionend": + return true; + case "compositionstart": + return true; + case "compositionupdate": + return true; + case "keydown": + return true; + case "keypress": + return true; + case "keyup": + return true; + case "focus": + return false; + case "focusout": + return true; + case "focusin": + return true; + case "blur": + return false; + case "change": + return true; + case "input": + return true; + case "invalid": + return true; + case "reset": + return true; + case "submit": + return true; + case "click": + return true; + case "contextmenu": + return true; + case "doubleclick": + return true; + case "dblclick": + return true; + case "drag": + return true; + case "dragend": + return true; + case "dragenter": + return false; + case "dragexit": + return false; + case "dragleave": + return true; + case "dragover": + return true; + case "dragstart": + return true; + case "drop": + return true; + case "mousedown": + return true; + case "mouseenter": + return false; + case "mouseleave": + return false; + case "mousemove": + return true; + case "mouseout": + return true; + case "scroll": + return false; + case "mouseover": + return true; + case "mouseup": + return true; + case "pointerdown": + return true; + case "pointermove": + return true; + case "pointerup": + return true; + case "pointercancel": + return true; + case "gotpointercapture": + return true; + case "lostpointercapture": + return true; + case "pointerenter": + return false; + case "pointerleave": + return false; + case "pointerover": + return true; + case "pointerout": + return true; + case "select": + return true; + case "touchcancel": + return true; + case "touchend": + return true; + case "touchmove": + return true; + case "touchstart": + return true; + case "wheel": + return true; + case "abort": + return false; + case "canplay": + return true; + case "canplaythrough": + return true; + case "durationchange": + return true; + case "emptied": + return true; + case "encrypted": + return true; + case "ended": + return true; + case "error": + return false; + case "loadeddata": + return true; + case "loadedmetadata": + return true; + case "loadstart": + return false; + case "pause": + return true; + case "play": + return true; + case "playing": + return true; + case "progress": + return false; + case "ratechange": + return true; + case "seeked": + return true; + case "seeking": + return true; + case "stalled": + return true; + case "suspend": + return true; + case "timeupdate": + return true; + case "volumechange": + return true; + case "waiting": + return true; + case "animationstart": + return true; + case "animationend": + return true; + case "animationiteration": + return true; + case "transitionend": + return true; + case "toggle": + return true; + } +} \ No newline at end of file diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 3a1646fdf..7d58f9628 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -9,7 +9,7 @@ use dioxus_html::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, S use dioxus_html::input_data::keyboard_types::Modifiers; use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons; use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet}; -use dioxus_html::{on::*, KeyCode}; +use dioxus_html::{event_bubbles, on::*, KeyCode}; use std::{ any::Any, cell::RefCell, @@ -207,6 +207,7 @@ impl InnerInputState { name, element: Some(node.id), data, + bubbles: event_bubbles(name), }) } } @@ -574,6 +575,7 @@ impl RinkInputHandler { name: event, element: Some(node.id), data: data.clone(), + bubbles: event_bubbles(event), }); } } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index ebcdefbcc..0e434c1a0 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -8,6 +8,7 @@ //! - Partial delegation?> use dioxus_core::{DomEdit, ElementId, SchedulerMsg, UserEvent}; +use dioxus_html::event_bubbles; use dioxus_interpreter_js::Interpreter; use js_sys::Function; use std::{any::Any, rc::Rc, sync::Arc}; @@ -45,6 +46,7 @@ impl WebsysDom { element: Some(ElementId(id)), scope_id: None, priority: dioxus_core::EventPriority::Medium, + bubbles: event.bubbles(), }); } Some(Err(e)) => { @@ -64,6 +66,7 @@ impl WebsysDom { element: None, scope_id: None, priority: dioxus_core::EventPriority::Low, + bubbles: event.bubbles(), }); } } @@ -121,12 +124,17 @@ impl WebsysDom { event_name, root, .. } => { let handler: &Function = self.handler.as_ref().unchecked_ref(); - self.interpreter.NewEventListener(event_name, root, handler); + self.interpreter.NewEventListener( + event_name, + root, + handler, + event_bubbles(event_name), + ); } - DomEdit::RemoveEventListener { root, event } => { - self.interpreter.RemoveEventListener(root, event) - } + DomEdit::RemoveEventListener { root, event } => self + .interpreter + .RemoveEventListener(root, event, event_bubbles(event)), DomEdit::RemoveAttribute { root, name, ns } => { self.interpreter.RemoveAttribute(root, name, ns) diff --git a/packages/web/src/rehydrate.rs b/packages/web/src/rehydrate.rs index 24e46d11e..a6b7df999 100644 --- a/packages/web/src/rehydrate.rs +++ b/packages/web/src/rehydrate.rs @@ -1,5 +1,6 @@ use crate::dom::WebsysDom; use dioxus_core::{VNode, VirtualDom}; +use dioxus_html::event_bubbles; use wasm_bindgen::JsCast; use web_sys::{Comment, Element, Node, Text}; @@ -111,6 +112,7 @@ impl WebsysDom { listener.event, listener.mounted_node.get().unwrap().as_u64(), self.handler.as_ref().unchecked_ref(), + event_bubbles(listener.event), ); } From 2d45e1dc8d0f44caf8886b0c5b09bff3473cf8fe Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 22 Jun 2022 14:24:03 -0500 Subject: [PATCH 10/39] add bubbling data to events after merge --- packages/liveview/src/events.rs | 2 ++ packages/tui/src/hooks.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/packages/liveview/src/events.rs b/packages/liveview/src/events.rs index 10d1ffd38..2c10925b5 100644 --- a/packages/liveview/src/events.rs +++ b/packages/liveview/src/events.rs @@ -6,6 +6,7 @@ use std::any::Any; use std::sync::Arc; use dioxus_core::{ElementId, EventPriority, UserEvent}; +use dioxus_html::event_bubbles; use dioxus_html::on::*; #[derive(serde::Serialize, serde::Deserialize)] @@ -46,6 +47,7 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent { scope_id: None, element: mounted_dom_id, data: event, + bubbles: event_bubbles(name), } } diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 45a428607..6d633eab8 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -187,6 +187,7 @@ impl InnerInputState { name: "focus", element: Some(id), data: Arc::new(FocusData {}), + bubbles: event_bubbles("focus"), }); resolved_events.push(UserEvent { scope_id: None, @@ -194,6 +195,7 @@ impl InnerInputState { name: "focusin", element: Some(id), data: Arc::new(FocusData {}), + bubbles: event_bubbles("focusin"), }); } if let Some(id) = old_focus { @@ -203,6 +205,7 @@ impl InnerInputState { name: "focusout", element: Some(id), data: Arc::new(FocusData {}), + bubbles: event_bubbles("focusout"), }); } } From c52a9334cb5339525c3a6a75a620ae428d5a21b3 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 24 Jun 2022 14:05:19 -0500 Subject: [PATCH 11/39] fix tui tests --- packages/tui/tests/events.rs | 50 ++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/packages/tui/tests/events.rs b/packages/tui/tests/events.rs index ffe9ea9ec..ab6b70b2e 100644 --- a/packages/tui/tests/events.rs +++ b/packages/tui/tests/events.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent}; use dioxus_core::VNode; use dioxus_core::*; @@ -7,6 +5,31 @@ use dioxus_core_macro::*; use dioxus_hooks::*; use dioxus_html as dioxus_elements; use dioxus_tui::TuiContext; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +/// The tui renderer will look for any event that has occured or any future that has resolved in a loop. +/// It will resolve at most one event per loop. +/// This future will resolve after a certain number of polls. If the number of polls is greater than the number of events triggered, and the event has not been recieved there is an issue with the event system. +struct PollN(usize); +impl PollN { + fn new(n: usize) -> Self { + PollN(n) + } +} +impl Future for PollN { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + if self.0 == 0 { + Poll::Ready(()) + } else { + self.0 -= 1; + Poll::Pending + } + } +} #[test] fn key_down() { @@ -17,12 +40,17 @@ fn key_down() { let tui_ctx: TuiContext = cx.consume_context().unwrap(); let render_count_handle = render_count.clone(); cx.spawn(async move { - tokio::time::sleep(Duration::from_millis(100)).await; + PollN::new(3).await; render_count_handle.modify(|x| *x + 1); }); if *render_count.get() > 2 { panic!("Event was not received"); } + // focus the element + tui_ctx.inject_event(Event::Key(KeyEvent { + code: KeyCode::Tab, + modifiers: KeyModifiers::NONE, + })); tui_ctx.inject_event(Event::Key(KeyEvent { code: KeyCode::Char('a'), modifiers: KeyModifiers::NONE, @@ -49,7 +77,7 @@ fn mouse_down() { let tui_ctx: TuiContext = cx.consume_context().unwrap(); let render_count_handle = render_count.clone(); cx.spawn(async move { - tokio::time::sleep(Duration::from_millis(100)).await; + PollN::new(2).await; render_count_handle.modify(|x| *x + 1); }); if *render_count.get() > 2 { @@ -83,7 +111,7 @@ fn mouse_up() { let tui_ctx: TuiContext = cx.consume_context().unwrap(); let render_count_handle = render_count.clone(); cx.spawn(async move { - tokio::time::sleep(Duration::from_millis(100)).await; + PollN::new(3).await; render_count_handle.modify(|x| *x + 1); }); if *render_count.get() > 2 { @@ -122,7 +150,7 @@ fn mouse_enter() { let tui_ctx: TuiContext = cx.consume_context().unwrap(); let render_count_handle = render_count.clone(); cx.spawn(async move { - tokio::time::sleep(Duration::from_millis(100)).await; + PollN::new(3).await; render_count_handle.modify(|x| *x + 1); }); if *render_count.get() > 2 { @@ -161,7 +189,7 @@ fn mouse_exit() { let tui_ctx: TuiContext = cx.consume_context().unwrap(); let render_count_handle = render_count.clone(); cx.spawn(async move { - tokio::time::sleep(Duration::from_millis(100)).await; + PollN::new(3).await; render_count_handle.modify(|x| *x + 1); }); if *render_count.get() > 2 { @@ -200,7 +228,7 @@ fn mouse_move() { let tui_ctx: TuiContext = cx.consume_context().unwrap(); let render_count_handle = render_count.clone(); cx.spawn(async move { - tokio::time::sleep(Duration::from_millis(100)).await; + PollN::new(3).await; render_count_handle.modify(|x| *x + 1); }); if *render_count.get() > 2 { @@ -239,7 +267,7 @@ fn wheel() { let tui_ctx: TuiContext = cx.consume_context().unwrap(); let render_count_handle = render_count.clone(); cx.spawn(async move { - tokio::time::sleep(Duration::from_millis(100)).await; + PollN::new(3).await; render_count_handle.modify(|x| *x + 1); }); if *render_count.get() > 2 { @@ -279,7 +307,7 @@ fn click() { let tui_ctx: TuiContext = cx.consume_context().unwrap(); let render_count_handle = render_count.clone(); cx.spawn(async move { - tokio::time::sleep(Duration::from_millis(100)).await; + PollN::new(3).await; render_count_handle.modify(|x| *x + 1); }); if *render_count.get() > 2 { @@ -318,7 +346,7 @@ fn context_menu() { let tui_ctx: TuiContext = cx.consume_context().unwrap(); let render_count_handle = render_count.clone(); cx.spawn(async move { - tokio::time::sleep(Duration::from_millis(100)).await; + PollN::new(3).await; render_count_handle.modify(|x| *x + 1); }); if *render_count.get() > 2 { From 1c31fd0e22a9c468b53e403447d7dac55240c9d1 Mon Sep 17 00:00:00 2001 From: YuKun Liu Date: Sat, 25 Jun 2022 15:36:47 +0800 Subject: [PATCH 12/39] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7db59226c..441f2c417 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ desktop = ["dioxus-desktop"] router = ["dioxus-router"] tui = ["dioxus-tui"] liveview = ["dioxus-liveview"] -hot_reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot_reload", "dioxus-web?/hot_reload"] +hot-reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot_reload", "dioxus-web?/hot_reload"] native-core = ["dioxus-native-core", "dioxus-native-core-macro"] From fbf8e13b8da67bd5a7e9f4cb45cccceeabc568c6 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 25 Jun 2022 08:27:10 -0500 Subject: [PATCH 13/39] clean up imports --- Cargo.toml | 2 +- packages/native-core-macro/Cargo.toml | 4 +-- .../native-core-macro/tests/change_nodes.rs | 7 ++--- .../native-core-macro/tests/initial_build.rs | 7 ++--- .../tests/peristant_iterator.rs | 19 ++---------- .../native-core-macro/tests/update_state.rs | 10 +++---- packages/router/Cargo.toml | 5 ++-- packages/router/examples/simple.rs | 4 +-- packages/router/src/components/link.rs | 5 +--- packages/router/src/components/redirect.rs | 4 +-- packages/router/src/components/route.rs | 7 +---- packages/router/src/components/router.rs | 5 +--- packages/router/src/hooks/use_route.rs | 2 +- packages/router/src/hooks/use_router.rs | 2 +- packages/router/src/service.rs | 2 +- packages/router/tests/ssr_router.rs | 4 +-- packages/rsx-prelude/Cargo.toml | 22 ++++++++++++++ packages/rsx-prelude/src/lib.rs | 30 +++++++++++++++++++ packages/rsx_interpreter/Cargo.toml | 1 + packages/rsx_interpreter/tests/render.rs | 8 +---- packages/ssr/Cargo.toml | 5 +--- packages/ssr/tests/renders.rs | 4 +-- packages/tui/Cargo.toml | 3 +- packages/tui/tests/events.rs | 6 +--- packages/web/Cargo.toml | 4 +-- packages/web/examples/hydrate.rs | 4 +-- packages/web/src/lib.rs | 5 ++-- packages/web/tests/hydrate.rs | 4 +-- 28 files changed, 90 insertions(+), 95 deletions(-) create mode 100644 packages/rsx-prelude/Cargo.toml create mode 100644 packages/rsx-prelude/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 7db59226c..0512ddae9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,6 @@ liveview = ["dioxus-liveview"] hot_reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot_reload", "dioxus-web?/hot_reload"] native-core = ["dioxus-native-core", "dioxus-native-core-macro"] - [workspace] members = [ "packages/core", @@ -71,6 +70,7 @@ members = [ "packages/rsx_interpreter", "packages/native-core", "packages/native-core-macro", + "packages/rsx-prelude", ] [dev-dependencies] diff --git a/packages/native-core-macro/Cargo.toml b/packages/native-core-macro/Cargo.toml index 837f8890e..2d2c868ef 100644 --- a/packages/native-core-macro/Cargo.toml +++ b/packages/native-core-macro/Cargo.toml @@ -20,9 +20,7 @@ quote = "1.0" dioxus-native-core = { path = "../native-core", version = "^0.2.0" } [dev-dependencies] -dioxus-core = { path = "../core", version = "^0.2.1" } -dioxus-html = { path = "../html", version = "^0.2.1" } -dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" } +dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude", features = ["hot_reload"] } smallvec = "1.6" fxhash = "0.2" diff --git a/packages/native-core-macro/tests/change_nodes.rs b/packages/native-core-macro/tests/change_nodes.rs index 8f4e6c8f0..93cb8bce8 100644 --- a/packages/native-core-macro/tests/change_nodes.rs +++ b/packages/native-core-macro/tests/change_nodes.rs @@ -1,7 +1,6 @@ -use dioxus_core::VNode; -use dioxus_core::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::core as dioxus_core; +use dioxus::core::{ElementId, VElement}; +use dioxus::prelude::*; use dioxus_native_core::real_dom::RealDom; use dioxus_native_core::state::State; use dioxus_native_core_macro::State; diff --git a/packages/native-core-macro/tests/initial_build.rs b/packages/native-core-macro/tests/initial_build.rs index e6bd651e2..5f2fba7ce 100644 --- a/packages/native-core-macro/tests/initial_build.rs +++ b/packages/native-core-macro/tests/initial_build.rs @@ -1,9 +1,8 @@ use std::cell::Cell; -use dioxus_core::VNode; -use dioxus_core::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::core as dioxus_core; +use dioxus::core::{ElementId, VElement, VText}; +use dioxus::prelude::*; use dioxus_native_core::real_dom::RealDom; use dioxus_native_core::state::State; use dioxus_native_core_macro::State; diff --git a/packages/native-core-macro/tests/peristant_iterator.rs b/packages/native-core-macro/tests/peristant_iterator.rs index 29df3afe5..65b16706a 100644 --- a/packages/native-core-macro/tests/peristant_iterator.rs +++ b/packages/native-core-macro/tests/peristant_iterator.rs @@ -1,3 +1,5 @@ +use dioxus::core as dioxus_core; +use dioxus::prelude::*; use dioxus_native_core::{ real_dom::{NodeType, RealDom}, state::State, @@ -11,10 +13,6 @@ struct Empty {} #[test] #[allow(unused_variables)] fn traverse() { - use dioxus_core::*; - use dioxus_core_macro::*; - use dioxus_html as dioxus_elements; - #[allow(non_snake_case)] fn Base(cx: Scope) -> Element { rsx!(cx, div {}) @@ -108,11 +106,6 @@ fn traverse() { #[test] #[allow(unused_variables)] fn persist_removes() { - use dioxus_core::VNode; - use dioxus_core::*; - use dioxus_core_macro::*; - use dioxus_html as dioxus_elements; - #[allow(non_snake_case)] fn Base(cx: Scope) -> Element { rsx!(cx, div {}) @@ -201,10 +194,6 @@ fn persist_removes() { #[test] #[allow(unused_variables)] fn persist_instertions_before() { - use dioxus_core::*; - use dioxus_core_macro::*; - use dioxus_html as dioxus_elements; - #[allow(non_snake_case)] fn Base(cx: Scope) -> Element { rsx!(cx, div {}) @@ -271,10 +260,6 @@ fn persist_instertions_before() { #[test] #[allow(unused_variables)] fn persist_instertions_after() { - use dioxus_core::*; - use dioxus_core_macro::*; - use dioxus_html as dioxus_elements; - #[allow(non_snake_case)] fn Base(cx: Scope) -> Element { rsx!(cx, div {}) diff --git a/packages/native-core-macro/tests/update_state.rs b/packages/native-core-macro/tests/update_state.rs index 84ec5d530..1d5247f9b 100644 --- a/packages/native-core-macro/tests/update_state.rs +++ b/packages/native-core-macro/tests/update_state.rs @@ -1,9 +1,9 @@ use anymap::AnyMap; -use dioxus_core::AttributeValue; -use dioxus_core::VNode; -use dioxus_core::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::core as dioxus_core; +use dioxus::core::AttributeValue; +use dioxus::core::DomEdit; +use dioxus::core::Mutations; +use dioxus::prelude::*; use dioxus_native_core::node_ref::*; use dioxus_native_core::real_dom::*; use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index 02d6cb01a..de51108d7 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -11,9 +11,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../core", version = "^0.2.1", default-features = false } -dioxus-html = { path = "../html", version = "^0.2.1", default-features = false } -dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" } +dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude" } futures-channel = "0.3.21" url = { version = "2.2.2", default-features = false } @@ -44,6 +42,7 @@ default = ["query"] web = ["web-sys", "gloo-events", "js-sys", "wasm-bindgen"] query = ["serde", "serde_urlencoded"] wasm_test = [] +hot_reload = ["dioxus/hot_reload"] [dev-dependencies] console_error_panic_hook = "0.1.7" diff --git a/packages/router/examples/simple.rs b/packages/router/examples/simple.rs index a30a004d7..c8aba6a20 100644 --- a/packages/router/examples/simple.rs +++ b/packages/router/examples/simple.rs @@ -1,8 +1,6 @@ #![allow(non_snake_case)] -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; use dioxus_router::*; fn main() { diff --git a/packages/router/src/components/link.rs b/packages/router/src/components/link.rs index a2d03fba3..732ed8470 100644 --- a/packages/router/src/components/link.rs +++ b/packages/router/src/components/link.rs @@ -1,10 +1,7 @@ use std::sync::Arc; use crate::{use_route, RouterCore}; -use dioxus_core as dioxus; -use dioxus_core::prelude::*; -use dioxus_core_macro::{format_args_f, rsx, Props}; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; /// Props for the [`Link`](struct.Link.html) component. #[derive(Props)] diff --git a/packages/router/src/components/redirect.rs b/packages/router/src/components/redirect.rs index 7a7d85be2..3e3361ab9 100644 --- a/packages/router/src/components/redirect.rs +++ b/packages/router/src/components/redirect.rs @@ -1,6 +1,4 @@ -use dioxus_core as dioxus; -use dioxus_core::prelude::*; -use dioxus_core_macro::Props; +use dioxus::prelude::*; use crate::use_router; diff --git a/packages/router/src/components/route.rs b/packages/router/src/components/route.rs index 90334871e..6a80124d5 100644 --- a/packages/router/src/components/route.rs +++ b/packages/router/src/components/route.rs @@ -1,11 +1,6 @@ +use dioxus::prelude::*; use std::sync::Arc; -use dioxus_core as dioxus; -use dioxus_core::prelude::*; -use dioxus_core_macro::Props; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; - use crate::{RouteContext, RouterCore}; /// Props for the [`Route`](struct.Route.html) component. diff --git a/packages/router/src/components/router.rs b/packages/router/src/components/router.rs index 85adfa422..d29c2ee4d 100644 --- a/packages/router/src/components/router.rs +++ b/packages/router/src/components/router.rs @@ -1,9 +1,6 @@ use crate::ParsedRoute; use crate::{cfg::RouterCfg, RouteEvent, RouterCore}; -use dioxus_core as dioxus; -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; use futures_util::stream::StreamExt; use std::sync::Arc; diff --git a/packages/router/src/hooks/use_route.rs b/packages/router/src/hooks/use_route.rs index a37c6f102..abb0ea4a5 100644 --- a/packages/router/src/hooks/use_route.rs +++ b/packages/router/src/hooks/use_route.rs @@ -1,5 +1,5 @@ use crate::{ParsedRoute, RouteContext, RouterCore, RouterService}; -use dioxus_core::{ScopeId, ScopeState}; +use dioxus::core::{ScopeId, ScopeState}; use std::{borrow::Cow, str::FromStr, sync::Arc}; use url::Url; diff --git a/packages/router/src/hooks/use_router.rs b/packages/router/src/hooks/use_router.rs index 5e8fb71b6..25a9a471f 100644 --- a/packages/router/src/hooks/use_router.rs +++ b/packages/router/src/hooks/use_router.rs @@ -1,5 +1,5 @@ use crate::RouterService; -use dioxus_core::ScopeState; +use dioxus::core::ScopeState; /// This hook provides access to the `RouterService` for the app. pub fn use_router(cx: &ScopeState) -> &RouterService { diff --git a/packages/router/src/service.rs b/packages/router/src/service.rs index ad1b6ecb1..72f35d51e 100644 --- a/packages/router/src/service.rs +++ b/packages/router/src/service.rs @@ -2,7 +2,7 @@ // does each window have its own router? probably, lol use crate::cfg::RouterCfg; -use dioxus_core::ScopeId; +use dioxus::core::ScopeId; use futures_channel::mpsc::UnboundedSender; use std::any::Any; use std::{ diff --git a/packages/router/tests/ssr_router.rs b/packages/router/tests/ssr_router.rs index 212daa7cc..a362baa68 100644 --- a/packages/router/tests/ssr_router.rs +++ b/packages/router/tests/ssr_router.rs @@ -1,8 +1,6 @@ #![allow(non_snake_case)] -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; use dioxus_router::*; #[test] diff --git a/packages/rsx-prelude/Cargo.toml b/packages/rsx-prelude/Cargo.toml new file mode 100644 index 000000000..f1c389ada --- /dev/null +++ b/packages/rsx-prelude/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "rsx-prelude" +version = "0.1.0" +authors = ["Jonathan Kelley"] +edition = "2021" +description = "Basic functionality for the dioxus rsx macro." +license = "MIT OR Apache-2.0" +repository = "https://github.com/DioxusLabs/dioxus/" +homepage = "https://dioxuslabs.com" +documentation = "https://dioxuslabs.com" +keywords = ["dom", "ui", "gui", "react", "wasm"] +rust-version = "1.60.0" + +[dependencies] +dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" } +dioxus-core = { path = "../core", version = "^0.2.1" } +dioxus-html = { path = "../html", version = "^0.2.1" } +dioxus-rsx-interpreter = { path = "../rsx_interpreter", version = "^0.1.0", optional = true } +dioxus-hooks = { path = "../hooks", version = "^0.2.1" } + +[features] +hot_reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter"] diff --git a/packages/rsx-prelude/src/lib.rs b/packages/rsx-prelude/src/lib.rs new file mode 100644 index 000000000..e00ee6669 --- /dev/null +++ b/packages/rsx-prelude/src/lib.rs @@ -0,0 +1,30 @@ +//! This package is meant for internal use within dioxus. It provides a prelude that enables basic components to work. + +pub use dioxus_core as core; + +pub mod hooks { + pub use dioxus_hooks::*; +} + +pub use hooks::*; + +pub mod events { + pub use dioxus_html::{on::*, KeyCode}; +} + +#[cfg(feature = "hot_reload")] +pub use dioxus_rsx_interpreter as rsx_interpreter; + +pub mod prelude { + pub use crate::hooks::*; + pub use dioxus_core::prelude::*; + pub use dioxus_core_macro::{format_args_f, inline_props, rsx, Props}; + pub use dioxus_elements::{GlobalAttributes, SvgAttributes}; + pub use dioxus_html as dioxus_elements; + + #[cfg(feature = "hot_reload")] + pub use dioxus_rsx_interpreter::{ + captuered_context::{CapturedContext, FormattedArg, IfmtArgs}, + get_line_num, resolve_scope, CodeLocation, RsxContext, + }; +} diff --git a/packages/rsx_interpreter/Cargo.toml b/packages/rsx_interpreter/Cargo.toml index 0b1f79a5d..a4d54f355 100644 --- a/packages/rsx_interpreter/Cargo.toml +++ b/packages/rsx_interpreter/Cargo.toml @@ -21,3 +21,4 @@ dioxus-hooks = { path = "../hooks"} [dev-dependencies] dioxus-core-macro = { path = "../core-macro" } bumpalo = { version = "3.6", features = ["collections", "boxed"] } +dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude", features = ["hot_reload"] } \ No newline at end of file diff --git a/packages/rsx_interpreter/tests/render.rs b/packages/rsx_interpreter/tests/render.rs index ed139aec1..cc724db97 100644 --- a/packages/rsx_interpreter/tests/render.rs +++ b/packages/rsx_interpreter/tests/render.rs @@ -1,10 +1,4 @@ -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; -use dioxus_rsx_interpreter::{ - captuered_context::{CapturedContext, FormattedArg, IfmtArgs}, - CodeLocation, -}; +use dioxus::prelude::*; #[test] #[allow(non_snake_case)] diff --git a/packages/ssr/Cargo.toml b/packages/ssr/Cargo.toml index a2c238b35..984881633 100644 --- a/packages/ssr/Cargo.toml +++ b/packages/ssr/Cargo.toml @@ -15,11 +15,8 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] dioxus-core = { path = "../core", version = "^0.2.1", features = ["serialize"] } - [dev-dependencies] -dioxus-hooks = { path = "../hooks" } -dioxus-html = { path = "../html" } -dioxus-core-macro = { path = "../core-macro" } +dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude" } thiserror = "1.0.23" log = "0.4.13" fern = { version = "0.6.0", features = ["colored"] } diff --git a/packages/ssr/tests/renders.rs b/packages/ssr/tests/renders.rs index b948a5604..64a760e6a 100644 --- a/packages/ssr/tests/renders.rs +++ b/packages/ssr/tests/renders.rs @@ -1,6 +1,4 @@ -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; use dioxus_ssr::{render_lazy, render_vdom, render_vdom_cfg, SsrConfig, SsrRenderer, TextRenderer}; static SIMPLE_APP: Component = |cx| { diff --git a/packages/tui/Cargo.toml b/packages/tui/Cargo.toml index 88584cc4b..d201402e6 100644 --- a/packages/tui/Cargo.toml +++ b/packages/tui/Cargo.toml @@ -29,6 +29,5 @@ fxhash = "0.2" anymap = "0.12.1" [dev-dependencies] -dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" } -dioxus-hooks = { path = "../hooks", version = "^0.2.1" } +dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude" } tokio = { version = "1" } \ No newline at end of file diff --git a/packages/tui/tests/events.rs b/packages/tui/tests/events.rs index ffe9ea9ec..61259741b 100644 --- a/packages/tui/tests/events.rs +++ b/packages/tui/tests/events.rs @@ -1,11 +1,7 @@ use std::time::Duration; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent}; -use dioxus_core::VNode; -use dioxus_core::*; -use dioxus_core_macro::*; -use dioxus_hooks::*; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; use dioxus_tui::TuiContext; #[test] diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 2fa4a2f6b..90e7c20af 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -16,7 +16,7 @@ dioxus-html = { path = "../html", version = "^0.2.1", features = ["wasm-bind"] } dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features = [ "web" ] } -dioxus-rsx-interpreter = { path = "../rsx_interpreter", version = "*", optional = true } +dioxus-rsx-interpreter = { path = "../rsx_interpreter", version = "^0.1.0", optional = true } js-sys = "0.3.56" wasm-bindgen = { version = "0.2.79", features = ["enable-interning"] } @@ -80,7 +80,7 @@ panic_hook = ["console_error_panic_hook"] hot_reload = ["dioxus-rsx-interpreter", "web-sys/WebSocket", "web-sys/Location", "web-sys/MessageEvent", "web-sys/console", "serde_json"] [dev-dependencies] -dioxus-core-macro = { path = "../core-macro" } +dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude" } wasm-bindgen-test = "0.3.29" dioxus-ssr = { path = "../ssr" } wasm-logger = "0.2.0" diff --git a/packages/web/examples/hydrate.rs b/packages/web/examples/hydrate.rs index 7430b6b51..d666b498d 100644 --- a/packages/web/examples/hydrate.rs +++ b/packages/web/examples/hydrate.rs @@ -1,6 +1,4 @@ -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; use web_sys::window; fn app(cx: Scope) -> Element { diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index cb0e92155..4b9c44ead 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -58,10 +58,9 @@ use std::rc::Rc; pub use crate::cfg::WebConfig; pub use crate::util::use_eval; -use dioxus::SchedulerMsg; -use dioxus::VirtualDom; -pub use dioxus_core as dioxus; use dioxus_core::prelude::Component; +use dioxus_core::SchedulerMsg; +use dioxus_core::VirtualDom; use futures_util::FutureExt; mod cache; diff --git a/packages/web/tests/hydrate.rs b/packages/web/tests/hydrate.rs index 76f851c83..a7236b94f 100644 --- a/packages/web/tests/hydrate.rs +++ b/packages/web/tests/hydrate.rs @@ -1,6 +1,4 @@ -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; use wasm_bindgen_test::wasm_bindgen_test; use web_sys::window; From 6c20d74797333424caa405e13e00475b63f2dfdb Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 25 Jun 2022 08:48:03 -0500 Subject: [PATCH 14/39] finish cleanup --- Cargo.toml | 4 ++-- benches/jsframework.rs | 5 +---- packages/native-core-macro/tests/update_state.rs | 4 +--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0512ddae9..dd6775dac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ desktop = ["dioxus-desktop"] router = ["dioxus-router"] tui = ["dioxus-tui"] liveview = ["dioxus-liveview"] -hot_reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot_reload", "dioxus-web?/hot_reload"] +hot_reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot_reload", "dioxus-web?/hot_reload", "dioxus-router?/hot_reload"] native-core = ["dioxus-native-core", "dioxus-native-core-macro"] [workspace] @@ -85,7 +85,7 @@ serde_json = "1.0.79" rand = { version = "0.8.4", features = ["small_rng"] } tokio = { version = "1.16.1", features = ["full"] } reqwest = { version = "0.11.9", features = ["json"] } -dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui"] } +dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui", "hot_reload"] } fern = { version = "0.6.0", features = ["colored"] } criterion = "0.3.5" thiserror = "1.0.30" diff --git a/benches/jsframework.rs b/benches/jsframework.rs index 8a6fcaf30..c0fbf4764 100644 --- a/benches/jsframework.rs +++ b/benches/jsframework.rs @@ -13,10 +13,7 @@ //! These numbers don't represent Dioxus with the heuristic engine installed, so I assume it'll be even faster. use criterion::{criterion_group, criterion_main, Criterion}; -use dioxus_core as dioxus; -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; use rand::prelude::*; criterion_group!(mbenches, create_rows); diff --git a/packages/native-core-macro/tests/update_state.rs b/packages/native-core-macro/tests/update_state.rs index 1d5247f9b..28440803b 100644 --- a/packages/native-core-macro/tests/update_state.rs +++ b/packages/native-core-macro/tests/update_state.rs @@ -1,8 +1,6 @@ use anymap::AnyMap; use dioxus::core as dioxus_core; -use dioxus::core::AttributeValue; -use dioxus::core::DomEdit; -use dioxus::core::Mutations; +use dioxus::core::{AttributeValue, DomEdit, Mutations}; use dioxus::prelude::*; use dioxus_native_core::node_ref::*; use dioxus_native_core::real_dom::*; From 02756f6f922260dd82136699e348ef4ccdbe9f7e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 25 Jun 2022 10:14:36 -0500 Subject: [PATCH 15/39] fix key parsing --- packages/rsx_interpreter/src/interperter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rsx_interpreter/src/interperter.rs b/packages/rsx_interpreter/src/interperter.rs index 3268fee81..f35488c05 100644 --- a/packages/rsx_interpreter/src/interperter.rs +++ b/packages/rsx_interpreter/src/interperter.rs @@ -191,7 +191,7 @@ fn build_node<'a>( None, )), Some(lit) => { - let ifmt: IfmtInput = parse_str(&lit.value()).map_err(|err| { + let ifmt: IfmtInput = lit.value().parse().map_err(|err| { Error::ParseError(ParseError::new(err, ctx.location.clone())) })?; let key = bump.alloc(resolve_ifmt(&ifmt, &ctx.captured)?); From 9a963bba0acf9ac4f789ab1cc926fb80a1fe111a Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 25 Jun 2022 11:17:26 -0500 Subject: [PATCH 16/39] update router test imports --- packages/router/tests/web_router.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/router/tests/web_router.rs b/packages/router/tests/web_router.rs index 40b3a764f..94f57e445 100644 --- a/packages/router/tests/web_router.rs +++ b/packages/router/tests/web_router.rs @@ -1,8 +1,6 @@ #![cfg(target_arch = "wasm32")] -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; +use dioxus::prelude::*; use dioxus_router::*; use gloo_utils::document; use serde::{Deserialize, Serialize}; From 0af69f83f29a4fda32b5397cf1249251649249c7 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sun, 26 Jun 2022 14:23:19 +0800 Subject: [PATCH 17/39] Rename `hot_reload` to `hot-reload` --- Cargo.toml | 2 +- docs/reference/src/guide/hot_reloading.md | 4 ++-- packages/core-macro/Cargo.toml | 2 +- packages/core-macro/src/lib.rs | 6 +++--- packages/desktop/Cargo.toml | 2 +- packages/desktop/src/controller.rs | 2 +- packages/web/Cargo.toml | 2 +- packages/web/src/lib.rs | 2 +- src/lib.rs | 4 ++-- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 441f2c417..7266b6cee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ desktop = ["dioxus-desktop"] router = ["dioxus-router"] tui = ["dioxus-tui"] liveview = ["dioxus-liveview"] -hot-reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot_reload", "dioxus-web?/hot_reload"] +hot-reload = ["dioxus-core-macro/hot-reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot-reload", "dioxus-web?/hot-reload"] native-core = ["dioxus-native-core", "dioxus-native-core-macro"] diff --git a/docs/reference/src/guide/hot_reloading.md b/docs/reference/src/guide/hot_reloading.md index eff0c8a54..8560c62f9 100644 --- a/docs/reference/src/guide/hot_reloading.md +++ b/docs/reference/src/guide/hot_reloading.md @@ -5,9 +5,9 @@ # Setup Install [dioxus-cli](https://github.com/DioxusLabs/cli). -Enable the hot_reload feature on dioxus: +Enable the hot-reload feature on dioxus: ```toml -dioxus = { version = "*", features = ["web", "hot_reload"] } +dioxus = { version = "*", features = ["web", "hot-reload"] } ``` # Usage diff --git a/packages/core-macro/Cargo.toml b/packages/core-macro/Cargo.toml index 80502cf66..f6b352689 100644 --- a/packages/core-macro/Cargo.toml +++ b/packages/core-macro/Cargo.toml @@ -28,4 +28,4 @@ trybuild = "1.0" [features] default = [] -hot_reload = ["dioxus-rsx-interpreter"] \ No newline at end of file +hot-reload = ["dioxus-rsx-interpreter"] \ No newline at end of file diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 56d82876f..35298703c 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -34,12 +34,12 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token /// ``` #[proc_macro] pub fn rsx(s: TokenStream) -> TokenStream { - #[cfg(feature = "hot_reload")] + #[cfg(feature = "hot-reload")] let rsx_text = s.to_string(); match syn::parse::(s) { Err(err) => err.to_compile_error().into(), Ok(body) => { - #[cfg(feature = "hot_reload")] + #[cfg(feature = "hot-reload")] { use dioxus_rsx_interpreter::captuered_context::CapturedContextBuilder; @@ -66,7 +66,7 @@ pub fn rsx(s: TokenStream) -> TokenStream { Err(err) => err.into_compile_error().into(), } } - #[cfg(not(feature = "hot_reload"))] + #[cfg(not(feature = "hot-reload"))] body.to_token_stream().into() } } diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index de07bb88a..c5beefbdc 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -44,7 +44,7 @@ tokio_runtime = ["tokio"] fullscreen = ["wry/fullscreen"] transparent = ["wry/transparent"] tray = ["wry/tray"] -hot_reload = ["dioxus-rsx-interpreter", "interprocess"] +hot-reload = ["dioxus-rsx-interpreter", "interprocess"] [dev-dependencies] dioxus-core-macro = { path = "../core-macro" } diff --git a/packages/desktop/src/controller.rs b/packages/desktop/src/controller.rs index b6a954b01..6c0c7feef 100644 --- a/packages/desktop/src/controller.rs +++ b/packages/desktop/src/controller.rs @@ -51,7 +51,7 @@ impl DesktopController { dom.base_scope().provide_context(window_context); // allow other proccesses to send the new rsx text to the @dioxusin ipc channel and recieve erros on the @dioxusout channel - #[cfg(feature = "hot_reload")] + #[cfg(feature = "hot-reload")] { use dioxus_rsx_interpreter::{ error::Error, ErrorHandler, SetManyRsxMessage, RSX_CONTEXT, diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 2fa4a2f6b..e73f1512c 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -77,7 +77,7 @@ features = [ [features] default = ["panic_hook"] panic_hook = ["console_error_panic_hook"] -hot_reload = ["dioxus-rsx-interpreter", "web-sys/WebSocket", "web-sys/Location", "web-sys/MessageEvent", "web-sys/console", "serde_json"] +hot-reload = ["dioxus-rsx-interpreter", "web-sys/WebSocket", "web-sys/Location", "web-sys/MessageEvent", "web-sys/console", "serde_json"] [dev-dependencies] dioxus-core-macro = { path = "../core-macro" } diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index cb0e92155..abffaa66e 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -217,7 +217,7 @@ pub async fn run_with_props(root: Component, root_props: T let mut work_loop = ric_raf::RafLoop::new(); - #[cfg(feature = "hot_reload")] + #[cfg(feature = "hot-reload")] { use dioxus_rsx_interpreter::error::Error; use dioxus_rsx_interpreter::{ErrorHandler, SetManyRsxMessage, RSX_CONTEXT}; diff --git a/src/lib.rs b/src/lib.rs index a28d6c7f5..bd728ae2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ pub mod events { pub use dioxus_rsx as rsx; -#[cfg(feature = "hot_reload")] +#[cfg(feature = "hot-reload")] pub use dioxus_rsx_interpreter as rsx_interpreter; pub mod prelude { @@ -62,7 +62,7 @@ pub mod prelude { #[cfg(feature = "fermi")] pub use fermi::{use_atom_ref, use_init_atom_root, use_read, use_set, Atom, AtomRef}; - #[cfg(feature = "hot_reload")] + #[cfg(feature = "hot-reload")] pub use dioxus_rsx_interpreter::{ captuered_context::{CapturedContext, FormattedArg, IfmtArgs}, get_line_num, resolve_scope, CodeLocation, RsxContext, From 706b6c957bc091e4cf74572ef8da90cf418ea8f0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 26 Jun 2022 07:26:03 -0500 Subject: [PATCH 18/39] change hot_reload to hot-reload in dev-dependancies --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ee227d7d1..34c4a2043 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,7 @@ serde_json = "1.0.79" rand = { version = "0.8.4", features = ["small_rng"] } tokio = { version = "1.16.1", features = ["full"] } reqwest = { version = "0.11.9", features = ["json"] } -dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui", "hot_reload"] } +dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui", "hot-reload"] } fern = { version = "0.6.0", features = ["colored"] } criterion = "0.3.5" thiserror = "1.0.30" From 4106f07566979b68f4120ab36517960b96dfde09 Mon Sep 17 00:00:00 2001 From: Mark Wotton Date: Tue, 28 Jun 2022 09:58:27 +0700 Subject: [PATCH 19/39] tiny doc fix --- packages/fermi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fermi/README.md b/packages/fermi/README.md index 0923aa6f6..ae89ea73c 100644 --- a/packages/fermi/README.md +++ b/packages/fermi/README.md @@ -69,7 +69,7 @@ It's that simple! Fermi is currently under construction, so you have to use the `master` branch to get started. ```rust -[depdencies] +[dependencies] fermi = { git = "https://github.com/dioxuslabs/fermi" } ``` From 884723831bb677e5c28bdce74ddd54036363096e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 28 Jun 2022 09:48:19 -0500 Subject: [PATCH 20/39] fix path seperator --- packages/rsx_interpreter/src/lib.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/rsx_interpreter/src/lib.rs b/packages/rsx_interpreter/src/lib.rs index 8f74ce29b..cfda4c2ed 100644 --- a/packages/rsx_interpreter/src/lib.rs +++ b/packages/rsx_interpreter/src/lib.rs @@ -24,7 +24,8 @@ lazy_static! { // the location of the code relative to the current crate based on [std::panic::Location] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct CodeLocation { - pub file: String, + pub crate_path: String, + pub file_path: String, pub line: u32, pub column: u32, } @@ -83,14 +84,12 @@ macro_rules! get_line_num { () => {{ let line = line!(); let column = column!(); - let file = file!(); + let file_path = file!().to_string(); + let mut crate_path = env!("CARGO_MANIFEST_DIR").to_string(); - #[cfg(windows)] - let file = env!("CARGO_MANIFEST_DIR").to_string() + "\\" + file!(); - #[cfg(unix)] - let file = env!("CARGO_MANIFEST_DIR").to_string() + "/" + file!(); CodeLocation { - file: file.to_string(), + crate_path, + file_path, line: line, column: column, } From 8c3c75cab6b913982af1642b1bb3a57b2cd84f28 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 28 Jun 2022 10:02:50 -0500 Subject: [PATCH 21/39] update tests --- packages/rsx_interpreter/tests/render.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/rsx_interpreter/tests/render.rs b/packages/rsx_interpreter/tests/render.rs index ed139aec1..5d2d2bf8c 100644 --- a/packages/rsx_interpreter/tests/render.rs +++ b/packages/rsx_interpreter/tests/render.rs @@ -16,7 +16,8 @@ fn render_basic() { let dom = VirtualDom::new(Base); let static_vnodes = rsx!(div{"hello world"}); let location = CodeLocation { - file: String::new(), + file_path: String::new(), + crate_path: String::new(), line: 0, column: 0, }; @@ -61,7 +62,8 @@ fn render_nested() { } }; let location = CodeLocation { - file: String::new(), + file_path: String::new(), + crate_path: String::new(), line: 1, column: 0, }; @@ -112,7 +114,8 @@ fn render_component() { } }; let location = CodeLocation { - file: String::new(), + file_path: String::new(), + crate_path: String::new(), line: 2, column: 0, }; @@ -163,7 +166,8 @@ fn render_iterator() { } }; let location = CodeLocation { - file: String::new(), + file_path: String::new(), + crate_path: String::new(), line: 3, column: 0, }; @@ -216,7 +220,8 @@ fn render_captured_variable() { } }; let location = CodeLocation { - file: String::new(), + file_path: String::new(), + crate_path: String::new(), line: 4, column: 0, }; @@ -267,7 +272,8 @@ fn render_listener() { } }; let location = CodeLocation { - file: String::new(), + file_path: String::new(), + crate_path: String::new(), line: 5, column: 0, }; From 77304012880beab11e55b5650b1e9efbd5b96cff Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 28 Jun 2022 12:19:07 -0500 Subject: [PATCH 22/39] fix escape sequences in ifmt --- packages/core-macro/tests/ifmt.rs | 29 +++++++++++++++++++++++++++++ packages/rsx/src/ifmt.rs | 16 +++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 packages/core-macro/tests/ifmt.rs diff --git a/packages/core-macro/tests/ifmt.rs b/packages/core-macro/tests/ifmt.rs new file mode 100644 index 000000000..52ac347b3 --- /dev/null +++ b/packages/core-macro/tests/ifmt.rs @@ -0,0 +1,29 @@ +use std::borrow::Borrow; + +use dioxus_core_macro::*; + +#[test] +fn formatting_compiles() { + let x = (0, 1); + // escape sequences work + assert_eq!( + format_args_f!("{x:?} {{}}}}").to_string(), + format!("{:?} {{}}}}", x).to_string() + ); + assert_eq!( + format_args_f!("{{{{}} {x:?}").to_string(), + format!("{{{{}} {:?}", x).to_string() + ); + + // paths in formating works + assert_eq!( + format_args_f!("{x.0}").to_string(), + format!("{}", x.0).to_string() + ); + + // function calls in formatings work + assert_eq!( + format_args_f!("{x.borrow():?}").to_string(), + format!("{:?}", x.borrow()).to_string() + ); +} diff --git a/packages/rsx/src/ifmt.rs b/packages/rsx/src/ifmt.rs index 2d77a243f..58d52c7d1 100644 --- a/packages/rsx/src/ifmt.rs +++ b/packages/rsx/src/ifmt.rs @@ -14,7 +14,7 @@ pub fn format_args_f_impl(input: IfmtInput) -> Result { let mut expr_counter = 0; for segment in input.segments.iter() { match segment { - Segment::Literal(s) => format_literal += s, + Segment::Literal(s) => format_literal += &s.replace('{', "{{").replace('}', "}}"), Segment::Formatted { format_args, segment, @@ -94,7 +94,7 @@ impl FromStr for IfmtInput { while let Some(c) = chars.next() { if c == ':' { let mut current_format_args = String::new(); - for c in chars.by_ref() { + while let Some(c) = chars.next() { if c == '}' { segments.push(Segment::Formatted { format_args: current_format_args, @@ -116,6 +116,17 @@ impl FromStr for IfmtInput { current_captured.push(c); } } else { + if '}' == c { + if let Some(c) = chars.next_if(|c| *c == '}') { + current_literal.push(c); + continue; + } else { + return Err(Error::new( + Span::call_site(), + "unmatched closing '}' in format string", + )); + } + } current_literal.push(c); } } @@ -146,7 +157,6 @@ impl FormattedSegment { return Ok(Self::Ident(ident)); } } - // if let Ok(expr) = parse_str(&("{".to_string() + input + "}")) { if let Ok(expr) = parse_str(input) { Ok(Self::Expr(Box::new(expr))) } else { From f1f9f0df1a6ad7a084a9b5b923cd3421148b69cf Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 28 Jun 2022 12:37:19 -0500 Subject: [PATCH 23/39] pass clippy --- packages/rsx/src/ifmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rsx/src/ifmt.rs b/packages/rsx/src/ifmt.rs index 58d52c7d1..7972a5f86 100644 --- a/packages/rsx/src/ifmt.rs +++ b/packages/rsx/src/ifmt.rs @@ -94,7 +94,7 @@ impl FromStr for IfmtInput { while let Some(c) = chars.next() { if c == ':' { let mut current_format_args = String::new(); - while let Some(c) = chars.next() { + for c in chars.by_ref() { if c == '}' { segments.push(Segment::Formatted { format_args: current_format_args, From cf2f504d776365fd695547b8d7da3710138e7488 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 28 Jun 2022 12:48:38 -0500 Subject: [PATCH 24/39] fix unnessisary mut --- packages/rsx_interpreter/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rsx_interpreter/src/lib.rs b/packages/rsx_interpreter/src/lib.rs index cfda4c2ed..ff50dcdb2 100644 --- a/packages/rsx_interpreter/src/lib.rs +++ b/packages/rsx_interpreter/src/lib.rs @@ -85,7 +85,7 @@ macro_rules! get_line_num { let line = line!(); let column = column!(); let file_path = file!().to_string(); - let mut crate_path = env!("CARGO_MANIFEST_DIR").to_string(); + let crate_path = env!("CARGO_MANIFEST_DIR").to_string(); CodeLocation { crate_path, From 6932924f57e74b75658d04f152424268365b7d9e Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Tue, 28 Jun 2022 21:17:46 +0300 Subject: [PATCH 25/39] Comment on crossterm key event conversion --- packages/tui/src/hooks.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 1443719a0..d91667d97 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -770,7 +770,7 @@ fn translate_key_event(event: crossterm::event::KeyEvent) -> Option { /// The crossterm key_code nicely represents the meaning of the key and we can mostly convert it without any issues /// /// Exceptions: -/// BackTab and Null are converted to Key::Unidentified +/// BackTab is converted to Key::Tab, and Null is converted to Key::Unidentified fn key_from_crossterm_key_code(key_code: TermKeyCode) -> Key { match key_code { TermKeyCode::Backspace => Key::Backspace, @@ -785,7 +785,7 @@ fn key_from_crossterm_key_code(key_code: TermKeyCode) -> Key { TermKeyCode::PageDown => Key::PageDown, TermKeyCode::Tab => Key::Tab, // ? no corresponding Key - TermKeyCode::BackTab => Key::Unidentified, + TermKeyCode::BackTab => Key::Tab, TermKeyCode::Delete => Key::Delete, TermKeyCode::Insert => Key::Insert, TermKeyCode::F(1) => Key::F1, @@ -823,7 +823,7 @@ fn key_from_crossterm_key_code(key_code: TermKeyCode) -> Key { // Crossterm does not provide a way to get the `code` (physical key on keyboard) // So we make a guess based on their `key_code`, but this is probably going to break on anything other than a very standard european keyboard -// It may look fine, but it's a horrible hack. +// It may look fine, but it's a horrible hack. But there's nothing better we can do. fn guess_code_from_crossterm_key_code(key_code: TermKeyCode) -> Option { let code = match key_code { TermKeyCode::Backspace => Code::Backspace, @@ -837,7 +837,7 @@ fn guess_code_from_crossterm_key_code(key_code: TermKeyCode) -> Option { TermKeyCode::PageUp => Code::PageUp, TermKeyCode::PageDown => Code::PageDown, TermKeyCode::Tab => Code::Tab, - // ? no corresponding Code + // ? Apparently you get BackTab by pressing Tab TermKeyCode::BackTab => Code::Tab, TermKeyCode::Delete => Code::Delete, TermKeyCode::Insert => Code::Insert, @@ -897,7 +897,7 @@ fn guess_code_from_crossterm_key_code(key_code: TermKeyCode) -> Option { 'X' => Code::KeyX, 'Y' => Code::KeyY, 'Z' => Code::KeyZ, - _ => unreachable!(), + _ => unreachable!("Exhaustively checked all characters in range A..Z"), }, ' ' => Code::Space, '[' | '{' => Code::BracketLeft, @@ -928,7 +928,8 @@ fn guess_code_from_crossterm_key_code(key_code: TermKeyCode) -> Option { '*' => Code::Digit8, '(' => Code::Digit9, ')' => Code::Digit0, - // numpad charicter are ambiguous to tui + // numpad characters are ambiguous; we don't know which key was really pressed + // it could be also: // '*' => Code::Multiply, // '/' => Code::Divide, // '-' => Code::Subtract, From bfcfe2ca5f0e176b0887987d5d424a759037781f Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Tue, 28 Jun 2022 23:10:44 +0300 Subject: [PATCH 26/39] Remove use of deprecated functions --- packages/tui/src/hooks.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index d91667d97..bb5f81dfb 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -11,7 +11,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::{event_bubbles, on::*, KeyCode}; +use dioxus_html::{event_bubbles, on::*}; use std::{ any::Any, cell::{RefCell, RefMut}, @@ -172,8 +172,10 @@ impl InnerInputState { let old_focus = self.focus_state.last_focused_id; evts.retain(|e| match &e.1 { - EventData::Keyboard(k) => match k.key_code { - KeyCode::Tab => !self.focus_state.progress(dom, !k.shift_key), + EventData::Keyboard(k) => match k.code() { + Code::Tab => !self + .focus_state + .progress(dom, !k.modifiers().contains(Modifiers::SHIFT)), _ => true, }, _ => true, From 6a6a028afca781a613eaf5e29b7bc2fee5ea9c46 Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Tue, 28 Jun 2022 23:36:06 +0300 Subject: [PATCH 27/39] Create new example for all mouse events --- examples/all_events.rs | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 examples/all_events.rs diff --git a/examples/all_events.rs b/examples/all_events.rs new file mode 100644 index 000000000..9a5a585ca --- /dev/null +++ b/examples/all_events.rs @@ -0,0 +1,61 @@ +use dioxus::prelude::*; +use dioxus_core::UiEvent; +use dioxus_html::on::MouseData; +use std::sync::Arc; + +fn main() { + dioxus::desktop::launch(app); +} + +#[derive(Debug)] +enum Event { + MouseMove(Arc), + MouseClick(Arc), + MouseDoubleClick(Arc), + MouseDown(Arc), + MouseUp(Arc), +} + +const MAX_EVENTS: usize = 4; + +fn app(cx: Scope) -> Element { + let container_style = r#" + display: flex; + flex-direction: column; + align-items: center; + "#; + let rect_style = r#" + background: deepskyblue; + height: 50vh; + width: 50vw; + "#; + + let events = use_ref(&cx, || Vec::new()); + + let events_lock = events.read(); + let first_index = events_lock.len().saturating_sub(MAX_EVENTS); + let events_rendered = events_lock[first_index..] + .iter() + .map(|event| cx.render(rsx!(div{"{event:?}"}))); + + let log_event = move |event: Event| { + events.write().push(event); + }; + + cx.render(rsx! ( + div { + style: "{container_style}", + "Hover over to display coordinates:", + div { + style: "{rect_style}", + onmousemove: move |event| log_event(Event::MouseMove(event.data)), + onclick: move |event| log_event(Event::MouseClick(event.data)), + ondblclick: move |event| log_event(Event::MouseDoubleClick(event.data)), + onmousedown: move |event| log_event(Event::MouseDown(event.data)), + onmouseup: move |event| log_event(Event::MouseUp(event.data)), + prevent_default: "mousedown", + } + }, + events_rendered, + )) +} From c090741c60f847077f91babd75e2c23847a93a1b Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Tue, 28 Jun 2022 23:46:24 +0300 Subject: [PATCH 28/39] Improve event formatting --- examples/all_events.rs | 6 +++--- packages/html/src/events.rs | 14 +++++++++++++- packages/html/src/geometry.rs | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/examples/all_events.rs b/examples/all_events.rs index 9a5a585ca..5c6d56e37 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -36,7 +36,7 @@ fn app(cx: Scope) -> Element { let first_index = events_lock.len().saturating_sub(MAX_EVENTS); let events_rendered = events_lock[first_index..] .iter() - .map(|event| cx.render(rsx!(div{"{event:?}"}))); + .map(|event| cx.render(rsx!(div {"{event:?}"}))); let log_event = move |event: Event| { events.write().push(event); @@ -45,7 +45,6 @@ fn app(cx: Scope) -> Element { cx.render(rsx! ( div { style: "{container_style}", - "Hover over to display coordinates:", div { style: "{rect_style}", onmousemove: move |event| log_event(Event::MouseMove(event.data)), @@ -53,9 +52,10 @@ fn app(cx: Scope) -> Element { ondblclick: move |event| log_event(Event::MouseDoubleClick(event.data)), onmousedown: move |event| log_event(Event::MouseDown(event.data)), onmouseup: move |event| log_event(Event::MouseUp(event.data)), + // prevent selection prevent_default: "mousedown", } + div { events_rendered }, }, - events_rendered, )) } diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 3bd84ac0e..dbf6dd2ca 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -17,6 +17,7 @@ pub mod on { use keyboard_types::{Code, Key, Location, Modifiers}; use std::collections::HashMap; use std::convert::TryInto; + use std::fmt::{Debug, Formatter}; use std::str::FromStr; use super::*; @@ -568,7 +569,7 @@ pub mod on { pub type MouseEvent = UiEvent; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug, Clone)] + #[derive(Clone)] /// Data associated with a mouse event /// /// Do not use the deprecated fields; they may change or become private in the future. @@ -753,6 +754,17 @@ pub mod on { } } + 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() + } + } + pub type PointerEvent = UiEvent; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone)] diff --git a/packages/html/src/geometry.rs b/packages/html/src/geometry.rs index 027922a85..990ca5b5e 100644 --- a/packages/html/src/geometry.rs +++ b/packages/html/src/geometry.rs @@ -96,6 +96,7 @@ impl WheelDelta { } /// Coordinates of a point in the app's interface +#[derive(Debug)] pub struct Coordinates { screen: ScreenPoint, client: ClientPoint, From 8f8a614a5cca00c16949c7b2ac81ae367e9a29e7 Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Tue, 28 Jun 2022 23:58:07 +0300 Subject: [PATCH 29/39] Add keyboard events to event example --- examples/all_events.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/all_events.rs b/examples/all_events.rs index 5c6d56e37..513a92f68 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -1,6 +1,6 @@ use dioxus::prelude::*; use dioxus_core::UiEvent; -use dioxus_html::on::MouseData; +use dioxus_html::on::{KeyboardData, MouseData}; use std::sync::Arc; fn main() { @@ -14,6 +14,9 @@ enum Event { MouseDoubleClick(Arc), MouseDown(Arc), MouseUp(Arc), + KeyDown(Arc), + KeyUp(Arc), + KeyPress(Arc), } const MAX_EVENTS: usize = 4; @@ -47,13 +50,18 @@ fn app(cx: Scope) -> Element { style: "{container_style}", div { style: "{rect_style}", + // focusing is necessary to catch keyboard events + tabindex: "0", + onmousemove: move |event| log_event(Event::MouseMove(event.data)), onclick: move |event| log_event(Event::MouseClick(event.data)), ondblclick: move |event| log_event(Event::MouseDoubleClick(event.data)), onmousedown: move |event| log_event(Event::MouseDown(event.data)), onmouseup: move |event| log_event(Event::MouseUp(event.data)), - // prevent selection - prevent_default: "mousedown", + + onkeydown: move |event| log_event(Event::KeyDown(event.data)), + onkeyup: move |event| log_event(Event::KeyUp(event.data)), + onkeypress: move |event| log_event(Event::KeyPress(event.data)), } div { events_rendered }, }, From a46ff936006d4b355c1812dbae9cc182bdbf296e Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Wed, 29 Jun 2022 01:09:17 +0300 Subject: [PATCH 30/39] Improve keyboard event debug formatting --- packages/html/src/events.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index dbf6dd2ca..b4e81726e 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -427,7 +427,7 @@ pub mod on { pub type KeyboardEvent = UiEvent; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug, Clone)] + #[derive(Clone)] pub struct KeyboardData { #[deprecated( since = "0.3.0", @@ -553,6 +553,18 @@ pub mod on { } } + 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() + } + } + pub type FocusEvent = UiEvent; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone)] From f30387597abf4825897c4e5549207c961130657b Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Wed, 29 Jun 2022 01:18:09 +0300 Subject: [PATCH 31/39] Add wheel events to event example; make interface prettier --- examples/all_events.rs | 13 ++++++++++++- packages/html/src/events.rs | 10 +++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/examples/all_events.rs b/examples/all_events.rs index 513a92f68..713473dc5 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -1,6 +1,6 @@ use dioxus::prelude::*; use dioxus_core::UiEvent; -use dioxus_html::on::{KeyboardData, MouseData}; +use dioxus_html::on::{KeyboardData, MouseData, WheelData}; use std::sync::Arc; fn main() { @@ -14,6 +14,9 @@ enum Event { MouseDoubleClick(Arc), MouseDown(Arc), MouseUp(Arc), + + Wheel(Arc), + KeyDown(Arc), KeyUp(Arc), KeyPress(Arc), @@ -31,6 +34,10 @@ fn app(cx: Scope) -> Element { background: deepskyblue; height: 50vh; width: 50vw; + color: white; + padding: 20px; + margin: 20px; + text-aligh: center; "#; let events = use_ref(&cx, || Vec::new()); @@ -59,9 +66,13 @@ fn app(cx: Scope) -> Element { onmousedown: move |event| log_event(Event::MouseDown(event.data)), onmouseup: move |event| log_event(Event::MouseUp(event.data)), + onwheel: move |event| log_event(Event::Wheel(event.data)), + onkeydown: move |event| log_event(Event::KeyDown(event.data)), onkeyup: move |event| log_event(Event::KeyUp(event.data)), onkeypress: move |event| log_event(Event::KeyPress(event.data)), + + "Hover, click, type or scroll to see the info down below" } div { events_rendered }, }, diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index b4e81726e..d2fa8d3ba 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -828,7 +828,7 @@ pub mod on { pub type WheelEvent = UiEvent; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug, Clone)] + #[derive(Clone)] pub struct WheelData { #[deprecated(since = "0.3.0", note = "use delta() instead")] pub delta_mode: u32, @@ -889,6 +889,14 @@ pub mod on { } } + impl Debug for WheelData { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WheelData") + .field("delta", &self.delta()) + .finish() + } + } + pub type MediaEvent = UiEvent; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone)] From 0cdaeb1a8db6c8aa985b4da50a0f51d2f89e3557 Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Wed, 29 Jun 2022 01:24:33 +0300 Subject: [PATCH 32/39] Delete redundant examples (all events now demoed in single simple example) --- examples/all_events.rs | 10 ++++++-- examples/event_mouse.rs | 56 ----------------------------------------- examples/event_wheel.rs | 39 ---------------------------- examples/events.rs | 26 ------------------- 4 files changed, 8 insertions(+), 123 deletions(-) delete mode 100644 examples/event_mouse.rs delete mode 100644 examples/event_wheel.rs delete mode 100644 examples/events.rs diff --git a/examples/all_events.rs b/examples/all_events.rs index 713473dc5..540d87500 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -1,6 +1,6 @@ use dioxus::prelude::*; use dioxus_core::UiEvent; -use dioxus_html::on::{KeyboardData, MouseData, WheelData}; +use dioxus_html::on::{FocusData, KeyboardData, MouseData, WheelData}; use std::sync::Arc; fn main() { @@ -20,9 +20,12 @@ enum Event { KeyDown(Arc), KeyUp(Arc), KeyPress(Arc), + + FocusIn(Arc), + FocusOut(Arc), } -const MAX_EVENTS: usize = 4; +const MAX_EVENTS: usize = 8; fn app(cx: Scope) -> Element { let container_style = r#" @@ -72,6 +75,9 @@ fn app(cx: Scope) -> Element { onkeyup: move |event| log_event(Event::KeyUp(event.data)), onkeypress: move |event| log_event(Event::KeyPress(event.data)), + onfocusin: move |event| log_event(Event::FocusIn(event.data)), + onfocusout: move |event| log_event(Event::FocusOut(event.data)), + "Hover, click, type or scroll to see the info down below" } div { events_rendered }, diff --git a/examples/event_mouse.rs b/examples/event_mouse.rs deleted file mode 100644 index 165d9445c..000000000 --- a/examples/event_mouse.rs +++ /dev/null @@ -1,56 +0,0 @@ -use dioxus::prelude::*; -use dioxus_core::UiEvent; -use dioxus_html::on::MouseData; - -fn main() { - dioxus::desktop::launch(app); -} - -fn app(cx: Scope) -> Element { - let page_coordinates = use_state(&cx, || "".to_string()); - let screen_coordinates = use_state(&cx, || "".to_string()); - let element_coordinates = use_state(&cx, || "".to_string()); - let buttons = use_state(&cx, || "".to_string()); - let modifiers = use_state(&cx, || "".to_string()); - - let container_style = r#" - display: flex; - flex-direction: column; - align-items: center; - "#; - let rect_style = r#" - background: deepskyblue; - height: 50vh; - width: 50vw; - "#; - - let update_mouse_position = move |event: UiEvent| { - let mouse_data = event.data; - - page_coordinates.set(format!("{:?}", mouse_data.page_coordinates())); - screen_coordinates.set(format!("{:?}", mouse_data.screen_coordinates())); - element_coordinates.set(format!("{:?}", mouse_data.element_coordinates())); - - // Note: client coordinates are also available, but they would be the same as the page coordinates in this example, because there is no scrolling. - - buttons.set(format!("{:?}", mouse_data.held_buttons())); - modifiers.set(format!("{:?}", mouse_data.modifiers())); - }; - - cx.render(rsx! ( - div { - style: "{container_style}", - "Hover over to display coordinates:", - div { - style: "{rect_style}", - onmousemove: update_mouse_position, - prevent_default: "mousedown", - } - div {"Page coordinates: {page_coordinates}"}, - div {"Screen coordinates: {screen_coordinates}"}, - div {"Element coordinates: {element_coordinates}"}, - div {"Buttons: {buttons}"}, - div {"Modifiers: {modifiers}"}, - } - )) -} diff --git a/examples/event_wheel.rs b/examples/event_wheel.rs deleted file mode 100644 index 763c64d02..000000000 --- a/examples/event_wheel.rs +++ /dev/null @@ -1,39 +0,0 @@ -use dioxus::prelude::*; -use dioxus_core::UiEvent; -use dioxus_html::on::WheelData; - -fn main() { - dioxus::desktop::launch(app); -} - -fn app(cx: Scope) -> Element { - let delta = use_state(&cx, || "".to_string()); - - let container_style = r#" - display: flex; - flex-direction: column; - align-items: center; - "#; - let rect_style = r#" - background: deepskyblue; - height: 50vh; - width: 50vw; - "#; - - let handle_event = move |event: UiEvent| { - let wheel_data = event.data; - delta.set(format!("{:?}", wheel_data.delta())); - }; - - cx.render(rsx! ( - div { - style: "{container_style}", - "Scroll mouse wheel over rectangle:", - div { - style: "{rect_style}", - onwheel: handle_event, - } - div {"Delta: {delta}"}, - } - )) -} diff --git a/examples/events.rs b/examples/events.rs deleted file mode 100644 index 0a82e9bd7..000000000 --- a/examples/events.rs +++ /dev/null @@ -1,26 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - dioxus::desktop::launch(app); -} - -fn app(cx: Scope) -> Element { - cx.render(rsx! { - div { - button { - ondblclick: move |_| { - // - println!("double clicked!"); - }, - "Click me!" - } - input { - onfocusin: move |_| { - // - println!("blurred!"); - }, - "onblur": "console.log('blurred!')" - } - } - }) -} From 6aa800018b3b73e93794aba60f6a33750578279f Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Wed, 29 Jun 2022 01:52:03 +0300 Subject: [PATCH 33/39] Create similar event demo for TUI --- examples/tui_all_events.rs | 85 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 examples/tui_all_events.rs diff --git a/examples/tui_all_events.rs b/examples/tui_all_events.rs new file mode 100644 index 000000000..732b62532 --- /dev/null +++ b/examples/tui_all_events.rs @@ -0,0 +1,85 @@ +use dioxus::prelude::*; +use dioxus_html::on::{FocusData, KeyboardData, MouseData, WheelData}; +use std::sync::Arc; + +fn main() { + dioxus::tui::launch(app); +} + +#[derive(Debug)] +enum Event { + MouseMove(Arc), + MouseClick(Arc), + MouseDoubleClick(Arc), + MouseDown(Arc), + MouseUp(Arc), + + Wheel(Arc), + + KeyDown(Arc), + KeyUp(Arc), + KeyPress(Arc), + + FocusIn(Arc), + FocusOut(Arc), +} + +const MAX_EVENTS: usize = 8; + +fn app(cx: Scope) -> Element { + let events = use_ref(&cx, || Vec::new()); + + let events_lock = events.read(); + let first_index = events_lock.len().saturating_sub(MAX_EVENTS); + let events_rendered = events_lock[first_index..].iter().map(|event| { + // TUI panics if text overflows (https://github.com/DioxusLabs/dioxus/issues/371) + // temporary hack: just trim the strings (and make sure viewport is big enough) + // todo: remove + let mut trimmed = format!("{event:?}"); + trimmed.truncate(200); + cx.render(rsx!(p { "{trimmed}" })) + }); + + let log_event = move |event: Event| { + events.write().push(event); + }; + + cx.render(rsx! { + div { + width: "100%", + height: "100%", + flex_direction: "column", + div { + width: "80%", + height: "50%", + border_width: "1px", + justify_content: "center", + align_items: "center", + background_color: "hsl(248, 53%, 58%)", + + onmousemove: move |event| log_event(Event::MouseMove(event.data)), + onclick: move |event| log_event(Event::MouseClick(event.data)), + ondblclick: move |event| log_event(Event::MouseDoubleClick(event.data)), + onmousedown: move |event| log_event(Event::MouseDown(event.data)), + onmouseup: move |event| log_event(Event::MouseUp(event.data)), + + onwheel: move |event| log_event(Event::Wheel(event.data)), + + onkeydown: move |event| log_event(Event::KeyDown(event.data)), + onkeyup: move |event| log_event(Event::KeyUp(event.data)), + onkeypress: move |event| log_event(Event::KeyPress(event.data)), + + onfocusin: move |event| log_event(Event::FocusIn(event.data)), + onfocusout: move |event| log_event(Event::FocusOut(event.data)), + + "Hover, click, type or scroll to see the info down below" + }, + div { + width: "80%", + height: "50%", + flex_direction: "column", + events_rendered, + }, + }, + }) +} From cae0c1409d9ea12a280171c4a7fc1da1721f46a4 Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Wed, 29 Jun 2022 02:13:01 +0300 Subject: [PATCH 34/39] Fix some warnings: avoid deprecated fields --- examples/all_events.rs | 1 - examples/tui_buttons.rs | 5 +++-- packages/router/tests/web_router.rs | 1 + packages/tui/tests/events.rs | 5 +++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/all_events.rs b/examples/all_events.rs index 540d87500..5430bdbf5 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -1,5 +1,4 @@ use dioxus::prelude::*; -use dioxus_core::UiEvent; use dioxus_html::on::{FocusData, KeyboardData, MouseData, WheelData}; use std::sync::Arc; diff --git a/examples/tui_buttons.rs b/examples/tui_buttons.rs index 31d2e1ce0..f0c4b3948 100644 --- a/examples/tui_buttons.rs +++ b/examples/tui_buttons.rs @@ -1,4 +1,5 @@ -use dioxus::{events::KeyCode, prelude::*}; +use dioxus::prelude::*; +use dioxus_html::input_data::keyboard_types::Code; fn main() { dioxus::tui::launch(app); @@ -27,7 +28,7 @@ fn Button(cx: Scope) -> Element { background_color: "{color}", tabindex: "{cx.props.layer}", onkeydown: |e| { - if let KeyCode::Space = e.data.key_code{ + if let Code::Space = e.data.code() { toggle.modify(|f| !f); } }, diff --git a/packages/router/tests/web_router.rs b/packages/router/tests/web_router.rs index 40b3a764f..cddc542c1 100644 --- a/packages/router/tests/web_router.rs +++ b/packages/router/tests/web_router.rs @@ -1,4 +1,5 @@ #![cfg(target_arch = "wasm32")] +#![allow(non_snake_case)] use dioxus_core::prelude::*; use dioxus_core_macro::*; diff --git a/packages/tui/tests/events.rs b/packages/tui/tests/events.rs index ab6b70b2e..fe31f4c5e 100644 --- a/packages/tui/tests/events.rs +++ b/packages/tui/tests/events.rs @@ -4,6 +4,7 @@ use dioxus_core::*; use dioxus_core_macro::*; use dioxus_hooks::*; use dioxus_html as dioxus_elements; +use dioxus_html::input_data::keyboard_types::Code; use dioxus_tui::TuiContext; use std::future::Future; use std::pin::Pin; @@ -60,7 +61,7 @@ fn key_down() { width: "100%", height: "100%", onkeydown: move |evt| { - assert_eq!(evt.data.key_code, dioxus_html::KeyCode::A); + assert_eq!(evt.data.code(), Code::KeyA); tui_ctx.quit(); }, } @@ -290,7 +291,7 @@ fn wheel() { width: "100%", height: "100%", onwheel: move |evt| { - assert!(evt.data.delta_y > 0.0); + assert!(evt.data.delta().strip_units().y > 0.0); tui_ctx.quit(); }, } From ca60b01c4929fcaf28479ef36080ebf59ce48d42 Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Wed, 29 Jun 2022 02:17:00 +0300 Subject: [PATCH 35/39] Fix remaining warnings (unrelated) --- packages/router/tests/web_router.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/router/tests/web_router.rs b/packages/router/tests/web_router.rs index cddc542c1..1cfde67ac 100644 --- a/packages/router/tests/web_router.rs +++ b/packages/router/tests/web_router.rs @@ -6,7 +6,6 @@ use dioxus_core_macro::*; use dioxus_html as dioxus_elements; use dioxus_router::*; use gloo_utils::document; -use serde::{Deserialize, Serialize}; use wasm_bindgen_test::*; wasm_bindgen_test_configure!(run_in_browser); @@ -50,7 +49,7 @@ fn simple_test() { } fn BlogPost(cx: Scope) -> Element { - let id = use_route(&cx).parse_segment::("id")?; + let _id = use_route(&cx).parse_segment::("id")?; cx.render(rsx! { div { @@ -61,5 +60,5 @@ fn simple_test() { main(); - let element = gloo_utils::document(); + let _ = document(); } From a9f286c52b2252a03db68323c0655404fd676f93 Mon Sep 17 00:00:00 2001 From: Reinis Mazeiks Date: Wed, 29 Jun 2022 07:44:19 +0300 Subject: [PATCH 36/39] Fix bug in TUI key repeat handling --- packages/tui/src/hooks.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index bb5f81dfb..59aa7a7e3 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -146,14 +146,16 @@ impl InnerInputState { .last_key_pressed .as_ref() // heuristic for guessing which presses are auto-repeating. not necessarily accurate - .filter(|k2| k2.0.key() == k.key() && k2.1.elapsed() < MAX_REPEAT_TIME) + .filter(|(last_data, last_instant)| { + last_data.key() == k.key() && last_instant.elapsed() < MAX_REPEAT_TIME + }) .is_some(); - if is_repeating { - let new = k.clone(); - self.last_key_pressed = Some((new, Instant::now())); + if is_repeating { *k = KeyboardData::new(k.key(), k.code(), k.location(), true, k.modifiers()); } + + self.last_key_pressed = Some((k.clone(), Instant::now())); } } } From 58a495351a1b10405f9145f8da43c02553a7b6e6 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 29 Jun 2022 09:09:47 -0500 Subject: [PATCH 37/39] check that an node is an element before removing listeners --- packages/interpreter/src/interpreter.js | 88 ++++++++++++------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 759e83c9f..4483d202a 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -15,43 +15,45 @@ class ListenerMap { this.root = root; } - createBubbling(event_name, handler) { - if (this.global[event_name] === undefined) { - this.global[event_name] = {}; - this.global[event_name].active = 1; - this.global[event_name].callback = handler; - this.root.addEventListener(event_name, handler); - } else { - this.global[event_name].active++; + create(event_name, element, handler, bubbles) { + if (bubbles) { + if (this.global[event_name] === undefined) { + this.global[event_name] = {}; + this.global[event_name].active = 1; + this.global[event_name].callback = handler; + this.root.addEventListener(event_name, handler); + } else { + this.global[event_name].active++; + } + } + else { + const id = element.getAttribute("data-dioxus-id"); + if (!this.local[id]) { + this.local[id] = {}; + } + this.local[id][event_name] = handler; + element.addEventListener(event_name, handler); } } - createNonBubbling(event_name, element, handler) { - const id = element.getAttribute("data-dioxus-id"); - if (!this.local[id]) { - this.local[id] = {}; + remove(element, event_name, bubbles) { + if (bubbles) { + this.global[event_name].active--; + if (this.global[event_name].active === 0) { + this.root.removeEventListener(event_name, this.global[event_name].callback); + delete this.global[event_name]; + } } - this.local[id][event_name] = handler; - element.addEventListener(event_name, handler); - } - - removeBubbling(event_name) { - this.global[event_name].active--; - if (this.global[event_name].active === 0) { - this.root.removeEventListener(event_name, this.global[event_name].callback); - delete this.global[event_name]; + else { + const id = element.getAttribute("data-dioxus-id"); + delete this.local[id][event_name]; + if (this.local[id].length === 0) { + delete this.local[id]; + } + element.removeEventListener(event_name, handler); } } - removeNonBubbling(element, event_name) { - const id = element.getAttribute("data-dioxus-id"); - delete this.local[id][event_name]; - if (this.local[id].length === 0) { - delete this.local[id]; - } - element.removeEventListener(event_name, handler); - } - removeAllNonBubbling(element) { const id = element.getAttribute("data-dioxus-id"); delete this.local[id]; @@ -93,7 +95,9 @@ export class Interpreter { ReplaceWith(root_id, m) { let root = this.nodes[root_id]; let els = this.stack.splice(this.stack.length - m); - this.listeners.removeAllNonBubbling(root); + if (is_element_node(root.nodeType)) { + this.listeners.removeAllNonBubbling(root); + } root.replaceWith(...els); } InsertAfter(root, n) { @@ -108,8 +112,10 @@ export class Interpreter { } Remove(root) { let node = this.nodes[root]; - this.listeners.removeAllNonBubbling(node); if (node !== undefined) { + if (is_element_node(node)) { + this.listeners.removeAllNonBubbling(node); + } node.remove(); } } @@ -137,22 +143,12 @@ export class Interpreter { NewEventListener(event_name, root, handler, bubbles) { const element = this.nodes[root]; element.setAttribute("data-dioxus-id", `${root}`); - if (bubbles) { - this.listeners.createBubbling(event_name, handler); - } - else { - this.listeners.createNonBubbling(event_name, element, handler); - } + this.listeners.create(event_name, element, handler, bubbles); } RemoveEventListener(root, event_name, bubbles) { const element = this.nodes[root]; element.removeAttribute(`data-dioxus-id`); - if (bubbles) { - this.listeners.removeBubbling(event_name) - } - else { - this.listeners.removeNonBubbling(element, event_name); - } + this.listeners.remove(element, event_name, bubbles); } SetText(root, text) { this.nodes[root].textContent = text; @@ -660,6 +656,10 @@ const bool_attrs = { truespeed: true, }; +function is_element_node(node) { + return node.nodeType == 1; +} + function event_bubbles(event) { switch (event) { case "copy": From bf548d4a0ea7a28d9ef77e1f5f3ceb6a92dd9240 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 29 Jun 2022 09:48:16 -0500 Subject: [PATCH 38/39] set id before creating listener --- packages/core/src/diff.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 71c9f1289..37dbc38bf 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -402,12 +402,12 @@ impl<'b> DiffState<'b> { if old.listeners.len() == new.listeners.len() { for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) { + new_l.mounted_node.set(old_l.mounted_node.get()); if old_l.event != new_l.event { self.mutations .remove_event_listener(old_l.event, root.as_u64()); self.mutations.new_event_listener(new_l, cur_scope_id); } - new_l.mounted_node.set(old_l.mounted_node.get()); } } else { for listener in old.listeners { From 74fc28e5d144285fe4043f209cbc62013104ead4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 29 Jun 2022 10:09:37 -0500 Subject: [PATCH 39/39] add tests for diffing listeners --- tests/diffing.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/diffing.rs b/tests/diffing.rs index a876ffc03..3cc010673 100644 --- a/tests/diffing.rs +++ b/tests/diffing.rs @@ -756,3 +756,79 @@ fn add_nested_elements() { ] ); } + +#[test] +fn add_listeners() { + let vdom = new_dom(); + + let (_create, change) = vdom.diff_lazynodes( + rsx! { + div{} + }, + rsx! { + div{ + onkeyup: |_| {}, + onkeydown: |_| {}, + } + }, + ); + + assert_eq!( + change.edits, + [ + NewEventListener { event_name: "keyup", scope: ScopeId(0), root: 1 }, + NewEventListener { event_name: "keydown", scope: ScopeId(0), root: 1 }, + ] + ); +} + +#[test] +fn remove_listeners() { + let vdom = new_dom(); + + let (_create, change) = vdom.diff_lazynodes( + rsx! { + div{ + onkeyup: |_| {}, + onkeydown: |_| {}, + } + }, + rsx! { + div{} + }, + ); + + assert_eq!( + change.edits, + [ + RemoveEventListener { event: "keyup", root: 1 }, + RemoveEventListener { event: "keydown", root: 1 }, + ] + ); +} + +#[test] +fn diff_listeners() { + let vdom = new_dom(); + + let (_create, change) = vdom.diff_lazynodes( + rsx! { + div{ + onkeydown: |_| {}, + } + }, + rsx! { + div{ + onkeyup: |_| {}, + } + }, + ); + + assert_eq!( + change.edits, + [ + RemoveEventListener { root: 1, event: "keydown" }, + NewEventListener { event_name: "keyup", scope: ScopeId(0), root: 1 } + ] + ); +}