Explorar el Código

Merge remote-tracking branch 'upstream/master' into jk/rsx-refactor

Evan Almloff hace 3 años
padre
commit
9e7e5b0859

+ 1 - 1
docs/guide/src/README.md

@@ -71,7 +71,7 @@ Desktop APIs will likely be in flux as we figure out better patterns than our El
 [Jump to the getting started guide for Desktop.](/reference/platforms/desktop)
 
 Examples:
-- [File explorer](https://github.com/dioxusLabs/file-explorer/)
+- [File explorer](https://github.com/DioxusLabs/example-projects/blob/master/file-explorer)
 - [WiFi scanner](https://github.com/DioxusLabs/example-projects/blob/master/wifi-scanner)
 
 [![File ExplorerExample](https://raw.githubusercontent.com/DioxusLabs/example-projects/master/file-explorer/image.png)](https://github.com/DioxusLabs/example-projects/tree/master/file-explorer)

+ 1 - 1
docs/guide/src/setup.md

@@ -59,7 +59,7 @@ When using Debian/bullseye `libappindicator3-dev` is no longer available but rep
 sudo apt install libwebkit2gtk-4.0-dev libgtk-3-dev libayatana-appindicator3-dev
 ```
 
-If you run into issues, make sure you have all the basics installed, as outlined in the [Tauri docs](https://tauri.studio/en/docs/get-started/setup-linux).
+If you run into issues, make sure you have all the basics installed, as outlined in the [Tauri docs](https://tauri.studio/v1/guides/getting-started/prerequisites#setting-up-linux).
 
 
 ### macOS

+ 38 - 0
examples/rsx_usage.rs

@@ -46,6 +46,9 @@ fn main() {
 /// This type alias specifies the type for you so you don't need to write "None as Option<()>"
 const NONE_ELEMENT: Option<()> = None;
 
+use core::{fmt, str::FromStr};
+use std::fmt::Display;
+
 use baller::Baller;
 use dioxus::prelude::*;
 
@@ -187,6 +190,15 @@ fn app(cx: Scope) -> Element {
                 text: "using functionc all syntax"
             )
 
+            // Components can be geneirc too
+            // This component takes i32 type to give you typed input
+            TypedInput::<TypedInputProps<i32>> {}
+            // Type inference can be used too
+            TypedInput { initial: 10.0 }
+            // geneircs with the `inline_props` macro
+            label(text: "hello geneirc world!")
+            label(text: 99.9)
+
             // helper functions
             // Single values must be wrapped in braces or `Some` to satisfy `IntoIterator`
             [helper(&cx, "hello world!")]
@@ -227,9 +239,35 @@ pub fn Taller<'a>(cx: Scope<'a, TallerProps<'a>>) -> Element {
     })
 }
 
+#[derive(Props, PartialEq)]
+pub struct TypedInputProps<T> {
+    #[props(optional, default)]
+    initial: Option<T>,
+}
+
+#[allow(non_snake_case)]
+pub fn TypedInput<T>(_: Scope<TypedInputProps<T>>) -> Element
+where
+    T: FromStr + fmt::Display,
+    <T as FromStr>::Err: std::fmt::Display,
+{
+    todo!()
+}
+
 #[inline_props]
 fn with_inline<'a>(cx: Scope<'a>, text: &'a str) -> Element {
     cx.render(rsx! {
         p { "{text}" }
     })
 }
+
+// generic component with inline_props too
+#[inline_props]
+fn label<T>(cx: Scope, text: T) -> Element
+where
+    T: Display,
+{
+    cx.render(rsx! {
+        p { "{text}" }
+    })
+}

+ 75 - 0
examples/svg_basic.rs

@@ -0,0 +1,75 @@
+use dioxus::prelude::*;
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx!( svg {
+        width: "200",
+        height: "250",
+        xmlns: "http://www.w3.org/2000/svg",
+        version: "1.1",
+        rect {
+            x: "10",
+            y: "10",
+            width: "30",
+            height: "30",
+            stroke: "black",
+            fill: "transparent",
+            stroke_width: "5",
+        }
+        rect {
+            x: "60",
+            y: "10",
+            width: "30",
+            height: "30",
+            stroke: "black",
+            fill: "transparent",
+            stroke_width: "5",
+        }
+        circle {
+            cx: "25",
+            cy: "75",
+            r: "20",
+            stroke: "red",
+            fill: "transparent",
+            stroke_width: "5",
+        }
+        ellipse {
+            cx: "75",
+            cy: "75",
+            rx: "20",
+            ry: "5",
+            stroke: "red",
+            fill: "transparent",
+            stroke_width: "5",
+        }
+        line {
+            x1: "10",
+            x2: "50",
+            y1: "110",
+            y2: "150",
+            stroke: "orange",
+            stroke_width: "5",
+        }
+        polyline {
+            points: "60 110 65 120 70 115 75 130 80 125 85 140 90 135 95 150 100 145",
+            stroke: "orange",
+            fill: "transparent",
+            stroke_width: "5",
+        }
+        polygon {
+            points: "50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180",
+            stroke: "green",
+            fill: "transparent",
+            stroke_width: "5",
+        }
+        path {
+            d: "M20,230 Q40,205 50,230 T90,230",
+            fill: "none",
+            stroke: "blue",
+            stroke_width: "5",
+        }
+    }))
+}
+
+fn main() {
+    dioxus::desktop::launch(app);
+}

+ 15 - 3
packages/core-macro/src/inlineprops.rs

@@ -17,6 +17,7 @@ pub struct InlinePropsBody {
     pub inputs: Punctuated<FnArg, Token![,]>,
     // pub fields: FieldsNamed,
     pub output: ReturnType,
+    pub where_clause: Option<WhereClause>,
     pub block: Box<Block>,
 }
 
@@ -28,7 +29,7 @@ impl Parse for InlinePropsBody {
 
         let fn_token = input.parse()?;
         let ident = input.parse()?;
-        let generics = input.parse()?;
+        let generics: Generics = input.parse()?;
 
         let content;
         let paren_token = syn::parenthesized!(content in input);
@@ -47,6 +48,11 @@ impl Parse for InlinePropsBody {
 
         let output = input.parse()?;
 
+        let where_clause = input
+            .peek(syn::token::Where)
+            .then(|| input.parse())
+            .transpose()?;
+
         let block = input.parse()?;
 
         Ok(Self {
@@ -57,6 +63,7 @@ impl Parse for InlinePropsBody {
             paren_token,
             inputs,
             output,
+            where_clause,
             block,
             cx_token,
             attrs,
@@ -73,6 +80,7 @@ impl ToTokens for InlinePropsBody {
             generics,
             inputs,
             output,
+            where_clause,
             block,
             cx_token,
             attrs,
@@ -136,12 +144,16 @@ impl ToTokens for InlinePropsBody {
         out_tokens.append_all(quote! {
             #modifiers
             #[allow(non_camel_case_types)]
-            #vis struct #struct_name #struct_generics {
+            #vis struct #struct_name #struct_generics
+            #where_clause
+            {
                 #(#fields),*
             }
 
             #(#attrs)*
-            #vis fn #ident #fn_generics (#cx_token: Scope<#scope_lifetime #struct_name #generics>) #output {
+            #vis fn #ident #fn_generics (#cx_token: Scope<#scope_lifetime #struct_name #generics>) #output
+            #where_clause
+            {
                 let #struct_name { #(#field_names),* } = &cx.props;
                 #block
             }

+ 1 - 146
packages/core-macro/src/lib.rs

@@ -30,152 +30,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
 ///
 /// ## Complete Reference Guide:
 /// ```
-/// const Example: Component = |cx| {
-///     let formatting = "formatting!";
-///     let formatting_tuple = ("a", "b");
-///     let lazy_fmt = format_args!("lazily formatted text");
-///     cx.render(rsx! {
-///         div {
-///             // Elements
-///             div {}
-///             h1 {"Some text"}
-///             h1 {"Some text with {formatting}"}
-///             h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
-///             h2 {
-///                 "Multiple"
-///                 "Text"
-///                 "Blocks"
-///                 "Use comments as separators in html"
-///             }
-///             div {
-///                 h1 {"multiple"}
-///                 h2 {"nested"}
-///                 h3 {"elements"}
-///             }
-///             div {
-///                 class: "my special div"
-///                 h1 {"Headers and attributes!"}
-///             }
-///             div {
-///                 // pass simple rust expressions in
-///                 class: lazy_fmt,
-///                 id: format_args!("attributes can be passed lazily with std::fmt::Arguments"),
-///                 div {
-///                     class: {
-///                         const WORD: &str = "expressions";
-///                         format_args!("Arguments can be passed in through curly braces for complex {}", WORD)
-///                     }
-///                 }
-///             }
-///
-///             // Expressions can be used in element position too:
-///             {rsx!(p { "More templating!" })}
-///             {html!(<p>"Even HTML templating!!"</p>)}
-///
-///             // Iterators
-///             {(0..10).map(|i| rsx!(li { "{i}" }))}
-///             {{
-///                 let data = std::collections::HashMap::<&'static str, &'static str>::new();
-///                 // Iterators *should* have keys when you can provide them.
-///                 // Keys make your app run faster. Make sure your keys are stable, unique, and predictable.
-///                 // Using an "ID" associated with your data is a good idea.
-///                 data.into_iter().map(|(k, v)| rsx!(li { key: "{k}" "{v}" }))
-///             }}
-///
-///             // Matching
-///             {match true {
-///                 true => rsx!(h1 {"Top text"}),
-///                 false => rsx!(h1 {"Bottom text"})
-///             }}
-///
-///             // Conditional rendering
-///             // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
-///             // You can convert a bool condition to rsx! with .then and .or
-///             {true.then(|| rsx!(div {}))}
-///
-///             // True conditions
-///             {if true {
-///                 rsx!(h1 {"Top text"})
-///             } else {
-///                 rsx!(h1 {"Bottom text"})
-///             }}
-///
-///             // returning "None" is a bit noisy... but rare in practice
-///             {None as Option<()>}
-///
-///             // Use the Dioxus type-alias for less noise
-///             {NONE_ELEMENT}
-///
-///             // can also just use empty fragments
-///             Fragment {}
-///
-///             // Fragments let you insert groups of nodes without a parent.
-///             // This lets you make components that insert elements as siblings without a container.
-///             div {"A"}
-///             Fragment {
-///                 div {"B"}
-///                 div {"C"}
-///                 Fragment {
-///                     "D"
-///                     Fragment {
-///                         "heavily nested fragments is an antipattern"
-///                         "they cause Dioxus to do unnecessary work"
-///                         "don't use them carelessly if you can help it"
-///                     }
-///                 }
-///             }
-///
-///             // Components
-///             // Can accept any paths
-///             // Notice how you still get syntax highlighting and IDE support :)
-///             Baller {}
-///             baller::Baller { }
-///             crate::baller::Baller {}
-///
-///             // Can take properties
-///             Taller { a: "asd" }
-///
-///             // Can take optional properties
-///             Taller { a: "asd" }
-///
-///             // Can pass in props directly as an expression
-///             {{
-///                 let props = TallerProps {a: "hello"};
-///                 rsx!(Taller { ..props })
-///             }}
-///
-///             // Spreading can also be overridden manually
-///             Taller {
-///                 ..TallerProps { a: "ballin!" }
-///                 a: "not ballin!"
-///             }
-///
-///             // Can take children too!
-///             Taller { a: "asd", div {"hello world!"} }
-///         }
-///     })
-/// };
-///
-/// mod baller {
-///     use super::*;
-///     pub struct BallerProps {}
-///
-///     /// This component totally balls
-///     pub fn Baller(cx: Scope) -> DomTree {
-///         todo!()
-///     }
-/// }
-///
-/// #[derive(Debug, PartialEq, Props)]
-/// pub struct TallerProps {
-///     a: &'static str,
-/// }
-///
-/// /// This component is taller than most :)
-/// pub fn Taller(cx: Scope<TallerProps>) -> DomTree {
-///     let b = true;
-///     todo!()
-/// }
+#[doc = include_str!("../../../examples/rsx_usage.rs")]
 /// ```
 #[proc_macro]
 pub fn rsx(s: TokenStream) -> TokenStream {

+ 3 - 1
packages/core-macro/src/props/mod.rs

@@ -653,7 +653,9 @@ Finally, call `.build()` to create the instance of `{name}`.
                     }
                 }
 
-                impl #impl_generics dioxus::prelude::Properties for #name #ty_generics{
+                impl #impl_generics dioxus::prelude::Properties for #name #ty_generics
+                #b_generics_where_extras_predicates
+                {
                     type Builder = #builder_name #generics_with_empty;
                     const IS_STATIC: bool = #is_static;
                     fn builder() -> Self::Builder {

+ 4 - 5
packages/core/src/scopes.rs

@@ -609,7 +609,7 @@ impl ScopeState {
 
     /// Create a subscription that schedules a future render for the reference component
     ///
-    /// ## Notice: you should prefer using prepare_update and get_scope_id
+    /// ## Notice: you should prefer using schedule_update_any and scope_id
     pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
         let (chan, id) = (self.tasks.sender.clone(), self.scope_id());
         Arc::new(move || {
@@ -1000,12 +1000,11 @@ impl TaskQueue {
     fn remove(&self, id: TaskId) {
         if let Ok(mut tasks) = self.tasks.try_borrow_mut() {
             let _ = tasks.remove(&id);
+            if let Some(task_map) = self.task_map.borrow_mut().get_mut(&id.scope) {
+                task_map.remove(&id);
+            }
         }
-
         // the task map is still around, but it'll be removed when the scope is unmounted
-        if let Some(task_map) = self.task_map.borrow_mut().get_mut(&id.scope) {
-            task_map.remove(&id);
-        }
     }
 
     pub(crate) fn has_tasks(&self) -> bool {

+ 4 - 21
packages/core/src/virtual_dom.rs

@@ -340,29 +340,12 @@ impl VirtualDom {
 
                     let scopes = &mut self.scopes;
                     let task_poll = poll_fn(|cx| {
-                        //
-                        let mut any_pending = false;
-
                         let mut tasks = scopes.tasks.tasks.borrow_mut();
-                        let mut to_remove = vec![];
-
-                        // this would be better served by retain
-                        for (id, task) in tasks.iter_mut() {
-                            if task.as_mut().poll(cx).is_ready() {
-                                to_remove.push(*id);
-                            } else {
-                                any_pending = true;
-                            }
-                        }
-
-                        for id in to_remove {
-                            tasks.remove(&id);
-                        }
+                        tasks.retain(|_, task| task.as_mut().poll(cx).is_pending());
 
-                        // Resolve the future if any singular task is ready
-                        match any_pending {
-                            true => Poll::Pending,
-                            false => Poll::Ready(()),
+                        match tasks.is_empty() {
+                            true => Poll::Ready(()),
+                            false => Poll::Pending,
                         }
                     });
 

+ 7 - 0
packages/fermi/src/root.rs

@@ -74,6 +74,13 @@ impl AtomRoot {
             }
         } else {
             log::trace!("no atoms found for {:?}", ptr);
+            atoms.insert(
+                ptr,
+                Slot {
+                    value: Rc::new(value),
+                    subscribers: HashSet::new(),
+                },
+            );
         }
     }
 

+ 1 - 1
packages/native-core/Cargo.toml

@@ -15,7 +15,7 @@ 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" }
 
-stretch2 = "0.4.2"
+taffy = "0.1.0"
 smallvec = "1.6"
 fxhash = "0.2"
 anymap = "0.12.1"

+ 10 - 25
packages/native-core/src/layout_attributes.rs

@@ -1,6 +1,6 @@
 /*
 - [ ] pub display: Display,
-- [x] pub position_type: PositionType,  --> kinda, stretch doesnt support everything
+- [x] pub position_type: PositionType,  --> kinda, taffy doesnt support everything
 - [ ] pub direction: Direction,
 
 - [x] pub flex_direction: FlexDirection,
@@ -9,7 +9,7 @@
 - [x] pub flex_shrink: f32,
 - [x] pub flex_basis: Dimension,
 
-- [x] pub overflow: Overflow, ---> kinda implemented... stretch doesnt have support for directional overflow
+- [x] pub overflow: Overflow, ---> kinda implemented... taffy doesnt have support for directional overflow
 
 - [x] pub align_items: AlignItems,
 - [x] pub align_self: AlignSelf,
@@ -29,7 +29,10 @@
 - [ ] pub aspect_ratio: Number,
 */
 
-use stretch2::{prelude::*, style::PositionType};
+use taffy::{
+    prelude::*,
+    style::{FlexDirection, PositionType},
+};
 
 /// applies the entire html namespace defined in dioxus-html
 pub fn apply_layout_attributes(name: &str, value: &str, style: &mut Style) {
@@ -109,13 +112,7 @@ pub fn apply_layout_attributes(name: &str, value: &str, style: &mut Style) {
         "counter-reset" => {}
 
         "cursor" => {}
-        "direction" => {
-            match value {
-                "ltr" => style.direction = Direction::LTR,
-                "rtl" => style.direction = Direction::RTL,
-                _ => {}
-            }
-        }
+        "direction" => {}
 
         "display" => apply_display(name, value, style),
 
@@ -283,20 +280,8 @@ pub fn parse_value(value: &str) -> Option<UnitSystem> {
     }
 }
 
-fn apply_overflow(name: &str, value: &str, style: &mut Style) {
-    match name {
-        // todo: add more overflow support to stretch2
-        "overflow" | "overflow-x" | "overflow-y" => {
-            style.overflow = match value {
-                "auto" => Overflow::Visible,
-                "hidden" => Overflow::Hidden,
-                "scroll" => Overflow::Scroll,
-                "visible" => Overflow::Visible,
-                _ => Overflow::Visible,
-            };
-        }
-        _ => {}
-    }
+fn apply_overflow(_name: &str, _value: &str, _style: &mut Style) {
+    // todo: add overflow support to taffy
 }
 
 fn apply_display(_name: &str, value: &str, style: &mut Style) {
@@ -307,7 +292,7 @@ fn apply_display(_name: &str, value: &str, style: &mut Style) {
     }
 
     // TODO: there are way more variants
-    // stretch needs to be updated to handle them
+    // taffy needs to be updated to handle them
     //
     // "block" => Display::Block,
     // "inline" => Display::Inline,

+ 47 - 3
packages/rsx/src/component.rs

@@ -18,20 +18,59 @@ use quote::{quote, ToTokens, TokenStreamExt};
 use syn::{
     ext::IdentExt,
     parse::{Parse, ParseBuffer, ParseStream},
-    token, Error, Expr, Ident, LitStr, Result, Token,
+    spanned::Spanned,
+    token, AngleBracketedGenericArguments, Error, Expr, Ident, LitStr, PathArguments, Result,
+    Token,
 };
 
 #[derive(PartialEq, Eq)]
 pub struct Component {
     pub name: syn::Path,
+    pub prop_gen_args: Option<AngleBracketedGenericArguments>,
     pub body: Vec<ComponentField>,
     pub children: Vec<BodyNode>,
     pub manual_props: Option<Expr>,
 }
 
+impl Component {
+    pub fn validate_component_path(path: &syn::Path) -> Result<()> {
+        // ensure path segments doesn't have PathArguments, only the last
+        // segment is allowed to have one.
+        if path
+            .segments
+            .iter()
+            .take(path.segments.len() - 1)
+            .any(|seg| seg.arguments != PathArguments::None)
+        {
+            component_path_cannot_have_arguments!(path.span());
+        }
+
+        // ensure last segment only have value of None or AngleBracketed
+        if !matches!(
+            path.segments.last().unwrap().arguments,
+            PathArguments::None | PathArguments::AngleBracketed(_)
+        ) {
+            invalid_component_path!(path.span());
+        }
+
+        Ok(())
+    }
+}
+
 impl Parse for Component {
     fn parse(stream: ParseStream) -> Result<Self> {
-        let name = syn::Path::parse_mod_style(stream)?;
+        let mut name = stream.parse::<syn::Path>()?;
+        Component::validate_component_path(&name)?;
+
+        // extract the path arguments from the path into prop_gen_args
+        let prop_gen_args = name.segments.last_mut().and_then(|seg| {
+            if let PathArguments::AngleBracketed(args) = seg.arguments.clone() {
+                seg.arguments = PathArguments::None;
+                Some(args)
+            } else {
+                None
+            }
+        });
 
         let content: ParseBuffer;
 
@@ -65,6 +104,7 @@ impl Parse for Component {
 
         Ok(Self {
             name,
+            prop_gen_args,
             body,
             children,
             manual_props,
@@ -75,6 +115,7 @@ impl Parse for Component {
 impl ToTokens for Component {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let name = &self.name;
+        let prop_gen_args = &self.prop_gen_args;
 
         let mut has_key = None;
 
@@ -102,7 +143,10 @@ impl ToTokens for Component {
                 }}
             }
             None => {
-                let mut toks = quote! { fc_to_builder(#name) };
+                let mut toks = match prop_gen_args {
+                    Some(gen_args) => quote! { fc_to_builder #gen_args(#name) },
+                    None => quote! { fc_to_builder(#name) },
+                };
                 for field in &self.body {
                     match field.name.to_string().as_str() {
                         "key" => {

+ 15 - 0
packages/rsx/src/errors.rs

@@ -9,3 +9,18 @@ macro_rules! attr_after_element {
         return Err(Error::new($span, "expected element\n  = help move the attribute above all the children and text elements"));
     };
 }
+
+macro_rules! component_path_cannot_have_arguments {
+    ($span:expr) => {
+        return Err(Error::new(
+            $span,
+            "expected a path without arguments\n  = try remove the path arguments",
+        ));
+    };
+}
+
+macro_rules! invalid_component_path {
+    ($span:expr) => {
+        return Err(Error::new($span, "Invalid component path syntax"));
+    };
+}

+ 41 - 31
packages/rsx/src/node.rs

@@ -4,7 +4,7 @@ use proc_macro2::TokenStream as TokenStream2;
 use quote::{quote, ToTokens, TokenStreamExt};
 use syn::{
     parse::{Parse, ParseStream},
-    token, Expr, LitStr, Result, Token,
+    token, Expr, LitStr, Result,
 };
 
 /*
@@ -38,39 +38,49 @@ impl Parse for BodyNode {
             return Ok(BodyNode::Text(stream.parse()?));
         }
 
-        // div {} -> el
-        // Div {} -> comp
-        if stream.peek(syn::Ident) && stream.peek2(token::Brace) {
-            if stream
-                .fork()
-                .parse::<Ident>()?
-                .to_string()
-                .chars()
-                .next()
-                .unwrap()
-                .is_ascii_uppercase()
-            {
-                return Ok(BodyNode::Component(stream.parse()?));
-            } else {
-                return Ok(BodyNode::Element(stream.parse::<Element>()?));
+        let body_stream = stream.fork();
+        if let Ok(path) = body_stream.parse::<syn::Path>() {
+            // this is an Element if path match of:
+            // - one ident
+            // - followed by `{`
+            // - 1st char is lowercase
+            //
+            // example:
+            // div {}
+            if let Some(ident) = path.get_ident() {
+                if body_stream.peek(token::Brace)
+                    && ident
+                        .to_string()
+                        .chars()
+                        .next()
+                        .unwrap()
+                        .is_ascii_lowercase()
+                {
+                    return Ok(BodyNode::Element(stream.parse::<Element>()?));
+                }
             }
-        }
 
-        // component() -> comp
-        // ::component {} -> comp
-        // ::component () -> comp
-        if (stream.peek(syn::Ident) && stream.peek2(token::Paren))
-            || (stream.peek(Token![::]))
-            || (stream.peek(Token![:]) && stream.peek2(Token![:]))
-        {
-            return Ok(BodyNode::Component(stream.parse::<Component>()?));
-        }
+            // Otherwise this should be Component, allowed syntax:
+            // - syn::Path
+            // - PathArguments can only apper in last segment
+            // - followed by `{` or `(`, note `(` cannot be used with one ident
+            //
+            // example
+            // Div {}
+            // Div ()
+            // ::Div {}
+            // crate::Div {}
+            // component()
+            // ::component {}
+            // ::component ()
+            // crate::component{}
+            // crate::component()
+            // Input::<InputProps<'_, i32> {}
+            // crate::Input::<InputProps<'_, i32> {}
+            if body_stream.peek(token::Brace) || body_stream.peek(token::Paren) {
+                Component::validate_component_path(&path)?;
 
-        // crate::component{} -> comp
-        // crate::component() -> comp
-        if let Ok(pat) = stream.fork().parse::<syn::Path>() {
-            if pat.segments.len() > 1 {
-                return Ok(BodyNode::Component(stream.parse::<Component>()?));
+                return Ok(BodyNode::Component(stream.parse()?));
             }
         }
 

+ 1 - 1
packages/tui/Cargo.toml

@@ -23,7 +23,7 @@ crossterm = "0.23.0"
 anyhow = "1.0.42"
 tokio = { version = "1.15.0", features = ["full"] }
 futures = "0.3.19"
-stretch2 = "0.4.2"
+taffy = "0.1.0"
 smallvec = "1.6"
 fxhash = "0.2"
 anymap = "0.12.1"

+ 5 - 5
packages/tui/src/hooks.rs

@@ -17,8 +17,8 @@ use std::{
     sync::Arc,
     time::{Duration, Instant},
 };
-use stretch2::geometry::{Point, Size};
-use stretch2::{prelude::Layout, Stretch};
+use taffy::geometry::{Point, Size};
+use taffy::{prelude::Layout, Taffy};
 
 use crate::{Dom, Node};
 
@@ -153,7 +153,7 @@ impl InnerInputState {
         &mut self,
         evts: &mut [EventCore],
         resolved_events: &mut Vec<UserEvent>,
-        layout: &Stretch,
+        layout: &Taffy,
         dom: &mut Dom,
     ) {
         let previous_mouse = self.mouse.clone();
@@ -175,7 +175,7 @@ impl InnerInputState {
         &self,
         previous_mouse: Option<MouseData>,
         resolved_events: &mut Vec<UserEvent>,
-        layout: &Stretch,
+        layout: &Taffy,
         dom: &mut Dom,
     ) {
         fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool {
@@ -523,7 +523,7 @@ impl RinkInputHandler {
         )
     }
 
-    pub(crate) fn get_events(&self, layout: &Stretch, dom: &mut Dom) -> Vec<UserEvent> {
+    pub(crate) fn get_events(&self, layout: &Taffy, dom: &mut Dom) -> Vec<UserEvent> {
         let mut resolved_events = Vec::new();
 
         (*self.state).borrow_mut().update(

+ 11 - 12
packages/tui/src/layout.rs

@@ -6,7 +6,7 @@ use dioxus_native_core::layout_attributes::apply_layout_attributes;
 use dioxus_native_core::node_ref::{AttributeMask, NodeMask, NodeView};
 use dioxus_native_core::state::ChildDepState;
 use dioxus_native_core_macro::sorted_str_slice;
-use stretch2::prelude::*;
+use taffy::prelude::*;
 
 #[derive(Debug, Clone, Copy, PartialEq)]
 pub(crate) enum PossiblyUninitalized<T> {
@@ -28,13 +28,13 @@ impl<T> Default for PossiblyUninitalized<T> {
 }
 
 #[derive(Clone, PartialEq, Default, Debug)]
-pub(crate) struct StretchLayout {
+pub(crate) struct TaffyLayout {
     pub style: Style,
     pub node: PossiblyUninitalized<Node>,
 }
 
-impl ChildDepState for StretchLayout {
-    type Ctx = Rc<RefCell<Stretch>>;
+impl ChildDepState for TaffyLayout {
+    type Ctx = Rc<RefCell<Taffy>>;
     type DepState = Self;
     // use tag to force this to be called when a node is built
     const NODE_MASK: NodeMask =
@@ -53,7 +53,7 @@ impl ChildDepState for StretchLayout {
         Self::DepState: 'a,
     {
         let mut changed = false;
-        let mut stretch = ctx.borrow_mut();
+        let mut taffy = ctx.borrow_mut();
         let mut style = Style::default();
         if let Some(text) = node.text() {
             let char_len = text.chars().count();
@@ -70,11 +70,10 @@ impl ChildDepState for StretchLayout {
             };
             if let PossiblyUninitalized::Initialized(n) = self.node {
                 if self.style != style {
-                    stretch.set_style(n, style).unwrap();
+                    taffy.set_style(n, style).unwrap();
                 }
             } else {
-                self.node =
-                    PossiblyUninitalized::Initialized(stretch.new_node(style, &[]).unwrap());
+                self.node = PossiblyUninitalized::Initialized(taffy.new_node(style, &[]).unwrap());
                 changed = true;
             }
         } else {
@@ -100,14 +99,14 @@ impl ChildDepState for StretchLayout {
 
             if let PossiblyUninitalized::Initialized(n) = self.node {
                 if self.style != style {
-                    stretch.set_style(n, style).unwrap();
+                    taffy.set_style(n, style).unwrap();
                 }
-                if stretch.children(n).unwrap() != child_layout {
-                    stretch.set_children(n, &child_layout).unwrap();
+                if taffy.children(n).unwrap() != child_layout {
+                    taffy.set_children(n, &child_layout).unwrap();
                 }
             } else {
                 self.node = PossiblyUninitalized::Initialized(
-                    stretch.new_node(style, &child_layout).unwrap(),
+                    taffy.new_node(style, &child_layout).unwrap(),
                 );
                 changed = true;
             }

+ 24 - 17
packages/tui/src/lib.rs

@@ -13,12 +13,12 @@ use futures::{
     channel::mpsc::{UnboundedReceiver, UnboundedSender},
     pin_mut, StreamExt,
 };
-use layout::StretchLayout;
+use layout::TaffyLayout;
 use std::cell::RefCell;
 use std::rc::Rc;
 use std::{io, time::Duration};
-use stretch2::{prelude::Size, Stretch};
 use style_attributes::StyleModifier;
+use taffy::{geometry::Point, prelude::Size, Taffy};
 use tui::{backend::CrosstermBackend, layout::Rect, Terminal};
 
 mod config;
@@ -37,8 +37,8 @@ type Node = dioxus_native_core::real_dom::Node<NodeState>;
 
 #[derive(Debug, Clone, State, Default)]
 struct NodeState {
-    #[child_dep_state(layout, RefCell<Stretch>)]
-    layout: StretchLayout,
+    #[child_dep_state(layout, RefCell<Taffy>)]
+    layout: TaffyLayout,
     // depends on attributes, the C component of it's parent and a u8 context
     #[parent_dep_state(style)]
     style: StyleModifier,
@@ -88,9 +88,9 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
     let mut rdom: Dom = RealDom::new();
     let mutations = dom.rebuild();
     let to_update = rdom.apply_mutations(vec![mutations]);
-    let stretch = Rc::new(RefCell::new(Stretch::new()));
+    let taffy = Rc::new(RefCell::new(Taffy::new()));
     let mut any_map = AnyMap::new();
-    any_map.insert(stretch.clone());
+    any_map.insert(taffy.clone());
     let _to_rerender = rdom.update_state(&dom, to_update, any_map).unwrap();
 
     render_vdom(
@@ -99,7 +99,7 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
         handler,
         cfg,
         rdom,
-        stretch,
+        taffy,
         register_event,
     )
     .unwrap();
@@ -111,7 +111,7 @@ fn render_vdom(
     handler: RinkInputHandler,
     cfg: Config,
     mut rdom: Dom,
-    stretch: Rc<RefCell<Stretch>>,
+    taffy: Rc<RefCell<Taffy>>,
     mut register_event: impl FnMut(crossterm::event::Event),
 ) -> Result<()> {
     tokio::runtime::Builder::new_current_thread()
@@ -146,17 +146,17 @@ fn render_vdom(
 
                 if !to_rerender.is_empty() || resized {
                     resized = false;
-                    fn resize(dims: Rect, stretch: &mut Stretch, rdom: &Dom) {
+                    fn resize(dims: Rect, taffy: &mut Taffy, rdom: &Dom) {
                         let width = dims.width;
                         let height = dims.height;
                         let root_node = rdom[0].state.layout.node.unwrap();
 
-                        stretch
+                        taffy
                             .compute_layout(
                                 root_node,
                                 Size {
-                                    width: stretch2::prelude::Number::Defined((width - 1) as f32),
-                                    height: stretch2::prelude::Number::Defined((height - 1) as f32),
+                                    width: taffy::prelude::Number::Defined((width - 1) as f32),
+                                    height: taffy::prelude::Number::Defined((height - 1) as f32),
                                 },
                             )
                             .unwrap();
@@ -164,9 +164,16 @@ fn render_vdom(
                     if let Some(terminal) = &mut terminal {
                         terminal.draw(|frame| {
                             // size is guaranteed to not change when rendering
-                            resize(frame.size(), &mut stretch.borrow_mut(), &rdom);
+                            resize(frame.size(), &mut taffy.borrow_mut(), &rdom);
                             let root = &rdom[0];
-                            render::render_vnode(frame, &stretch.borrow(), &rdom, root, cfg);
+                            render::render_vnode(
+                                frame,
+                                &taffy.borrow(),
+                                &rdom,
+                                root,
+                                cfg,
+                                Point::zero(),
+                            );
                         })?;
                     } else {
                         resize(
@@ -176,7 +183,7 @@ fn render_vdom(
                                 width: 300,
                                 height: 300,
                             },
-                            &mut stretch.borrow_mut(),
+                            &mut taffy.borrow_mut(),
                             &rdom,
                         );
                     }
@@ -216,7 +223,7 @@ fn render_vdom(
                 }
 
                 {
-                    let evts = handler.get_events(&stretch.borrow(), &mut rdom);
+                    let evts = handler.get_events(&taffy.borrow(), &mut rdom);
                     for e in evts {
                         vdom.handle_message(SchedulerMsg::Event(e));
                     }
@@ -225,7 +232,7 @@ fn render_vdom(
                     let to_update = rdom.apply_mutations(mutations);
                     // update the style and layout
                     let mut any_map = AnyMap::new();
-                    any_map.insert(stretch.clone());
+                    any_map.insert(taffy.clone());
                     to_rerender = rdom.update_state(vdom, to_update, any_map).unwrap();
                 }
             }

+ 12 - 7
packages/tui/src/render.rs

@@ -1,9 +1,9 @@
 use dioxus_native_core::layout_attributes::UnitSystem;
 use std::io::Stdout;
-use stretch2::{
+use taffy::{
     geometry::Point,
     prelude::{Layout, Size},
-    Stretch,
+    Taffy,
 };
 use tui::{backend::CrosstermBackend, layout::Rect};
 
@@ -18,10 +18,11 @@ const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
 
 pub(crate) fn render_vnode(
     frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
-    layout: &Stretch,
+    layout: &Taffy,
     rdom: &Dom,
     node: &Node,
     cfg: Config,
+    parent_location: Point<f32>,
 ) {
     use dioxus_native_core::real_dom::NodeType;
 
@@ -29,7 +30,11 @@ pub(crate) fn render_vnode(
         return;
     }
 
-    let Layout { location, size, .. } = layout.layout(node.state.layout.node.unwrap()).unwrap();
+    let Layout {
+        mut location, size, ..
+    } = layout.layout(node.state.layout.node.unwrap()).unwrap();
+    location.x += parent_location.x;
+    location.y += parent_location.y;
 
     let Point { x, y } = location;
     let Size { width, height } = size;
@@ -57,7 +62,7 @@ pub(crate) fn render_vnode(
                 text,
                 style: node.state.style.core,
             };
-            let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
+            let area = Rect::new(x as u16, y as u16, *width as u16, *height as u16);
 
             // the renderer will panic if a node is rendered out of range even if the size is zero
             if area.width > 0 && area.height > 0 {
@@ -65,7 +70,7 @@ pub(crate) fn render_vnode(
             }
         }
         NodeType::Element { children, .. } => {
-            let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
+            let area = Rect::new(x as u16, y as u16, *width as u16, *height as u16);
 
             // the renderer will panic if a node is rendered out of range even if the size is zero
             if area.width > 0 && area.height > 0 {
@@ -73,7 +78,7 @@ pub(crate) fn render_vnode(
             }
 
             for c in children {
-                render_vnode(frame, layout, rdom, &rdom[c.0], cfg);
+                render_vnode(frame, layout, rdom, &rdom[c.0], cfg, location);
             }
         }
         NodeType::Placeholder => unreachable!(),

+ 2 - 2
packages/tui/src/style_attributes.rs

@@ -1,6 +1,6 @@
 /*
 - [ ] pub display: Display,
-- [x] pub position_type: PositionType,  --> kinda, stretch doesnt support everything
+- [x] pub position_type: PositionType,  --> kinda, taffy doesnt support everything
 - [ ] pub direction: Direction,
 
 - [x] pub flex_direction: FlexDirection,
@@ -9,7 +9,7 @@
 - [x] pub flex_shrink: f32,
 - [x] pub flex_basis: Dimension,
 
-- [x] pub overflow: Overflow, ---> kinda implemented... stretch doesnt have support for directional overflow
+- [x] pub overflow: Overflow, ---> kinda implemented... taffy doesnt have support for directional overflow
 
 - [x] pub align_items: AlignItems,
 - [x] pub align_self: AlignSelf,

+ 40 - 42
packages/tui/tests/margin.rs

@@ -1,15 +1,13 @@
-use stretch2 as stretch;
-
 #[test]
 fn margin_and_flex_row() {
-    let mut stretch = stretch::Stretch::new();
-    let node0 = stretch
+    let mut taffy = taffy::Taffy::new();
+    let node0 = taffy
         .new_node(
-            stretch::style::Style {
+            taffy::style::Style {
                 flex_grow: 1f32,
-                margin: stretch::geometry::Rect {
-                    start: stretch::style::Dimension::Points(10f32),
-                    end: stretch::style::Dimension::Points(10f32),
+                margin: taffy::geometry::Rect {
+                    start: taffy::style::Dimension::Points(10f32),
+                    end: taffy::style::Dimension::Points(10f32),
                     ..Default::default()
                 },
                 ..Default::default()
@@ -17,50 +15,50 @@ fn margin_and_flex_row() {
             &[],
         )
         .unwrap();
-    let node = stretch
+    let node = taffy
         .new_node(
-            stretch::style::Style {
-                size: stretch::geometry::Size {
-                    width: stretch::style::Dimension::Points(100f32),
-                    height: stretch::style::Dimension::Points(100f32),
+            taffy::style::Style {
+                size: taffy::geometry::Size {
+                    width: taffy::style::Dimension::Points(100f32),
+                    height: taffy::style::Dimension::Points(100f32),
                 },
                 ..Default::default()
             },
             &[node0],
         )
         .unwrap();
-    stretch
-        .compute_layout(node, stretch::geometry::Size::undefined())
+    taffy
+        .compute_layout(node, taffy::geometry::Size::undefined())
         .unwrap();
-    assert_eq!(stretch.layout(node).unwrap().size.width, 100f32);
-    assert_eq!(stretch.layout(node).unwrap().size.height, 100f32);
-    assert_eq!(stretch.layout(node).unwrap().location.x, 0f32);
-    assert_eq!(stretch.layout(node).unwrap().location.y, 0f32);
-    assert_eq!(stretch.layout(node0).unwrap().size.width, 80f32);
-    assert_eq!(stretch.layout(node0).unwrap().size.height, 100f32);
-    assert_eq!(stretch.layout(node0).unwrap().location.x, 10f32);
-    assert_eq!(stretch.layout(node0).unwrap().location.y, 0f32);
+    assert_eq!(taffy.layout(node).unwrap().size.width, 100f32);
+    assert_eq!(taffy.layout(node).unwrap().size.height, 100f32);
+    assert_eq!(taffy.layout(node).unwrap().location.x, 0f32);
+    assert_eq!(taffy.layout(node).unwrap().location.y, 0f32);
+    assert_eq!(taffy.layout(node0).unwrap().size.width, 80f32);
+    assert_eq!(taffy.layout(node0).unwrap().size.height, 100f32);
+    assert_eq!(taffy.layout(node0).unwrap().location.x, 10f32);
+    assert_eq!(taffy.layout(node0).unwrap().location.y, 0f32);
 }
 
 #[test]
 fn margin_and_flex_row2() {
-    let mut stretch = stretch::Stretch::new();
-    let node0 = stretch
+    let mut taffy = taffy::Taffy::new();
+    let node0 = taffy
         .new_node(
-            stretch::style::Style {
+            taffy::style::Style {
                 flex_grow: 1f32,
-                margin: stretch::geometry::Rect {
+                margin: taffy::geometry::Rect {
                     // left
-                    start: stretch::style::Dimension::Points(10f32),
+                    start: taffy::style::Dimension::Points(10f32),
 
                     // right?
-                    end: stretch::style::Dimension::Points(10f32),
+                    end: taffy::style::Dimension::Points(10f32),
 
                     // top?
-                    // top: stretch::style::Dimension::Points(10f32),
+                    // top: taffy::style::Dimension::Points(10f32),
 
                     // bottom?
-                    // bottom: stretch::style::Dimension::Points(10f32),
+                    // bottom: taffy::style::Dimension::Points(10f32),
                     ..Default::default()
                 },
                 ..Default::default()
@@ -69,12 +67,12 @@ fn margin_and_flex_row2() {
         )
         .unwrap();
 
-    let node = stretch
+    let node = taffy
         .new_node(
-            stretch::style::Style {
-                size: stretch::geometry::Size {
-                    width: stretch::style::Dimension::Points(100f32),
-                    height: stretch::style::Dimension::Points(100f32),
+            taffy::style::Style {
+                size: taffy::geometry::Size {
+                    width: taffy::style::Dimension::Points(100f32),
+                    height: taffy::style::Dimension::Points(100f32),
                 },
                 ..Default::default()
             },
@@ -82,12 +80,12 @@ fn margin_and_flex_row2() {
         )
         .unwrap();
 
-    stretch
-        .compute_layout(node, stretch::geometry::Size::undefined())
+    taffy
+        .compute_layout(node, taffy::geometry::Size::undefined())
         .unwrap();
 
-    assert_eq!(stretch.layout(node).unwrap().size.width, 100f32);
-    assert_eq!(stretch.layout(node).unwrap().size.height, 100f32);
-    assert_eq!(stretch.layout(node).unwrap().location.x, 0f32);
-    assert_eq!(stretch.layout(node).unwrap().location.y, 0f32);
+    assert_eq!(taffy.layout(node).unwrap().size.width, 100f32);
+    assert_eq!(taffy.layout(node).unwrap().size.height, 100f32);
+    assert_eq!(taffy.layout(node).unwrap().location.x, 0f32);
+    assert_eq!(taffy.layout(node).unwrap().location.y, 0f32);
 }