Merge remote-tracking branch 'upstream/master' into jk/rsx-refactor
This commit is contained in:
commit
9e7e5b0859
22 files changed
+358
-324
No files matched your search
@@ -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)
|
[Jump to the getting started guide for Desktop.](/reference/platforms/desktop)
|
||||||
|
|
||||||
Examples:
|
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)
|
- [WiFi scanner](https://github.com/DioxusLabs/example-projects/blob/master/wifi-scanner)
|
||||||
|
|
||||||
[](https://github.com/DioxusLabs/example-projects/tree/master/file-explorer)
|
[](https://github.com/DioxusLabs/example-projects/tree/master/file-explorer)
|
||||||
|
|||||||
@@ -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
|
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
|
### macOS
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ fn main() {
|
|||||||
/// This type alias specifies the type for you so you don't need to write "None as Option<()>"
|
/// This type alias specifies the type for you so you don't need to write "None as Option<()>"
|
||||||
const NONE_ELEMENT: Option<()> = None;
|
const NONE_ELEMENT: Option<()> = None;
|
||||||
|
|
||||||
|
use core::{fmt, str::FromStr};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use baller::Baller;
|
use baller::Baller;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
@@ -187,6 +190,15 @@ fn app(cx: Scope) -> Element {
|
|||||||
text: "using functionc all syntax"
|
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
|
// helper functions
|
||||||
// Single values must be wrapped in braces or `Some` to satisfy `IntoIterator`
|
// Single values must be wrapped in braces or `Some` to satisfy `IntoIterator`
|
||||||
[helper(&cx, "hello world!")]
|
[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]
|
#[inline_props]
|
||||||
fn with_inline<'a>(cx: Scope<'a>, text: &'a str) -> Element {
|
fn with_inline<'a>(cx: Scope<'a>, text: &'a str) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
p { "{text}" }
|
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}" }
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ pub struct InlinePropsBody {
|
|||||||
pub inputs: Punctuated<FnArg, Token![,]>,
|
pub inputs: Punctuated<FnArg, Token![,]>,
|
||||||
// pub fields: FieldsNamed,
|
// pub fields: FieldsNamed,
|
||||||
pub output: ReturnType,
|
pub output: ReturnType,
|
||||||
|
pub where_clause: Option<WhereClause>,
|
||||||
pub block: Box<Block>,
|
pub block: Box<Block>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ impl Parse for InlinePropsBody {
|
|||||||
|
|
||||||
let fn_token = input.parse()?;
|
let fn_token = input.parse()?;
|
||||||
let ident = input.parse()?;
|
let ident = input.parse()?;
|
||||||
let generics = input.parse()?;
|
let generics: Generics = input.parse()?;
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
let paren_token = syn::parenthesized!(content in input);
|
let paren_token = syn::parenthesized!(content in input);
|
||||||
@@ -47,6 +48,11 @@ impl Parse for InlinePropsBody {
|
|||||||
|
|
||||||
let output = input.parse()?;
|
let output = input.parse()?;
|
||||||
|
|
||||||
|
let where_clause = input
|
||||||
|
.peek(syn::token::Where)
|
||||||
|
.then(|| input.parse())
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
let block = input.parse()?;
|
let block = input.parse()?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -57,6 +63,7 @@ impl Parse for InlinePropsBody {
|
|||||||
paren_token,
|
paren_token,
|
||||||
inputs,
|
inputs,
|
||||||
output,
|
output,
|
||||||
|
where_clause,
|
||||||
block,
|
block,
|
||||||
cx_token,
|
cx_token,
|
||||||
attrs,
|
attrs,
|
||||||
@@ -73,6 +80,7 @@ impl ToTokens for InlinePropsBody {
|
|||||||
generics,
|
generics,
|
||||||
inputs,
|
inputs,
|
||||||
output,
|
output,
|
||||||
|
where_clause,
|
||||||
block,
|
block,
|
||||||
cx_token,
|
cx_token,
|
||||||
attrs,
|
attrs,
|
||||||
@@ -136,12 +144,16 @@ impl ToTokens for InlinePropsBody {
|
|||||||
out_tokens.append_all(quote! {
|
out_tokens.append_all(quote! {
|
||||||
#modifiers
|
#modifiers
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#vis struct #struct_name #struct_generics {
|
#vis struct #struct_name #struct_generics
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
#(#fields),*
|
#(#fields),*
|
||||||
}
|
}
|
||||||
|
|
||||||
#(#attrs)*
|
#(#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;
|
let #struct_name { #(#field_names),* } = &cx.props;
|
||||||
#block
|
#block
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,152 +30,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||||||
///
|
///
|
||||||
/// ## Complete Reference Guide:
|
/// ## Complete Reference Guide:
|
||||||
/// ```
|
/// ```
|
||||||
/// const Example: Component = |cx| {
|
#[doc = include_str!("../../../examples/rsx_usage.rs")]
|
||||||
/// 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!()
|
|
||||||
/// }
|
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn rsx(s: TokenStream) -> TokenStream {
|
pub fn rsx(s: TokenStream) -> TokenStream {
|
||||||
|
|||||||
@@ -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;
|
type Builder = #builder_name #generics_with_empty;
|
||||||
const IS_STATIC: bool = #is_static;
|
const IS_STATIC: bool = #is_static;
|
||||||
fn builder() -> Self::Builder {
|
fn builder() -> Self::Builder {
|
||||||
|
|||||||
@@ -609,7 +609,7 @@ impl ScopeState {
|
|||||||
|
|
||||||
/// Create a subscription that schedules a future render for the reference component
|
/// 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> {
|
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
|
||||||
let (chan, id) = (self.tasks.sender.clone(), self.scope_id());
|
let (chan, id) = (self.tasks.sender.clone(), self.scope_id());
|
||||||
Arc::new(move || {
|
Arc::new(move || {
|
||||||
@@ -1000,12 +1000,11 @@ impl TaskQueue {
|
|||||||
fn remove(&self, id: TaskId) {
|
fn remove(&self, id: TaskId) {
|
||||||
if let Ok(mut tasks) = self.tasks.try_borrow_mut() {
|
if let Ok(mut tasks) = self.tasks.try_borrow_mut() {
|
||||||
let _ = tasks.remove(&id);
|
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
|
// 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 {
|
pub(crate) fn has_tasks(&self) -> bool {
|
||||||
|
|||||||
@@ -340,29 +340,12 @@ impl VirtualDom {
|
|||||||
|
|
||||||
let scopes = &mut self.scopes;
|
let scopes = &mut self.scopes;
|
||||||
let task_poll = poll_fn(|cx| {
|
let task_poll = poll_fn(|cx| {
|
||||||
//
|
|
||||||
let mut any_pending = false;
|
|
||||||
|
|
||||||
let mut tasks = scopes.tasks.tasks.borrow_mut();
|
let mut tasks = scopes.tasks.tasks.borrow_mut();
|
||||||
let mut to_remove = vec![];
|
tasks.retain(|_, task| task.as_mut().poll(cx).is_pending());
|
||||||
|
|
||||||
// this would be better served by retain
|
match tasks.is_empty() {
|
||||||
for (id, task) in tasks.iter_mut() {
|
true => Poll::Ready(()),
|
||||||
if task.as_mut().poll(cx).is_ready() {
|
false => Poll::Pending,
|
||||||
to_remove.push(*id);
|
|
||||||
} else {
|
|
||||||
any_pending = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for id in to_remove {
|
|
||||||
tasks.remove(&id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve the future if any singular task is ready
|
|
||||||
match any_pending {
|
|
||||||
true => Poll::Pending,
|
|
||||||
false => Poll::Ready(()),
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,13 @@ impl AtomRoot {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::trace!("no atoms found for {:?}", ptr);
|
log::trace!("no atoms found for {:?}", ptr);
|
||||||
|
atoms.insert(
|
||||||
|
ptr,
|
||||||
|
Slot {
|
||||||
|
value: Rc::new(value),
|
||||||
|
subscribers: HashSet::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ dioxus-core = { path = "../core", version = "^0.2.1" }
|
|||||||
dioxus-html = { path = "../html", version = "^0.2.1" }
|
dioxus-html = { path = "../html", version = "^0.2.1" }
|
||||||
dioxus-core-macro = { path = "../core-macro", 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"
|
smallvec = "1.6"
|
||||||
fxhash = "0.2"
|
fxhash = "0.2"
|
||||||
anymap = "0.12.1"
|
anymap = "0.12.1"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
- [ ] pub display: Display,
|
- [ ] 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,
|
- [ ] pub direction: Direction,
|
||||||
|
|
||||||
- [x] pub flex_direction: FlexDirection,
|
- [x] pub flex_direction: FlexDirection,
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
- [x] pub flex_shrink: f32,
|
- [x] pub flex_shrink: f32,
|
||||||
- [x] pub flex_basis: Dimension,
|
- [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_items: AlignItems,
|
||||||
- [x] pub align_self: AlignSelf,
|
- [x] pub align_self: AlignSelf,
|
||||||
@@ -29,7 +29,10 @@
|
|||||||
- [ ] pub aspect_ratio: Number,
|
- [ ] pub aspect_ratio: Number,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use stretch2::{prelude::*, style::PositionType};
|
use taffy::{
|
||||||
|
prelude::*,
|
||||||
|
style::{FlexDirection, PositionType},
|
||||||
|
};
|
||||||
|
|
||||||
/// applies the entire html namespace defined in dioxus-html
|
/// applies the entire html namespace defined in dioxus-html
|
||||||
pub fn apply_layout_attributes(name: &str, value: &str, style: &mut Style) {
|
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" => {}
|
"counter-reset" => {}
|
||||||
|
|
||||||
"cursor" => {}
|
"cursor" => {}
|
||||||
"direction" => {
|
"direction" => {}
|
||||||
match value {
|
|
||||||
"ltr" => style.direction = Direction::LTR,
|
|
||||||
"rtl" => style.direction = Direction::RTL,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"display" => apply_display(name, value, style),
|
"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) {
|
fn apply_overflow(_name: &str, _value: &str, _style: &mut Style) {
|
||||||
match name {
|
// todo: add overflow support to taffy
|
||||||
// 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_display(_name: &str, value: &str, style: &mut Style) {
|
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
|
// 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,
|
// "block" => Display::Block,
|
||||||
// "inline" => Display::Inline,
|
// "inline" => Display::Inline,
|
||||||
|
|||||||
@@ -18,20 +18,59 @@ use quote::{quote, ToTokens, TokenStreamExt};
|
|||||||
use syn::{
|
use syn::{
|
||||||
ext::IdentExt,
|
ext::IdentExt,
|
||||||
parse::{Parse, ParseBuffer, ParseStream},
|
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)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub struct Component {
|
pub struct Component {
|
||||||
pub name: syn::Path,
|
pub name: syn::Path,
|
||||||
|
pub prop_gen_args: Option<AngleBracketedGenericArguments>,
|
||||||
pub body: Vec<ComponentField>,
|
pub body: Vec<ComponentField>,
|
||||||
pub children: Vec<BodyNode>,
|
pub children: Vec<BodyNode>,
|
||||||
pub manual_props: Option<Expr>,
|
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 {
|
impl Parse for Component {
|
||||||
fn parse(stream: ParseStream) -> Result<Self> {
|
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;
|
let content: ParseBuffer;
|
||||||
|
|
||||||
@@ -65,6 +104,7 @@ impl Parse for Component {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name,
|
name,
|
||||||
|
prop_gen_args,
|
||||||
body,
|
body,
|
||||||
children,
|
children,
|
||||||
manual_props,
|
manual_props,
|
||||||
@@ -75,6 +115,7 @@ impl Parse for Component {
|
|||||||
impl ToTokens for Component {
|
impl ToTokens for Component {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
let name = &self.name;
|
let name = &self.name;
|
||||||
|
let prop_gen_args = &self.prop_gen_args;
|
||||||
|
|
||||||
let mut has_key = None;
|
let mut has_key = None;
|
||||||
|
|
||||||
@@ -102,7 +143,10 @@ impl ToTokens for Component {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
None => {
|
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 {
|
for field in &self.body {
|
||||||
match field.name.to_string().as_str() {
|
match field.name.to_string().as_str() {
|
||||||
"key" => {
|
"key" => {
|
||||||
|
|||||||
@@ -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"));
|
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
@@ -4,7 +4,7 @@ use proc_macro2::TokenStream as TokenStream2;
|
|||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
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()?));
|
return Ok(BodyNode::Text(stream.parse()?));
|
||||||
}
|
}
|
||||||
|
|
||||||
// div {} -> el
|
let body_stream = stream.fork();
|
||||||
// Div {} -> comp
|
if let Ok(path) = body_stream.parse::<syn::Path>() {
|
||||||
if stream.peek(syn::Ident) && stream.peek2(token::Brace) {
|
// this is an Element if path match of:
|
||||||
if stream
|
// - one ident
|
||||||
.fork()
|
// - followed by `{`
|
||||||
.parse::<Ident>()?
|
// - 1st char is lowercase
|
||||||
.to_string()
|
//
|
||||||
.chars()
|
// example:
|
||||||
.next()
|
// div {}
|
||||||
.unwrap()
|
if let Some(ident) = path.get_ident() {
|
||||||
.is_ascii_uppercase()
|
if body_stream.peek(token::Brace)
|
||||||
{
|
&& ident
|
||||||
return Ok(BodyNode::Component(stream.parse()?));
|
.to_string()
|
||||||
} else {
|
.chars()
|
||||||
return Ok(BodyNode::Element(stream.parse::<Element>()?));
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.is_ascii_lowercase()
|
||||||
|
{
|
||||||
|
return Ok(BodyNode::Element(stream.parse::<Element>()?));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// component() -> comp
|
// Otherwise this should be Component, allowed syntax:
|
||||||
// ::component {} -> comp
|
// - syn::Path
|
||||||
// ::component () -> comp
|
// - PathArguments can only apper in last segment
|
||||||
if (stream.peek(syn::Ident) && stream.peek2(token::Paren))
|
// - followed by `{` or `(`, note `(` cannot be used with one ident
|
||||||
|| (stream.peek(Token![::]))
|
//
|
||||||
|| (stream.peek(Token![:]) && stream.peek2(Token![:]))
|
// example
|
||||||
{
|
// Div {}
|
||||||
return Ok(BodyNode::Component(stream.parse::<Component>()?));
|
// 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
|
return Ok(BodyNode::Component(stream.parse()?));
|
||||||
// crate::component() -> comp
|
|
||||||
if let Ok(pat) = stream.fork().parse::<syn::Path>() {
|
|
||||||
if pat.segments.len() > 1 {
|
|
||||||
return Ok(BodyNode::Component(stream.parse::<Component>()?));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ crossterm = "0.23.0"
|
|||||||
anyhow = "1.0.42"
|
anyhow = "1.0.42"
|
||||||
tokio = { version = "1.15.0", features = ["full"] }
|
tokio = { version = "1.15.0", features = ["full"] }
|
||||||
futures = "0.3.19"
|
futures = "0.3.19"
|
||||||
stretch2 = "0.4.2"
|
taffy = "0.1.0"
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
fxhash = "0.2"
|
fxhash = "0.2"
|
||||||
anymap = "0.12.1"
|
anymap = "0.12.1"
|
||||||
@@ -17,8 +17,8 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use stretch2::geometry::{Point, Size};
|
use taffy::geometry::{Point, Size};
|
||||||
use stretch2::{prelude::Layout, Stretch};
|
use taffy::{prelude::Layout, Taffy};
|
||||||
|
|
||||||
use crate::{Dom, Node};
|
use crate::{Dom, Node};
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ impl InnerInputState {
|
|||||||
&mut self,
|
&mut self,
|
||||||
evts: &mut [EventCore],
|
evts: &mut [EventCore],
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<UserEvent>,
|
||||||
layout: &Stretch,
|
layout: &Taffy,
|
||||||
dom: &mut Dom,
|
dom: &mut Dom,
|
||||||
) {
|
) {
|
||||||
let previous_mouse = self.mouse.clone();
|
let previous_mouse = self.mouse.clone();
|
||||||
@@ -175,7 +175,7 @@ impl InnerInputState {
|
|||||||
&self,
|
&self,
|
||||||
previous_mouse: Option<MouseData>,
|
previous_mouse: Option<MouseData>,
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<UserEvent>,
|
||||||
layout: &Stretch,
|
layout: &Taffy,
|
||||||
dom: &mut Dom,
|
dom: &mut Dom,
|
||||||
) {
|
) {
|
||||||
fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool {
|
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();
|
let mut resolved_events = Vec::new();
|
||||||
|
|
||||||
(*self.state).borrow_mut().update(
|
(*self.state).borrow_mut().update(
|
||||||
|
|||||||
+11
-12
@@ -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::node_ref::{AttributeMask, NodeMask, NodeView};
|
||||||
use dioxus_native_core::state::ChildDepState;
|
use dioxus_native_core::state::ChildDepState;
|
||||||
use dioxus_native_core_macro::sorted_str_slice;
|
use dioxus_native_core_macro::sorted_str_slice;
|
||||||
use stretch2::prelude::*;
|
use taffy::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub(crate) enum PossiblyUninitalized<T> {
|
pub(crate) enum PossiblyUninitalized<T> {
|
||||||
@@ -28,13 +28,13 @@ impl<T> Default for PossiblyUninitalized<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Default, Debug)]
|
#[derive(Clone, PartialEq, Default, Debug)]
|
||||||
pub(crate) struct StretchLayout {
|
pub(crate) struct TaffyLayout {
|
||||||
pub style: Style,
|
pub style: Style,
|
||||||
pub node: PossiblyUninitalized<Node>,
|
pub node: PossiblyUninitalized<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChildDepState for StretchLayout {
|
impl ChildDepState for TaffyLayout {
|
||||||
type Ctx = Rc<RefCell<Stretch>>;
|
type Ctx = Rc<RefCell<Taffy>>;
|
||||||
type DepState = Self;
|
type DepState = Self;
|
||||||
// use tag to force this to be called when a node is built
|
// use tag to force this to be called when a node is built
|
||||||
const NODE_MASK: NodeMask =
|
const NODE_MASK: NodeMask =
|
||||||
@@ -53,7 +53,7 @@ impl ChildDepState for StretchLayout {
|
|||||||
Self::DepState: 'a,
|
Self::DepState: 'a,
|
||||||
{
|
{
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
let mut stretch = ctx.borrow_mut();
|
let mut taffy = ctx.borrow_mut();
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
if let Some(text) = node.text() {
|
if let Some(text) = node.text() {
|
||||||
let char_len = text.chars().count();
|
let char_len = text.chars().count();
|
||||||
@@ -70,11 +70,10 @@ impl ChildDepState for StretchLayout {
|
|||||||
};
|
};
|
||||||
if let PossiblyUninitalized::Initialized(n) = self.node {
|
if let PossiblyUninitalized::Initialized(n) = self.node {
|
||||||
if self.style != style {
|
if self.style != style {
|
||||||
stretch.set_style(n, style).unwrap();
|
taffy.set_style(n, style).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.node =
|
self.node = PossiblyUninitalized::Initialized(taffy.new_node(style, &[]).unwrap());
|
||||||
PossiblyUninitalized::Initialized(stretch.new_node(style, &[]).unwrap());
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -100,14 +99,14 @@ impl ChildDepState for StretchLayout {
|
|||||||
|
|
||||||
if let PossiblyUninitalized::Initialized(n) = self.node {
|
if let PossiblyUninitalized::Initialized(n) = self.node {
|
||||||
if self.style != style {
|
if self.style != style {
|
||||||
stretch.set_style(n, style).unwrap();
|
taffy.set_style(n, style).unwrap();
|
||||||
}
|
}
|
||||||
if stretch.children(n).unwrap() != child_layout {
|
if taffy.children(n).unwrap() != child_layout {
|
||||||
stretch.set_children(n, &child_layout).unwrap();
|
taffy.set_children(n, &child_layout).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.node = PossiblyUninitalized::Initialized(
|
self.node = PossiblyUninitalized::Initialized(
|
||||||
stretch.new_node(style, &child_layout).unwrap(),
|
taffy.new_node(style, &child_layout).unwrap(),
|
||||||
);
|
);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-17
@@ -13,12 +13,12 @@ use futures::{
|
|||||||
channel::mpsc::{UnboundedReceiver, UnboundedSender},
|
channel::mpsc::{UnboundedReceiver, UnboundedSender},
|
||||||
pin_mut, StreamExt,
|
pin_mut, StreamExt,
|
||||||
};
|
};
|
||||||
use layout::StretchLayout;
|
use layout::TaffyLayout;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{io, time::Duration};
|
use std::{io, time::Duration};
|
||||||
use stretch2::{prelude::Size, Stretch};
|
|
||||||
use style_attributes::StyleModifier;
|
use style_attributes::StyleModifier;
|
||||||
|
use taffy::{geometry::Point, prelude::Size, Taffy};
|
||||||
use tui::{backend::CrosstermBackend, layout::Rect, Terminal};
|
use tui::{backend::CrosstermBackend, layout::Rect, Terminal};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
@@ -37,8 +37,8 @@ type Node = dioxus_native_core::real_dom::Node<NodeState>;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, State, Default)]
|
#[derive(Debug, Clone, State, Default)]
|
||||||
struct NodeState {
|
struct NodeState {
|
||||||
#[child_dep_state(layout, RefCell<Stretch>)]
|
#[child_dep_state(layout, RefCell<Taffy>)]
|
||||||
layout: StretchLayout,
|
layout: TaffyLayout,
|
||||||
// depends on attributes, the C component of it's parent and a u8 context
|
// depends on attributes, the C component of it's parent and a u8 context
|
||||||
#[parent_dep_state(style)]
|
#[parent_dep_state(style)]
|
||||||
style: StyleModifier,
|
style: StyleModifier,
|
||||||
@@ -88,9 +88,9 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
|||||||
let mut rdom: Dom = RealDom::new();
|
let mut rdom: Dom = RealDom::new();
|
||||||
let mutations = dom.rebuild();
|
let mutations = dom.rebuild();
|
||||||
let to_update = rdom.apply_mutations(vec![mutations]);
|
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();
|
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();
|
let _to_rerender = rdom.update_state(&dom, to_update, any_map).unwrap();
|
||||||
|
|
||||||
render_vdom(
|
render_vdom(
|
||||||
@@ -99,7 +99,7 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
|||||||
handler,
|
handler,
|
||||||
cfg,
|
cfg,
|
||||||
rdom,
|
rdom,
|
||||||
stretch,
|
taffy,
|
||||||
register_event,
|
register_event,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -111,7 +111,7 @@ fn render_vdom(
|
|||||||
handler: RinkInputHandler,
|
handler: RinkInputHandler,
|
||||||
cfg: Config,
|
cfg: Config,
|
||||||
mut rdom: Dom,
|
mut rdom: Dom,
|
||||||
stretch: Rc<RefCell<Stretch>>,
|
taffy: Rc<RefCell<Taffy>>,
|
||||||
mut register_event: impl FnMut(crossterm::event::Event),
|
mut register_event: impl FnMut(crossterm::event::Event),
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
tokio::runtime::Builder::new_current_thread()
|
tokio::runtime::Builder::new_current_thread()
|
||||||
@@ -146,17 +146,17 @@ fn render_vdom(
|
|||||||
|
|
||||||
if !to_rerender.is_empty() || resized {
|
if !to_rerender.is_empty() || resized {
|
||||||
resized = false;
|
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 width = dims.width;
|
||||||
let height = dims.height;
|
let height = dims.height;
|
||||||
let root_node = rdom[0].state.layout.node.unwrap();
|
let root_node = rdom[0].state.layout.node.unwrap();
|
||||||
|
|
||||||
stretch
|
taffy
|
||||||
.compute_layout(
|
.compute_layout(
|
||||||
root_node,
|
root_node,
|
||||||
Size {
|
Size {
|
||||||
width: stretch2::prelude::Number::Defined((width - 1) as f32),
|
width: taffy::prelude::Number::Defined((width - 1) as f32),
|
||||||
height: stretch2::prelude::Number::Defined((height - 1) as f32),
|
height: taffy::prelude::Number::Defined((height - 1) as f32),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -164,9 +164,16 @@ fn render_vdom(
|
|||||||
if let Some(terminal) = &mut terminal {
|
if let Some(terminal) = &mut terminal {
|
||||||
terminal.draw(|frame| {
|
terminal.draw(|frame| {
|
||||||
// size is guaranteed to not change when rendering
|
// 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];
|
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 {
|
} else {
|
||||||
resize(
|
resize(
|
||||||
@@ -176,7 +183,7 @@ fn render_vdom(
|
|||||||
width: 300,
|
width: 300,
|
||||||
height: 300,
|
height: 300,
|
||||||
},
|
},
|
||||||
&mut stretch.borrow_mut(),
|
&mut taffy.borrow_mut(),
|
||||||
&rdom,
|
&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 {
|
for e in evts {
|
||||||
vdom.handle_message(SchedulerMsg::Event(e));
|
vdom.handle_message(SchedulerMsg::Event(e));
|
||||||
}
|
}
|
||||||
@@ -225,7 +232,7 @@ fn render_vdom(
|
|||||||
let to_update = rdom.apply_mutations(mutations);
|
let to_update = rdom.apply_mutations(mutations);
|
||||||
// update the style and layout
|
// update the style and layout
|
||||||
let mut any_map = AnyMap::new();
|
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();
|
to_rerender = rdom.update_state(vdom, to_update, any_map).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use dioxus_native_core::layout_attributes::UnitSystem;
|
use dioxus_native_core::layout_attributes::UnitSystem;
|
||||||
use std::io::Stdout;
|
use std::io::Stdout;
|
||||||
use stretch2::{
|
use taffy::{
|
||||||
geometry::Point,
|
geometry::Point,
|
||||||
prelude::{Layout, Size},
|
prelude::{Layout, Size},
|
||||||
Stretch,
|
Taffy,
|
||||||
};
|
};
|
||||||
use tui::{backend::CrosstermBackend, layout::Rect};
|
use tui::{backend::CrosstermBackend, layout::Rect};
|
||||||
|
|
||||||
@@ -18,10 +18,11 @@ const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
|||||||
|
|
||||||
pub(crate) fn render_vnode(
|
pub(crate) fn render_vnode(
|
||||||
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
|
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
|
||||||
layout: &Stretch,
|
layout: &Taffy,
|
||||||
rdom: &Dom,
|
rdom: &Dom,
|
||||||
node: &Node,
|
node: &Node,
|
||||||
cfg: Config,
|
cfg: Config,
|
||||||
|
parent_location: Point<f32>,
|
||||||
) {
|
) {
|
||||||
use dioxus_native_core::real_dom::NodeType;
|
use dioxus_native_core::real_dom::NodeType;
|
||||||
|
|
||||||
@@ -29,7 +30,11 @@ pub(crate) fn render_vnode(
|
|||||||
return;
|
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 Point { x, y } = location;
|
||||||
let Size { width, height } = size;
|
let Size { width, height } = size;
|
||||||
@@ -57,7 +62,7 @@ pub(crate) fn render_vnode(
|
|||||||
text,
|
text,
|
||||||
style: node.state.style.core,
|
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
|
// 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 {
|
if area.width > 0 && area.height > 0 {
|
||||||
@@ -65,7 +70,7 @@ pub(crate) fn render_vnode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeType::Element { children, .. } => {
|
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
|
// 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 {
|
if area.width > 0 && area.height > 0 {
|
||||||
@@ -73,7 +78,7 @@ pub(crate) fn render_vnode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for c in children {
|
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!(),
|
NodeType::Placeholder => unreachable!(),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
- [ ] pub display: Display,
|
- [ ] 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,
|
- [ ] pub direction: Direction,
|
||||||
|
|
||||||
- [x] pub flex_direction: FlexDirection,
|
- [x] pub flex_direction: FlexDirection,
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
- [x] pub flex_shrink: f32,
|
- [x] pub flex_shrink: f32,
|
||||||
- [x] pub flex_basis: Dimension,
|
- [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_items: AlignItems,
|
||||||
- [x] pub align_self: AlignSelf,
|
- [x] pub align_self: AlignSelf,
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
use stretch2 as stretch;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn margin_and_flex_row() {
|
fn margin_and_flex_row() {
|
||||||
let mut stretch = stretch::Stretch::new();
|
let mut taffy = taffy::Taffy::new();
|
||||||
let node0 = stretch
|
let node0 = taffy
|
||||||
.new_node(
|
.new_node(
|
||||||
stretch::style::Style {
|
taffy::style::Style {
|
||||||
flex_grow: 1f32,
|
flex_grow: 1f32,
|
||||||
margin: stretch::geometry::Rect {
|
margin: taffy::geometry::Rect {
|
||||||
start: stretch::style::Dimension::Points(10f32),
|
start: taffy::style::Dimension::Points(10f32),
|
||||||
end: stretch::style::Dimension::Points(10f32),
|
end: taffy::style::Dimension::Points(10f32),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -17,50 +15,50 @@ fn margin_and_flex_row() {
|
|||||||
&[],
|
&[],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let node = stretch
|
let node = taffy
|
||||||
.new_node(
|
.new_node(
|
||||||
stretch::style::Style {
|
taffy::style::Style {
|
||||||
size: stretch::geometry::Size {
|
size: taffy::geometry::Size {
|
||||||
width: stretch::style::Dimension::Points(100f32),
|
width: taffy::style::Dimension::Points(100f32),
|
||||||
height: stretch::style::Dimension::Points(100f32),
|
height: taffy::style::Dimension::Points(100f32),
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
&[node0],
|
&[node0],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
stretch
|
taffy
|
||||||
.compute_layout(node, stretch::geometry::Size::undefined())
|
.compute_layout(node, taffy::geometry::Size::undefined())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(stretch.layout(node).unwrap().size.width, 100f32);
|
assert_eq!(taffy.layout(node).unwrap().size.width, 100f32);
|
||||||
assert_eq!(stretch.layout(node).unwrap().size.height, 100f32);
|
assert_eq!(taffy.layout(node).unwrap().size.height, 100f32);
|
||||||
assert_eq!(stretch.layout(node).unwrap().location.x, 0f32);
|
assert_eq!(taffy.layout(node).unwrap().location.x, 0f32);
|
||||||
assert_eq!(stretch.layout(node).unwrap().location.y, 0f32);
|
assert_eq!(taffy.layout(node).unwrap().location.y, 0f32);
|
||||||
assert_eq!(stretch.layout(node0).unwrap().size.width, 80f32);
|
assert_eq!(taffy.layout(node0).unwrap().size.width, 80f32);
|
||||||
assert_eq!(stretch.layout(node0).unwrap().size.height, 100f32);
|
assert_eq!(taffy.layout(node0).unwrap().size.height, 100f32);
|
||||||
assert_eq!(stretch.layout(node0).unwrap().location.x, 10f32);
|
assert_eq!(taffy.layout(node0).unwrap().location.x, 10f32);
|
||||||
assert_eq!(stretch.layout(node0).unwrap().location.y, 0f32);
|
assert_eq!(taffy.layout(node0).unwrap().location.y, 0f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn margin_and_flex_row2() {
|
fn margin_and_flex_row2() {
|
||||||
let mut stretch = stretch::Stretch::new();
|
let mut taffy = taffy::Taffy::new();
|
||||||
let node0 = stretch
|
let node0 = taffy
|
||||||
.new_node(
|
.new_node(
|
||||||
stretch::style::Style {
|
taffy::style::Style {
|
||||||
flex_grow: 1f32,
|
flex_grow: 1f32,
|
||||||
margin: stretch::geometry::Rect {
|
margin: taffy::geometry::Rect {
|
||||||
// left
|
// left
|
||||||
start: stretch::style::Dimension::Points(10f32),
|
start: taffy::style::Dimension::Points(10f32),
|
||||||
|
|
||||||
// right?
|
// right?
|
||||||
end: stretch::style::Dimension::Points(10f32),
|
end: taffy::style::Dimension::Points(10f32),
|
||||||
|
|
||||||
// top?
|
// top?
|
||||||
// top: stretch::style::Dimension::Points(10f32),
|
// top: taffy::style::Dimension::Points(10f32),
|
||||||
|
|
||||||
// bottom?
|
// bottom?
|
||||||
// bottom: stretch::style::Dimension::Points(10f32),
|
// bottom: taffy::style::Dimension::Points(10f32),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -69,12 +67,12 @@ fn margin_and_flex_row2() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let node = stretch
|
let node = taffy
|
||||||
.new_node(
|
.new_node(
|
||||||
stretch::style::Style {
|
taffy::style::Style {
|
||||||
size: stretch::geometry::Size {
|
size: taffy::geometry::Size {
|
||||||
width: stretch::style::Dimension::Points(100f32),
|
width: taffy::style::Dimension::Points(100f32),
|
||||||
height: stretch::style::Dimension::Points(100f32),
|
height: taffy::style::Dimension::Points(100f32),
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -82,12 +80,12 @@ fn margin_and_flex_row2() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
stretch
|
taffy
|
||||||
.compute_layout(node, stretch::geometry::Size::undefined())
|
.compute_layout(node, taffy::geometry::Size::undefined())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(stretch.layout(node).unwrap().size.width, 100f32);
|
assert_eq!(taffy.layout(node).unwrap().size.width, 100f32);
|
||||||
assert_eq!(stretch.layout(node).unwrap().size.height, 100f32);
|
assert_eq!(taffy.layout(node).unwrap().size.height, 100f32);
|
||||||
assert_eq!(stretch.layout(node).unwrap().location.x, 0f32);
|
assert_eq!(taffy.layout(node).unwrap().location.x, 0f32);
|
||||||
assert_eq!(stretch.layout(node).unwrap().location.y, 0f32);
|
assert_eq!(taffy.layout(node).unwrap().location.y, 0f32);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user