From a4eb4dc8dfd33c6056fbcf5af3d19601a2060d1e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 4 May 2022 13:58:48 -0500 Subject: [PATCH 01/10] add offset mouse data to tui --- packages/tui/src/hooks.rs | 98 ++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 57 deletions(-) diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 04d320fcb..658229fae 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -188,16 +188,6 @@ impl InnerInputState { layout: &Stretch, dom: &mut Dom, ) { - struct Data<'b> { - new_pos: (i32, i32), - old_pos: Option<(i32, i32)>, - clicked: bool, - released: bool, - wheel_delta: f64, - mouse_data: &'b MouseData, - wheel_data: &'b Option, - } - fn layout_contains_point(layout: &Layout, point: (i32, i32)) -> bool { layout.location.x as i32 <= point.0 && layout.location.x as i32 + layout.size.width as i32 >= point.0 @@ -230,6 +220,13 @@ impl InnerInputState { } } + fn prepare_mouse_data(mouse_data: &MouseData, layout: &Layout) -> MouseData { + let mut data = mouse_data.clone(); + data.offset_x = data.client_x - layout.location.x as i32; + data.offset_y = data.client_y - layout.location.y as i32; + data + } + if let Some(mouse) = &self.mouse { let new_pos = (mouse.0.screen_x, mouse.0.screen_y); let old_pos = previous_mouse @@ -242,31 +239,21 @@ impl InnerInputState { let wheel_delta = self.wheel.as_ref().map_or(0.0, |w| w.delta_y); let mouse_data = &mouse.0; let wheel_data = &self.wheel; - let data = Data { - new_pos, - old_pos, - clicked, - released, - wheel_delta, - mouse_data, - wheel_data, - }; { // mousemove let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mousemove") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let previously_contained = data - .old_pos + let previously_contained = old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) .is_some(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); if currently_contains && previously_contained { try_create_event( "mousemove", - Arc::new(data.mouse_data.clone()), + Arc::new(prepare_mouse_data(mouse_data, node_layout)), &mut will_bubble, resolved_events, node, @@ -281,16 +268,15 @@ impl InnerInputState { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseenter") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let previously_contained = data - .old_pos + let previously_contained = old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) .is_some(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); if currently_contains && !previously_contained { try_create_event( "mouseenter", - Arc::new(data.mouse_data.clone()), + Arc::new(mouse_data.clone()), &mut will_bubble, resolved_events, node, @@ -305,16 +291,15 @@ impl InnerInputState { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseover") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let previously_contained = data - .old_pos + let previously_contained = old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) .is_some(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); if currently_contains && !previously_contained { try_create_event( "mouseover", - Arc::new(data.mouse_data.clone()), + Arc::new(prepare_mouse_data(mouse_data, node_layout)), &mut will_bubble, resolved_events, node, @@ -329,12 +314,12 @@ impl InnerInputState { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mousedown") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); - if currently_contains && data.clicked { + if currently_contains && clicked { try_create_event( "mousedown", - Arc::new(data.mouse_data.clone()), + Arc::new(prepare_mouse_data(mouse_data, node_layout)), &mut will_bubble, resolved_events, node, @@ -349,12 +334,12 @@ impl InnerInputState { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseup") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); - if currently_contains && data.released { + if currently_contains && released { try_create_event( "mouseup", - Arc::new(data.mouse_data.clone()), + Arc::new(prepare_mouse_data(mouse_data, node_layout)), &mut will_bubble, resolved_events, node, @@ -369,12 +354,12 @@ impl InnerInputState { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("click") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); - if currently_contains && data.released && data.mouse_data.button == 0 { + if currently_contains && released && mouse_data.button == 0 { try_create_event( "click", - Arc::new(data.mouse_data.clone()), + Arc::new(prepare_mouse_data(mouse_data, node_layout)), &mut will_bubble, resolved_events, node, @@ -389,12 +374,12 @@ impl InnerInputState { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("contextmenu") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); - if currently_contains && data.released && data.mouse_data.button == 2 { + if currently_contains && released && mouse_data.button == 2 { try_create_event( "contextmenu", - Arc::new(data.mouse_data.clone()), + Arc::new(prepare_mouse_data(mouse_data, node_layout)), &mut will_bubble, resolved_events, node, @@ -409,10 +394,10 @@ impl InnerInputState { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("wheel") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); - if let Some(w) = data.wheel_data { - if currently_contains && data.wheel_delta != 0.0 { + if let Some(w) = wheel_data { + if currently_contains && wheel_delta != 0.0 { try_create_event( "wheel", Arc::new(w.clone()), @@ -431,16 +416,15 @@ impl InnerInputState { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseleave") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let previously_contained = data - .old_pos + let previously_contained = old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) .is_some(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); if !currently_contains && previously_contained { try_create_event( "mouseleave", - Arc::new(data.mouse_data.clone()), + Arc::new(prepare_mouse_data(mouse_data, node_layout)), &mut will_bubble, resolved_events, node, @@ -455,16 +439,15 @@ impl InnerInputState { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseout") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let previously_contained = data - .old_pos + let previously_contained = old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) .is_some(); - let currently_contains = layout_contains_point(node_layout, data.new_pos); + let currently_contains = layout_contains_point(node_layout, new_pos); if !currently_contains && previously_contained { try_create_event( "mouseout", - Arc::new(data.mouse_data.clone()), + Arc::new(prepare_mouse_data(mouse_data, node_layout)), &mut will_bubble, resolved_events, node, @@ -603,7 +586,7 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> { }; // from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent - // The `offset`, `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively. + // The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively. // todo? // But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway. EventData::Mouse(MouseData { @@ -614,8 +597,9 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> { client_y: y, ctrl_key: ctrl, meta_key: meta, - offset_x: x, - offset_y: y, + // offset x/y are set when the origin of the event is assigned to an element + offset_x: 0, + offset_y: 0, page_x: x, page_y: y, screen_x: x, From 8ab1c4d0dec39ee681512a117088e17ee4bce650 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 May 2022 16:29:24 -0400 Subject: [PATCH 02/10] feat: upgrade wry --- Cargo.toml | 1 - packages/desktop/Cargo.toml | 8 ++------ packages/desktop/src/desktop_context.rs | 2 +- packages/desktop/src/lib.rs | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0a0b4034..86a952e7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,6 @@ html = ["dioxus-html"] ssr = ["dioxus-ssr"] web = ["dioxus-web", "dioxus-router/web"] desktop = ["dioxus-desktop"] -ayatana = ["dioxus-desktop/ayatana"] router = ["dioxus-router"] tui = ["dioxus-tui"] liveview = ["dioxus-liveview"] diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 912a29abf..e61120d64 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -20,7 +20,7 @@ serde = "1.0.136" serde_json = "1.0.79" thiserror = "1.0.30" log = "0.4.14" -wry = { version = "0.13.1" } +wry = { version = "0.16.0" } futures-channel = "0.3.21" tokio = { version = "1.16.1", features = [ "sync", @@ -28,7 +28,7 @@ tokio = { version = "1.16.1", features = [ "rt", "time", ], optional = true, default-features = false } -webbrowser = "0.6.0" +webbrowser = "0.7.1" mime_guess = "2.0.3" dunce = "1.0.2" @@ -38,13 +38,9 @@ core-foundation = "0.9.3" [features] default = ["tokio_runtime"] tokio_runtime = ["tokio"] - -devtool = ["wry/devtool"] fullscreen = ["wry/fullscreen"] transparent = ["wry/transparent"] - tray = ["wry/tray"] -ayatana = ["wry/ayatana"] [dev-dependencies] diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 155f7af1b..0b37910b0 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -191,7 +191,7 @@ pub(super) fn handler( SetTitle(content) => window.set_title(&content), SetDecorations(state) => window.set_decorations(state), - DevTool => webview.devtool(), + DevTool => {} Eval(code) => webview .evaluate_script(code.as_str()) diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 1e3898f05..160ed7d43 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -200,7 +200,7 @@ pub fn launch_with_props( ) } else { // in debug, we are okay with the reload menu showing and dev tool - webview = webview.with_dev_tool(true); + webview = webview.with_devtools(true); } desktop.webviews.insert(window_id, webview.build().unwrap()); From 356f37e9ee5514fc441bbc1fade99c1edec09e52 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 May 2022 16:50:33 -0400 Subject: [PATCH 03/10] feat: arbitrary attributes --- packages/core/src/arbitrary_value.rs | 265 +++++++++++++++++++++++++++ packages/core/src/lib.rs | 2 + packages/core/src/mutations.rs | 7 +- packages/core/src/nodes.rs | 23 ++- packages/ssr/src/lib.rs | 6 +- packages/web/Cargo.toml | 2 +- packages/web/src/dom.rs | 2 +- 7 files changed, 298 insertions(+), 9 deletions(-) create mode 100644 packages/core/src/arbitrary_value.rs diff --git a/packages/core/src/arbitrary_value.rs b/packages/core/src/arbitrary_value.rs new file mode 100644 index 000000000..80acc0ca6 --- /dev/null +++ b/packages/core/src/arbitrary_value.rs @@ -0,0 +1,265 @@ +use std::fmt::Formatter; + +// trying to keep values at 3 bytes +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum AttributeValue<'a> { + Text(&'a str), + Float32(f32), + Float64(f64), + Int32(i32), + Int64(i64), + Uint32(u32), + Uint64(u64), + Bool(bool), + ColorRGB(u8, u8, u8), + ColorRGBA(u8, u8, u8, u8), + ColorHex(u32), + ColorHexAlpha(u32, u8), + Vec3Float(f32, f32, f32), + Vec3Int(i32, i32, i32), + Vec3Uint(u32, u32, u32), + Vec4Float(f32, f32, f32, f32), + Vec4Int(i32, i32, i32, i32), + Vec4Uint(u32, u32, u32, u32), + Bytes(&'a [u8]), + Any(ArbitraryAttributeValue<'a>), +} + +impl<'a> AttributeValue<'a> { + pub fn is_truthy(&self) -> bool { + match self { + AttributeValue::Text(t) => *t == "true", + AttributeValue::Bool(t) => *t, + _ => false, + } + } + + pub fn is_falsy(&self) -> bool { + match self { + AttributeValue::Text(t) => *t == "false", + AttributeValue::Bool(t) => !(*t), + _ => false, + } + } +} + +impl<'a> std::fmt::Display for AttributeValue<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + AttributeValue::Text(a) => write!(f, "{}", a), + AttributeValue::Float32(a) => write!(f, "{}", a), + AttributeValue::Float64(a) => write!(f, "{}", a), + AttributeValue::Int32(a) => write!(f, "{}", a), + AttributeValue::Int64(a) => write!(f, "{}", a), + AttributeValue::Uint32(a) => write!(f, "{}", a), + AttributeValue::Uint64(a) => write!(f, "{}", a), + AttributeValue::Bool(a) => write!(f, "{}", a), + AttributeValue::ColorRGB(_, _, _) => todo!(), + AttributeValue::ColorRGBA(_, _, _, _) => todo!(), + AttributeValue::ColorHex(_) => todo!(), + AttributeValue::ColorHexAlpha(_, _) => todo!(), + AttributeValue::Vec3Float(_, _, _) => todo!(), + AttributeValue::Vec3Int(_, _, _) => todo!(), + AttributeValue::Vec3Uint(_, _, _) => todo!(), + AttributeValue::Vec4Float(_, _, _, _) => todo!(), + AttributeValue::Vec4Int(_, _, _, _) => todo!(), + AttributeValue::Vec4Uint(_, _, _, _) => todo!(), + AttributeValue::Bytes(_) => todo!(), + AttributeValue::Any(_) => todo!(), + } + } +} + +#[derive(Clone, Copy)] +pub struct ArbitraryAttributeValue<'a> { + pub value: &'a dyn std::any::Any, + pub cmp: fn(&'a dyn std::any::Any, &'a dyn std::any::Any) -> bool, +} + +impl PartialEq for ArbitraryAttributeValue<'_> { + fn eq(&self, other: &Self) -> bool { + (self.cmp)(self.value, other.value) + } +} + +impl std::fmt::Debug for ArbitraryAttributeValue<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ArbitraryAttributeValue").finish() + } +} + +#[cfg(feature = "serialize")] +impl<'a> serde::Serialize for ArbitraryAttributeValue<'a> { + fn serialize(&self, _serializer: S) -> Result + where + S: serde::Serializer, + { + panic!("ArbitraryAttributeValue should not be serialized") + } +} +#[cfg(feature = "serialize")] +impl<'de, 'a> serde::Deserialize<'de> for &'a ArbitraryAttributeValue<'a> { + fn deserialize(_deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + panic!("ArbitraryAttributeValue is not deserializable!") + } +} +#[cfg(feature = "serialize")] +impl<'de, 'a> serde::Deserialize<'de> for ArbitraryAttributeValue<'a> { + fn deserialize(_deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + panic!("ArbitraryAttributeValue is not deserializable!") + } +} + +impl<'a> AttributeValue<'a> { + pub fn as_text(&self) -> Option<&'a str> { + match self { + AttributeValue::Text(s) => Some(s), + _ => None, + } + } + + pub fn as_float32(&self) -> Option { + match self { + AttributeValue::Float32(f) => Some(*f), + _ => None, + } + } + + pub fn as_float64(&self) -> Option { + match self { + AttributeValue::Float64(f) => Some(*f), + _ => None, + } + } + + pub fn as_int32(&self) -> Option { + match self { + AttributeValue::Int32(i) => Some(*i), + _ => None, + } + } + + pub fn as_int64(&self) -> Option { + match self { + AttributeValue::Int64(i) => Some(*i), + _ => None, + } + } + + pub fn as_uint32(&self) -> Option { + match self { + AttributeValue::Uint32(i) => Some(*i), + _ => None, + } + } + + pub fn as_uint64(&self) -> Option { + match self { + AttributeValue::Uint64(i) => Some(*i), + _ => None, + } + } + + pub fn as_bool(&self) -> Option { + match self { + AttributeValue::Bool(b) => Some(*b), + _ => None, + } + } + + pub fn as_color_rgb(&self) -> Option<(u8, u8, u8)> { + match self { + AttributeValue::ColorRGB(r, g, b) => Some((*r, *g, *b)), + _ => None, + } + } + + pub fn as_color_rgba(&self) -> Option<(u8, u8, u8, u8)> { + match self { + AttributeValue::ColorRGBA(r, g, b, a) => Some((*r, *g, *b, *a)), + _ => None, + } + } + + pub fn as_color_hex(&self) -> Option { + match self { + AttributeValue::ColorHex(c) => Some(*c), + _ => None, + } + } + + pub fn as_color_hex_alpha(&self) -> Option<(u32, u8)> { + match self { + AttributeValue::ColorHexAlpha(c, a) => Some((*c, *a)), + _ => None, + } + } + + pub fn as_vec3_float(&self) -> Option<(f32, f32, f32)> { + match self { + AttributeValue::Vec3Float(x, y, z) => Some((*x, *y, *z)), + _ => None, + } + } + + pub fn as_vec3_int(&self) -> Option<(i32, i32, i32)> { + match self { + AttributeValue::Vec3Int(x, y, z) => Some((*x, *y, *z)), + _ => None, + } + } + + pub fn as_vec3_uint(&self) -> Option<(u32, u32, u32)> { + match self { + AttributeValue::Vec3Uint(x, y, z) => Some((*x, *y, *z)), + _ => None, + } + } + + pub fn as_vec4_float(&self) -> Option<(f32, f32, f32, f32)> { + match self { + AttributeValue::Vec4Float(x, y, z, w) => Some((*x, *y, *z, *w)), + _ => None, + } + } + + pub fn as_vec4_int(&self) -> Option<(i32, i32, i32, i32)> { + match self { + AttributeValue::Vec4Int(x, y, z, w) => Some((*x, *y, *z, *w)), + _ => None, + } + } + + pub fn as_vec4_uint(&self) -> Option<(u32, u32, u32, u32)> { + match self { + AttributeValue::Vec4Uint(x, y, z, w) => Some((*x, *y, *z, *w)), + _ => None, + } + } + + pub fn as_bytes(&self) -> Option<&[u8]> { + match self { + AttributeValue::Bytes(b) => Some(b), + _ => None, + } + } + + pub fn as_any(&self) -> Option<&'a ArbitraryAttributeValue> { + match self { + AttributeValue::Any(a) => Some(a), + _ => None, + } + } +} + +#[test] +fn test_attribute_value_size() { + assert_eq!(std::mem::size_of::>(), 24); +} diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index f31e2e328..708df1020 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -2,6 +2,7 @@ #![doc = include_str!("../README.md")] #![deny(missing_docs)] +pub(crate) mod arbitrary_value; pub(crate) mod diff; pub(crate) mod events; pub(crate) mod lazynodes; @@ -13,6 +14,7 @@ pub(crate) mod util; pub(crate) mod virtual_dom; pub(crate) mod innerlude { + pub use crate::arbitrary_value::*; pub use crate::events::*; pub use crate::lazynodes::*; pub use crate::mutations::*; diff --git a/packages/core/src/mutations.rs b/packages/core/src/mutations.rs index 2d3115ce3..4218e613f 100644 --- a/packages/core/src/mutations.rs +++ b/packages/core/src/mutations.rs @@ -167,8 +167,9 @@ pub enum DomEdit<'bump> { field: &'static str, /// The value of the attribute. - value: &'bump str, + value: AttributeValue<'bump>, + // value: &'bump str, /// The (optional) namespace of the attribute. /// For instance, "style" is in the "style" namespace. ns: Option<&'bump str>, @@ -286,7 +287,7 @@ impl<'a> Mutations<'a> { self.edits.push(SetText { text, root }); } - pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute, root: u64) { + pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: u64) { let Attribute { name, value, @@ -296,7 +297,7 @@ impl<'a> Mutations<'a> { self.edits.push(SetAttribute { field: name, - value, + value: value.clone(), ns: *namespace, root, }); diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 0d3359a8c..086c6b20d 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -4,7 +4,7 @@ //! cheap and *very* fast to construct - building a full tree should be quick. use crate::{ - innerlude::{ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState}, + innerlude::{ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState, AttributeValue}, lazynodes::LazyNodes, AnyEvent, Component, }; @@ -339,7 +339,7 @@ pub struct Attribute<'a> { pub name: &'static str, /// The value of the attribute. - pub value: &'a str, + pub value: AttributeValue<'a>, /// An indication if this attribute can be ignored during diffing /// @@ -357,6 +357,7 @@ pub struct Attribute<'a> { pub namespace: Option<&'static str>, } + /// An event listener. /// IE onclick, onkeydown, etc pub struct Listener<'bump> { @@ -610,6 +611,24 @@ impl<'a> NodeFactory<'a> { is_volatile: bool, ) -> Attribute<'a> { let (value, is_static) = self.raw_text(val); + Attribute { + name, + value: AttributeValue::Text(value), + is_static, + namespace, + is_volatile, + } + } + + /// Create a new [`Attribute`] using non-arguments + pub fn custom_attr( + &self, + name: &'static str, + value: AttributeValue<'a>, + namespace: Option<&'static str>, + is_volatile: bool, + is_static: bool, + ) -> Attribute<'a> { Attribute { name, value, diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index b473e7fa8..9eb204f78 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -186,7 +186,9 @@ impl<'a> TextRenderer<'a, '_> { while let Some(attr) = attr_iter.next() { match attr.namespace { None => match attr.name { - "dangerous_inner_html" => inner_html = Some(attr.value), + "dangerous_inner_html" => { + inner_html = Some(attr.value.as_text().unwrap()) + } "allowfullscreen" | "allowpaymentrequest" | "async" @@ -213,7 +215,7 @@ impl<'a> TextRenderer<'a, '_> { | "reversed" | "selected" | "truespeed" => { - if attr.value != "false" { + if attr.value.is_truthy() { write!(f, " {}=\"{}\"", attr.name, attr.value)? } } diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index d9b66b7a5..387349ec5 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] dioxus-core = { path = "../core", version = "^0.2.1" } dioxus-html = { path = "../html", version = "^0.2.1", features = ["wasm-bind"] } dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features = [ - "web", + "web" ] } js-sys = "0.3.56" diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 84101423b..94d66460f 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -146,7 +146,7 @@ impl WebsysDom { value, ns, } => { - let value = serde_wasm_bindgen::to_value(value).unwrap(); + let value = serde_wasm_bindgen::to_value(&value).unwrap(); self.interpreter.SetAttribute(root, field, value, ns) } } From bded3142517975758e6a9a2ac9c8e1a42d8d8179 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 29 Apr 2022 23:19:35 -0400 Subject: [PATCH 04/10] wip: remove color --- packages/core/src/arbitrary_value.rs | 47 +++++----------------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/packages/core/src/arbitrary_value.rs b/packages/core/src/arbitrary_value.rs index 80acc0ca6..792bf405c 100644 --- a/packages/core/src/arbitrary_value.rs +++ b/packages/core/src/arbitrary_value.rs @@ -12,16 +12,15 @@ pub enum AttributeValue<'a> { Uint32(u32), Uint64(u64), Bool(bool), - ColorRGB(u8, u8, u8), - ColorRGBA(u8, u8, u8, u8), - ColorHex(u32), - ColorHexAlpha(u32, u8), + Vec3Float(f32, f32, f32), Vec3Int(i32, i32, i32), Vec3Uint(u32, u32, u32), + Vec4Float(f32, f32, f32, f32), Vec4Int(i32, i32, i32, i32), Vec4Uint(u32, u32, u32, u32), + Bytes(&'a [u8]), Any(ArbitraryAttributeValue<'a>), } @@ -55,10 +54,6 @@ impl<'a> std::fmt::Display for AttributeValue<'a> { AttributeValue::Uint32(a) => write!(f, "{}", a), AttributeValue::Uint64(a) => write!(f, "{}", a), AttributeValue::Bool(a) => write!(f, "{}", a), - AttributeValue::ColorRGB(_, _, _) => todo!(), - AttributeValue::ColorRGBA(_, _, _, _) => todo!(), - AttributeValue::ColorHex(_) => todo!(), - AttributeValue::ColorHexAlpha(_, _) => todo!(), AttributeValue::Vec3Float(_, _, _) => todo!(), AttributeValue::Vec3Int(_, _, _) => todo!(), AttributeValue::Vec3Uint(_, _, _) => todo!(), @@ -174,34 +169,6 @@ impl<'a> AttributeValue<'a> { } } - pub fn as_color_rgb(&self) -> Option<(u8, u8, u8)> { - match self { - AttributeValue::ColorRGB(r, g, b) => Some((*r, *g, *b)), - _ => None, - } - } - - pub fn as_color_rgba(&self) -> Option<(u8, u8, u8, u8)> { - match self { - AttributeValue::ColorRGBA(r, g, b, a) => Some((*r, *g, *b, *a)), - _ => None, - } - } - - pub fn as_color_hex(&self) -> Option { - match self { - AttributeValue::ColorHex(c) => Some(*c), - _ => None, - } - } - - pub fn as_color_hex_alpha(&self) -> Option<(u32, u8)> { - match self { - AttributeValue::ColorHexAlpha(c, a) => Some((*c, *a)), - _ => None, - } - } - pub fn as_vec3_float(&self) -> Option<(f32, f32, f32)> { match self { AttributeValue::Vec3Float(x, y, z) => Some((*x, *y, *z)), @@ -259,7 +226,7 @@ impl<'a> AttributeValue<'a> { } } -#[test] -fn test_attribute_value_size() { - assert_eq!(std::mem::size_of::>(), 24); -} +// #[test] +// fn test_attribute_value_size() { +// assert_eq!(std::mem::size_of::>(), 24); +// } From 21df7d214222899a8145d62476a9dc1c24984d8e Mon Sep 17 00:00:00 2001 From: Jonathan Moore Date: Thu, 5 May 2022 17:49:08 -0500 Subject: [PATCH 05/10] Fix typo --- docs/guide/src/elements/lists.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/elements/lists.md b/docs/guide/src/elements/lists.md index 8cd58c841..9f8a8072c 100644 --- a/docs/guide/src/elements/lists.md +++ b/docs/guide/src/elements/lists.md @@ -102,7 +102,7 @@ For keen Rustaceans: notice how we don't actually call `collect` on the name lis ## Keeping list items in order with `key` -The examples above demonstrate the power of iterators in `rsx!` but all share the same issue: if your array items move (e.g. due to sorting), get inserted, or get deleted, Dioxus has no way of knowing what happened. This can cause Elements to be unnecessarily removed, changed and rebuilt when all that was needed was to change their position – this is inneficient. +The examples above demonstrate the power of iterators in `rsx!` but all share the same issue: if your array items move (e.g. due to sorting), get inserted, or get deleted, Dioxus has no way of knowing what happened. This can cause Elements to be unnecessarily removed, changed and rebuilt when all that was needed was to change their position – this is inefficient. To solve this problem, each item in the list must be **uniquely identifiable**. You can achieve this by giving it a unique, fixed "key". In Dioxus, a key is a string that identifies an item among others in the list. From e3757f97ed9321590c981b17096397852b47bc4b Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 6 May 2022 17:01:50 -0500 Subject: [PATCH 06/10] only trigger onmousemove if the position has changed --- packages/tui/src/hooks.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 658229fae..38ab6659b 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -242,23 +242,25 @@ impl InnerInputState { { // mousemove - let mut will_bubble = FxHashSet::default(); - for node in dom.get_listening_sorted("mousemove") { - let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); - let previously_contained = old_pos - .filter(|pos| layout_contains_point(node_layout, *pos)) - .is_some(); - let currently_contains = layout_contains_point(node_layout, new_pos); + if old_pos != Some(new_pos) { + let mut will_bubble = FxHashSet::default(); + for node in dom.get_listening_sorted("mousemove") { + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); + let previously_contained = old_pos + .filter(|pos| layout_contains_point(node_layout, *pos)) + .is_some(); + let currently_contains = layout_contains_point(node_layout, new_pos); - if currently_contains && previously_contained { - try_create_event( - "mousemove", - Arc::new(prepare_mouse_data(mouse_data, node_layout)), - &mut will_bubble, - resolved_events, - node, - dom, - ); + if currently_contains && previously_contained { + try_create_event( + "mousemove", + Arc::new(prepare_mouse_data(mouse_data, node_layout)), + &mut will_bubble, + resolved_events, + node, + dom, + ); + } } } } From c72cc9ae419cbf0ac07bf21f4d7acb65b598d084 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Fri, 6 May 2022 23:36:16 +0100 Subject: [PATCH 07/10] fixed FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 332e03eb2..ae9a2478a 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ # These are supported funding model platforms -github: jkelleyrtp # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: DioxusLabs # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] open_collective: dioxus-labs # Replace with a single Open Collective username From ac95ab52226b73fb716896258c6a7cfb399daa5e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 6 May 2022 19:35:55 -0500 Subject: [PATCH 08/10] switch released pressed --- packages/tui/src/hooks.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 38ab6659b..fd3f7fe01 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -232,10 +232,12 @@ impl InnerInputState { let old_pos = previous_mouse .as_ref() .map(|m| (m.0.screen_x, m.0.screen_y)); - let clicked = - (!mouse.0.buttons & previous_mouse.as_ref().map(|m| m.0.buttons).unwrap_or(0)) > 0; + // the a mouse button is pressed if a button was not down and is now down + let pressed = + (mouse.0.buttons & !previous_mouse.as_ref().map(|m| m.0.buttons).unwrap_or(0)) > 0; + // the a mouse button is pressed if a button was down and is now not down let released = - (mouse.0.buttons & !previous_mouse.map(|m| m.0.buttons).unwrap_or(0)) > 0; + (!mouse.0.buttons & previous_mouse.map(|m| m.0.buttons).unwrap_or(0)) > 0; let wheel_delta = self.wheel.as_ref().map_or(0.0, |w| w.delta_y); let mouse_data = &mouse.0; let wheel_data = &self.wheel; @@ -311,8 +313,8 @@ impl InnerInputState { } } - { - // mousedown + // mousedown + if pressed { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mousedown") { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); From 03110b85cd685977356d220482c023009e55939e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 May 2022 20:46:33 -0400 Subject: [PATCH 09/10] fix: cargo fmt --- packages/core/src/nodes.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 086c6b20d..66c25f1db 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -4,7 +4,7 @@ //! cheap and *very* fast to construct - building a full tree should be quick. use crate::{ - innerlude::{ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState, AttributeValue}, + innerlude::{AttributeValue, ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState}, lazynodes::LazyNodes, AnyEvent, Component, }; @@ -357,7 +357,6 @@ pub struct Attribute<'a> { pub namespace: Option<&'static str>, } - /// An event listener. /// IE onclick, onkeydown, etc pub struct Listener<'bump> { From 9c9928d226687466497422d5b970e69e48f8672e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 May 2022 20:48:57 -0400 Subject: [PATCH 10/10] fix: tui values --- packages/tui/src/hooks.rs | 2 +- packages/tui/src/layout.rs | 8 +++++--- packages/tui/src/style_attributes.rs | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index fd3f7fe01..c9d1e2a8c 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -320,7 +320,7 @@ impl InnerInputState { let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let currently_contains = layout_contains_point(node_layout, new_pos); - if currently_contains && clicked { + if currently_contains { try_create_event( "mousedown", Arc::new(prepare_mouse_data(mouse_data, node_layout)), diff --git a/packages/tui/src/layout.rs b/packages/tui/src/layout.rs index f56f48955..c13435896 100644 --- a/packages/tui/src/layout.rs +++ b/packages/tui/src/layout.rs @@ -79,9 +79,11 @@ impl ChildDepState for StretchLayout { } } else { // gather up all the styles from the attribute list - for &Attribute { name, value, .. } in node.attributes() { - assert!(SORTED_LAYOUT_ATTRS.binary_search(&name).is_ok()); - apply_layout_attributes(name, value, &mut style); + for Attribute { name, value, .. } in node.attributes() { + assert!(SORTED_LAYOUT_ATTRS.binary_search(name).is_ok()); + if let Some(text) = value.as_text() { + apply_layout_attributes(name, text, &mut style); + } } // the root node fills the entire area diff --git a/packages/tui/src/style_attributes.rs b/packages/tui/src/style_attributes.rs index cb140cfcf..fd8d51cb3 100644 --- a/packages/tui/src/style_attributes.rs +++ b/packages/tui/src/style_attributes.rs @@ -78,8 +78,10 @@ impl ParentDepState for StyleModifier { } // gather up all the styles from the attribute list - for &Attribute { name, value, .. } in node.attributes() { - apply_style_attributes(name, value, &mut new); + for Attribute { name, value, .. } in node.attributes() { + if let Some(text) = value.as_text() { + apply_style_attributes(name, text, &mut new); + } } // keep the text styling from the parent element