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 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/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. diff --git a/packages/core/src/arbitrary_value.rs b/packages/core/src/arbitrary_value.rs new file mode 100644 index 000000000..792bf405c --- /dev/null +++ b/packages/core/src/arbitrary_value.rs @@ -0,0 +1,232 @@ +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), + + 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::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_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..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}, + innerlude::{AttributeValue, ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState}, 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 /// @@ -610,6 +610,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/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()); 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/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 722c2598c..077787666 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -192,16 +192,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 @@ -234,48 +224,49 @@ 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 .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; - 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 - .filter(|pos| layout_contains_point(node_layout, *pos)) - .is_some(); - let currently_contains = layout_contains_point(node_layout, data.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(data.mouse_data.clone()), - &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, + ); + } } } } @@ -285,16 +276,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, @@ -309,16 +299,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, @@ -328,17 +317,17 @@ 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(); - 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 { 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, @@ -353,12 +342,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, @@ -373,12 +362,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, @@ -393,12 +382,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, @@ -413,10 +402,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()), @@ -435,16 +424,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, @@ -459,16 +447,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, @@ -606,13 +593,14 @@ 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. let coordinates = Coordinates::new( ScreenPoint::new(x, y), ClientPoint::new(x, y), - ElementPoint::new(x, y), + // offset x/y are set when the origin of the event is assigned to an element + ElementPoint::new(0., 0.), PagePoint::new(x, y), ); 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 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) } }