examples: upgrade to new version of dioxus core.
also add the inline_props macro
This commit is contained in:
1 parent
21e00c114e
commit
cda759c659
41 files changed
+556
-253
No files matched your search
+1
-1
@@ -17,7 +17,7 @@ pub static App: Component<()> = |cx| {
|
|||||||
|
|
||||||
let (async_count, dir) = (count.for_async(), *direction);
|
let (async_count, dir) = (count.for_async(), *direction);
|
||||||
|
|
||||||
let (task, _) = use_coroutine(cx, move || async move {
|
let task = use_coroutine(&cx, move || async move {
|
||||||
loop {
|
loop {
|
||||||
TimeoutFuture::new(250).await;
|
TimeoutFuture::new(250).await;
|
||||||
*async_count.get_mut() += dir;
|
*async_count.get_mut() += dir;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ struct C1Props<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn Child1<'a>(cx: Scope<'a, C1Props<'a>>) -> Element {
|
fn Child1<'a>(cx: Scope<'a, C1Props<'a>>) -> Element {
|
||||||
let (left, right) = props.text.split_once("=").unwrap();
|
let (left, right) = cx.props.text.split_once("=").unwrap();
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
@@ -58,7 +58,7 @@ struct C2Props<'a> {
|
|||||||
fn Child2<'a>(cx: Scope<'a, C2Props<'a>>) -> Element {
|
fn Child2<'a>(cx: Scope<'a, C2Props<'a>>) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
Child3 {
|
Child3 {
|
||||||
text: props.text
|
text: cx.props.text
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,6 @@ fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element {
|
|||||||
rsx!(cx, button {
|
rsx!(cx, button {
|
||||||
class: "calculator-key {cx.props.name}"
|
class: "calculator-key {cx.props.name}"
|
||||||
onclick: {cx.props.onclick}
|
onclick: {cx.props.onclick}
|
||||||
{&props.children}
|
{&cx.props.children}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -58,15 +58,15 @@ static App: Component<()> = |cx| {
|
|||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
struct HorseyProps<'a> {
|
struct HorseyProps<'a> {
|
||||||
pos: i32,
|
pos: i32,
|
||||||
children: ScopeChildren<'a>,
|
children: Element<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Horsey<'a>((cx, props): ScopeState<'a, HorseyProps<'a>>) -> Element {
|
fn Horsey<'a>(cx: Scope<'a, HorseyProps<'a>>) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
button { "pause" }
|
button { "pause" }
|
||||||
div {
|
div {
|
||||||
{&props.children}
|
{&cx.props.children}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
simple_logger::init_with_level(log::Level::Debug);
|
// simple_logger::init_with_level(log::Level::Debug);
|
||||||
dioxus::desktop::launch_cfg(App, |c| {
|
dioxus::desktop::launch_cfg(App, |c| {
|
||||||
c.with_window(|w| {
|
c.with_window(|w| {
|
||||||
w.with_resizable(true).with_inner_size(
|
w.with_resizable(true).with_inner_size(
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ struct ActionButtonProps<'a> {
|
|||||||
|
|
||||||
fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
|
fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
|
||||||
rsx!(cx, div { class: "col-sm-6 smallpad"
|
rsx!(cx, div { class: "col-sm-6 smallpad"
|
||||||
button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}", onclick: move |_| (props.onclick)(),
|
button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}", onclick: move |_| (cx.props.onclick)(),
|
||||||
"{cx.props.name}"
|
"{cx.props.name}"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::desktop::launch(App);
|
dioxus::desktop::launch(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn App((cx, props): ScopeState<()>) -> Element {
|
fn app(cx: Scope<()>) -> Element {
|
||||||
cx.render(rsx! (
|
cx.render(rsx! (
|
||||||
div { "Hello, world!" }
|
div { "Hello, world!" }
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ use dioxus::ssr;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let vdom = VirtualDom::new(App);
|
let vdom = VirtualDom::new(App);
|
||||||
let content = ssr::render_vdom(&vdom, |f| f.pre_render(true));
|
let content = ssr::render_vdom_cfg(&vdom, |f| f.pre_render(true));
|
||||||
|
|
||||||
dioxus::desktop::launch(App, |c| c.with_prerendered(content));
|
dioxus::desktop::launch_cfg(App, |c| c.with_prerendered(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
static App: Component<()> = |cx| {
|
static App: Component<()> = |cx| {
|
||||||
|
|||||||
@@ -33,11 +33,7 @@ fn main() {
|
|||||||
AppendChildren { many: 1 },
|
AppendChildren { many: 1 },
|
||||||
];
|
];
|
||||||
|
|
||||||
dioxus_desktop::run(APP, (), |c| c.with_edits(edits));
|
let app: Component<()> = |cx| rsx!(cx, div { "some app" });
|
||||||
}
|
|
||||||
|
|
||||||
const APP: Component<()> = |(cx, _props)| {
|
dioxus_desktop::run(app, (), |c| c.with_edits(edits));
|
||||||
rsx!(cx, div {
|
}
|
||||||
"some app"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
@@ -15,6 +15,8 @@
|
|||||||
//! the RefCell will panic and crash. You can use `try_get_mut` or `.modify` to avoid this problem, or just not hold two
|
//! the RefCell will panic and crash. You can use `try_get_mut` or `.modify` to avoid this problem, or just not hold two
|
||||||
//! RefMuts at the same time.
|
//! RefMuts at the same time.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use dioxus::desktop::wry::application::dpi::LogicalSize;
|
use dioxus::desktop::wry::application::dpi::LogicalSize;
|
||||||
use dioxus::events::*;
|
use dioxus::events::*;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
@@ -73,16 +75,16 @@ static App: Component<()> = |cx| {
|
|||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
struct CalculatorKeyProps<'a> {
|
struct CalculatorKeyProps<'a> {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
onclick: &'a dyn Fn(MouseEvent),
|
onclick: &'a dyn Fn(Arc<MouseEvent>),
|
||||||
children: ScopeChildren<'a>,
|
children: Element<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn CalculatorKey<'a>((cx, props): ScopeState<'a, CalculatorKeyProps<'a>>) -> Element<'a> {
|
fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
button {
|
button {
|
||||||
class: "calculator-key {cx.props.name}"
|
class: "calculator-key {cx.props.name}"
|
||||||
onclick: {cx.props.onclick}
|
onclick: move |e| (cx.props.onclick)(e)
|
||||||
{&props.children}
|
{&cx.props.children}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -168,7 +170,7 @@ impl Calculator {
|
|||||||
self.cur_val = self.display_value.parse::<f64>().unwrap();
|
self.cur_val = self.display_value.parse::<f64>().unwrap();
|
||||||
self.waiting_for_operand = true;
|
self.waiting_for_operand = true;
|
||||||
}
|
}
|
||||||
fn handle_keydown(&mut self, evt: KeyboardEvent) {
|
fn handle_keydown(&mut self, evt: Arc<KeyboardEvent>) {
|
||||||
match evt.key_code {
|
match evt.key_code {
|
||||||
KeyCode::Backspace => self.backspace(),
|
KeyCode::Backspace => self.backspace(),
|
||||||
KeyCode::Num0 => self.input_digit(0),
|
KeyCode::Num0 => self.input_digit(0),
|
||||||
|
|||||||
+2
-24
@@ -1,5 +1,5 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus::router::Router;
|
use dioxus_router::{use_router, Link};
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum Route {
|
pub enum Route {
|
||||||
@@ -24,7 +24,7 @@ pub enum Route {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static App: Component<()> = |cx| {
|
static App: Component<()> = |cx| {
|
||||||
let route = use_router(cx, Route::parse)?;
|
let route = use_router(&cx, Route::parse);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
nav {
|
nav {
|
||||||
@@ -61,26 +61,4 @@ impl Route {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Routable for Route {
|
|
||||||
fn from_path(path: &str, params: &std::collections::HashMap<&str, &str>) -> Option<Self> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_path(&self) -> String {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn routes() -> Vec<&'static str> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn not_found_route() -> Option<Self> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn recognize(pathname: &str) -> Option<Self> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
//! - Allow top-level fragments
|
//! - Allow top-level fragments
|
||||||
//!
|
//!
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::desktop::launch(Example);
|
dioxus::desktop::launch(EXAMPLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When trying to return "nothing" to Dioxus, you'll need to specify the type parameter or Rust will be sad.
|
/// When trying to return "nothing" to Dioxus, you'll need to specify the type parameter or Rust will be sad.
|
||||||
@@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None;
|
|||||||
use baller::Baller;
|
use baller::Baller;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
pub static Example: Component<()> = |cx| {
|
pub static EXAMPLE: Component<()> = |cx| {
|
||||||
let formatting = "formatting!";
|
let formatting = "formatting!";
|
||||||
let formatting_tuple = ("a", "b");
|
let formatting_tuple = ("a", "b");
|
||||||
let lazy_fmt = format_args!("lazily formatted text");
|
let lazy_fmt = format_args!("lazily formatted text");
|
||||||
@@ -173,12 +173,12 @@ pub static Example: Component<()> = |cx| {
|
|||||||
Taller { a: "asd", div {"hello world!"} }
|
Taller { a: "asd", div {"hello world!"} }
|
||||||
|
|
||||||
// helper functions
|
// helper functions
|
||||||
{helper(cx, "hello world!")}
|
{helper(&cx, "hello world!")}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
fn helper(cx: Scope, text: &str) -> Element {
|
fn helper<'a>(cx: &'a ScopeState, text: &str) -> Element<'a> {
|
||||||
rsx!(cx, p { "{text}" })
|
rsx!(cx, p { "{text}" })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ mod baller {
|
|||||||
pub struct BallerProps {}
|
pub struct BallerProps {}
|
||||||
|
|
||||||
/// This component totally balls
|
/// This component totally balls
|
||||||
pub fn Baller(_: ScopeState<BallerProps>) -> Element {
|
pub fn Baller(_: Scope<BallerProps>) -> Element {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,13 +196,11 @@ mod baller {
|
|||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
pub struct TallerProps<'a> {
|
pub struct TallerProps<'a> {
|
||||||
a: &'static str,
|
a: &'static str,
|
||||||
|
children: Element<'a>,
|
||||||
#[builder(default)]
|
|
||||||
children: ScopeChildren<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This component is taller than most :)
|
/// This component is taller than most :)
|
||||||
pub fn Taller<'a>(_: ScopeState<'a, TallerProps<'a>>) -> Element {
|
pub fn Taller<'a>(_: Scope<'a, TallerProps<'a>>) -> Element {
|
||||||
let b = true;
|
let b = true;
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
+3
-12
@@ -1,15 +1,13 @@
|
|||||||
#![allow(non_upper_case_globals)]
|
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus::ssr;
|
use dioxus::ssr;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut vdom = VirtualDom::new(App);
|
let mut vdom = VirtualDom::new(APP);
|
||||||
vdom.rebuild_in_place().expect("Rebuilding failed");
|
let _ = vdom.rebuild();
|
||||||
println!("{}", ssr::render_vdom(&vdom));
|
println!("{}", ssr::render_vdom(&vdom));
|
||||||
}
|
}
|
||||||
|
|
||||||
static App: Component<()> = |cx| {
|
static APP: Component<()> = |cx| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
div {
|
div {
|
||||||
h1 { "Title" }
|
h1 { "Title" }
|
||||||
@@ -17,10 +15,3 @@ static App: Component<()> = |cx| {
|
|||||||
}
|
}
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MyProps<'a> {
|
|
||||||
text: &'a str,
|
|
||||||
}
|
|
||||||
fn App2<'a>(cx: Scope<'a, MyProps<'a>>) -> Element {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ use dioxus::prelude::*;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
use dioxus::desktop::wry::application::platform::macos::*;
|
use dioxus::desktop::wry::application::platform::macos::*;
|
||||||
dioxus::desktop::launch(App, |c| {
|
dioxus::desktop::launch_cfg(App, |c| {
|
||||||
c.with_window(|w| {
|
c.with_window(|w| {
|
||||||
w.with_fullsize_content_view(true)
|
w.with_fullsize_content_view(true)
|
||||||
.with_titlebar_buttons_hidden(false)
|
.with_titlebar_buttons_hidden(false)
|
||||||
|
|||||||
+8
-13
@@ -6,29 +6,24 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::desktop::launch(App);
|
dioxus::desktop::launch(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
static App: Component<()> = |cx| {
|
fn app(cx: Scope<()>) -> Element {
|
||||||
let mut count = use_state(&cx, || 0);
|
let mut count = use_state(&cx, || 0);
|
||||||
|
|
||||||
cx.push_task(async move {
|
cx.push_task(|| async move {
|
||||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
println!("setting count");
|
|
||||||
count += 1;
|
count += 1;
|
||||||
// count.set(10);
|
|
||||||
// *count += 1;
|
|
||||||
// let c = count.get() + 1;
|
|
||||||
// count.set(c);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
h1 { "High-Five counter: {count}" }
|
h1 { "High-Five counter: {count}" }
|
||||||
// button {
|
button {
|
||||||
// onclick: move |_| count +=1 ,
|
onclick: move |_| count +=1 ,
|
||||||
// "Click me!"
|
"Click me!"
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
}
|
||||||
+6
-6
@@ -23,9 +23,9 @@ pub struct TodoItem {
|
|||||||
|
|
||||||
const STYLE: &str = include_str!("./assets/todomvc.css");
|
const STYLE: &str = include_str!("./assets/todomvc.css");
|
||||||
const App: Component<()> = |cx| {
|
const App: Component<()> = |cx| {
|
||||||
let mut draft = use_state(&cx, || "".to_string());
|
let draft = use_state(&cx, || "".to_string());
|
||||||
let mut todos = use_state(&cx, || HashMap::<u32, Rc<TodoItem>>::new());
|
let todos = use_state(&cx, || HashMap::<u32, Rc<TodoItem>>::new());
|
||||||
let mut filter = use_state(&cx, || FilterState::All);
|
let filter = use_state(&cx, || FilterState::All);
|
||||||
|
|
||||||
let todolist = todos
|
let todolist = todos
|
||||||
.iter()
|
.iter()
|
||||||
@@ -86,9 +86,9 @@ pub struct TodoEntryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn TodoEntry(cx: Scope<TodoEntryProps>) -> Element {
|
pub fn TodoEntry(cx: Scope<TodoEntryProps>) -> Element {
|
||||||
let mut is_editing = use_state(&cx, || false);
|
let is_editing = use_state(&cx, || false);
|
||||||
let mut contents = use_state(&cx, || String::from(""));
|
let contents = use_state(&cx, || String::from(""));
|
||||||
let todo = &props.todo;
|
let todo = &cx.props.todo;
|
||||||
|
|
||||||
rsx!(cx, li {
|
rsx!(cx, li {
|
||||||
"{todo.id}"
|
"{todo.id}"
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ struct RowProps {
|
|||||||
row_id: usize,
|
row_id: usize,
|
||||||
label: Label,
|
label: Label,
|
||||||
}
|
}
|
||||||
fn Row((cx, props): ScopeState<RowProps>) -> Element {
|
fn Row(cx: Scope<RowProps>) -> Element {
|
||||||
let [adj, col, noun] = props.label.0;
|
let [adj, col, noun] = cx.props.label.0;
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
tr {
|
tr {
|
||||||
td { class:"col-md-1", "{cx.props.row_id}" }
|
td { class:"col-md-1", "{cx.props.row_id}" }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "dioxus-core-macro"
|
name = "dioxus-core-macro"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
authors = ["Jonathan Kelley"]
|
authors = ["Jonathan Kelley"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
description = "Core macro for Dioxus Virtual DOM"
|
description = "Core macro for Dioxus Virtual DOM"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
repository = "https://github.com/DioxusLabs/dioxus/"
|
repository = "https://github.com/DioxusLabs/dioxus/"
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
use dioxus_core_macro::{inline_props, Props};
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
type Element<'a> = ();
|
||||||
|
|
||||||
|
pub struct Scope<'a, T> {
|
||||||
|
props: &'a T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline_props]
|
||||||
|
pub fn component(
|
||||||
|
cx: Scope,
|
||||||
|
chkk: String,
|
||||||
|
chkk2: String,
|
||||||
|
r: u32,
|
||||||
|
cat: &'a str,
|
||||||
|
drd: String,
|
||||||
|
e: String,
|
||||||
|
) -> Element {
|
||||||
|
let r = chkk.len();
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
|
use syn::{
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
punctuated::Punctuated,
|
||||||
|
token, Block, FnArg, Generics, Ident, Result, ReturnType, Token, Visibility,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct InlinePropsBody {
|
||||||
|
pub vis: syn::Visibility,
|
||||||
|
pub fn_token: Token![fn],
|
||||||
|
pub ident: Ident,
|
||||||
|
pub cx_token: Ident,
|
||||||
|
pub generics: Generics,
|
||||||
|
pub paren_token: token::Paren,
|
||||||
|
pub inputs: Punctuated<FnArg, Token![,]>,
|
||||||
|
// pub fields: FieldsNamed,
|
||||||
|
pub output: ReturnType,
|
||||||
|
pub block: Box<Block>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The custom rusty variant of parsing rsx!
|
||||||
|
impl Parse for InlinePropsBody {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let vis: Visibility = input.parse()?;
|
||||||
|
|
||||||
|
let fn_token = input.parse()?;
|
||||||
|
let ident = input.parse()?;
|
||||||
|
let generics = input.parse()?;
|
||||||
|
|
||||||
|
let content;
|
||||||
|
let paren_token = syn::parenthesized!(content in input);
|
||||||
|
|
||||||
|
let cx_token: Ident = content.parse()?;
|
||||||
|
let _: Token![:] = content.parse()?;
|
||||||
|
let _: Ident = content.parse()?;
|
||||||
|
let _: Result<Token![,]> = content.parse();
|
||||||
|
|
||||||
|
let inputs = syn::punctuated::Punctuated::parse_terminated(&content)?;
|
||||||
|
|
||||||
|
let output = input.parse()?;
|
||||||
|
|
||||||
|
let block = input.parse()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
vis,
|
||||||
|
fn_token,
|
||||||
|
ident,
|
||||||
|
generics,
|
||||||
|
paren_token,
|
||||||
|
inputs,
|
||||||
|
output,
|
||||||
|
block,
|
||||||
|
cx_token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize the same way, regardless of flavor
|
||||||
|
impl ToTokens for InlinePropsBody {
|
||||||
|
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
|
||||||
|
let Self {
|
||||||
|
vis,
|
||||||
|
ident,
|
||||||
|
generics,
|
||||||
|
inputs,
|
||||||
|
output,
|
||||||
|
block,
|
||||||
|
cx_token,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let fields = inputs.iter().map(|f| {
|
||||||
|
quote! { #vis #f }
|
||||||
|
});
|
||||||
|
|
||||||
|
let struct_name = Ident::new(&format!("{}Props", ident), Span::call_site());
|
||||||
|
|
||||||
|
let field_names = inputs.iter().filter_map(|f| match f {
|
||||||
|
FnArg::Receiver(_) => todo!(),
|
||||||
|
FnArg::Typed(t) => Some(&t.pat),
|
||||||
|
});
|
||||||
|
|
||||||
|
let modifiers = if generics.params.is_empty() {
|
||||||
|
quote! { #[derive(Props, PartialEq)] }
|
||||||
|
} else {
|
||||||
|
quote! { #[derive(PartialEq)] }
|
||||||
|
};
|
||||||
|
|
||||||
|
out_tokens.append_all(quote! {
|
||||||
|
#modifiers
|
||||||
|
#vis struct #struct_name #generics {
|
||||||
|
#(#fields),*
|
||||||
|
}
|
||||||
|
|
||||||
|
#vis fn #ident #generics (#cx_token: Scope<#struct_name>) #output {
|
||||||
|
let #struct_name { #(#field_names),* } = &cx.props;
|
||||||
|
#block
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,8 @@ use proc_macro::TokenStream;
|
|||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::parse_macro_input;
|
use syn::parse_macro_input;
|
||||||
|
|
||||||
pub(crate) mod htm;
|
|
||||||
pub(crate) mod ifmt;
|
pub(crate) mod ifmt;
|
||||||
|
pub(crate) mod inlineprops;
|
||||||
pub(crate) mod props;
|
pub(crate) mod props;
|
||||||
pub(crate) mod router;
|
pub(crate) mod router;
|
||||||
pub(crate) mod rsx;
|
pub(crate) mod rsx;
|
||||||
@@ -214,3 +214,37 @@ pub fn routable_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
|||||||
let input = parse_macro_input!(input as Routable);
|
let input = parse_macro_input!(input as Routable);
|
||||||
routable_derive_impl(input).into()
|
routable_derive_impl(input).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Derive props for a component within the component definition.
|
||||||
|
///
|
||||||
|
/// This macro provides a simple transformation from `Scope<{}>` to `Scope<P>`,
|
||||||
|
/// removing some boilerplate when defining props.
|
||||||
|
///
|
||||||
|
/// You don't *need* to use this macro at all, but it can be helpful in cases where
|
||||||
|
/// you would be repeating a lot of the usual Rust boilerplate.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// #[inline_props]
|
||||||
|
/// fn app(cx: Scope<{ bob: String }>) -> Element {
|
||||||
|
/// cx.render(rsx!("hello, {bob}"))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // is equivalent to
|
||||||
|
///
|
||||||
|
/// #[derive(PartialEq, Props)]
|
||||||
|
/// struct AppProps {
|
||||||
|
/// bob: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn app(cx: Scope<AppProps>) -> Element {
|
||||||
|
/// cx.render(rsx!("hello, {bob}"))
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn inline_props(_args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
|
||||||
|
match syn::parse::<inlineprops::InlinePropsBody>(s) {
|
||||||
|
Err(e) => e.to_compile_error().into(),
|
||||||
|
Ok(s) => s.to_token_stream().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,7 +52,6 @@ fern = { version = "0.6.0", features = ["colored"] }
|
|||||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||||
simple_logger = "1.13.0"
|
simple_logger = "1.13.0"
|
||||||
dioxus-core-macro = { path = "../core-macro", version = "^0.1.2" }
|
dioxus-core-macro = { path = "../core-macro", version = "^0.1.2" }
|
||||||
# dioxus-hooks = { path = "../hooks" }
|
|
||||||
criterion = "0.3.5"
|
criterion = "0.3.5"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,9 @@ fn main() {
|
|||||||
VNodes::Element(vel) => dbg!(vel),
|
VNodes::Element(vel) => dbg!(vel),
|
||||||
_ => todo!()
|
_ => todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
Dioxus-core builds off the many frameworks that came before it. Notably, Dioxus borrows these concepts:
|
Dioxus-core builds off the many frameworks that came before it. Notably, Dioxus borrows these concepts:
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ use dioxus_html as dioxus_elements;
|
|||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
||||||
fn App(cx: Scope<()>) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
cx.render(rsx!(div {
|
cx.render(rsx!(div {
|
||||||
App2 {
|
app2 (
|
||||||
p: "asd"
|
p: "asd"
|
||||||
}
|
)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ struct Borrowed<'a> {
|
|||||||
p: &'a str,
|
p: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn App2<'a>(cx: Scope<'a, Borrowed<'a>>) -> Element {
|
fn app2<'a>(cx: Scope<'a, Borrowed<'a>>) -> Element {
|
||||||
let g = eat2(&cx);
|
let g = eat2(&cx);
|
||||||
rsx!(cx, "")
|
rsx!(cx, "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! This module contains the stateful DiffMachine and all methods to diff VNodes, their properties, and their children.
|
//! This module contains the stateful DiffState and all methods to diff VNodes, their properties, and their children.
|
||||||
//!
|
//!
|
||||||
//! The [`DiffMachine`] calculates the diffs between the old and new frames, updates the new nodes, and generates a set
|
//! The [`DiffState`] calculates the diffs between the old and new frames, updates the new nodes, and generates a set
|
||||||
//! of mutations for the RealDom to apply.
|
//! of mutations for the RealDom to apply.
|
||||||
//!
|
//!
|
||||||
//! ## Notice:
|
//! ## Notice:
|
||||||
@@ -86,14 +86,14 @@
|
|||||||
//! - Certain web-dom-specific optimizations.
|
//! - Certain web-dom-specific optimizations.
|
||||||
//!
|
//!
|
||||||
//! More info on how to improve this diffing algorithm:
|
//! More info on how to improve this diffing algorithm:
|
||||||
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
//! - <https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/>
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use DomEdit::*;
|
use DomEdit::*;
|
||||||
|
|
||||||
/// Our DiffMachine is an iterative tree differ.
|
/// Our DiffState is an iterative tree differ.
|
||||||
///
|
///
|
||||||
/// It uses techniques of a stack machine to allow pausing and restarting of the diff algorithm. This
|
/// It uses techniques of a stack machine to allow pausing and restarting of the diff algorithm. This
|
||||||
/// was originally implemented using recursive techniques, but Rust lacks the ability to call async functions recursively,
|
/// was originally implemented using recursive techniques, but Rust lacks the ability to call async functions recursively,
|
||||||
@@ -490,7 +490,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
(Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
|
(Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
|
||||||
|
|
||||||
// The normal pathway still works, but generates slightly weird instructions
|
// The normal pathway still works, but generates slightly weird instructions
|
||||||
// This pathway just ensures we get create and replace
|
// This pathway ensures uses the ReplaceAll, not the InsertAfter and remove
|
||||||
(Placeholder(_), Fragment(new)) => {
|
(Placeholder(_), Fragment(new)) => {
|
||||||
self.stack
|
self.stack
|
||||||
.create_children(new.children, MountType::Replace { old: old_node });
|
.create_children(new.children, MountType::Replace { old: old_node });
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
//! This can be done either through boxing directly, or by using dynamic-sized-types and a custom allocator. In our case,
|
//! This can be done either through boxing directly, or by using dynamic-sized-types and a custom allocator. In our case,
|
||||||
//! we build a tiny alloactor in the stack and allocate the closure into that.
|
//! we build a tiny alloactor in the stack and allocate the closure into that.
|
||||||
//!
|
//!
|
||||||
//! The logic for this was borrowed from https://docs.rs/stack_dst/0.6.1/stack_dst/. Unfortunately, this crate does not
|
//! The logic for this was borrowed from <https://docs.rs/stack_dst/0.6.1/stack_dst/>. Unfortunately, this crate does not
|
||||||
//! support non-static closures, so we've implemented the core logic of `ValueA` in this module.
|
//! support non-static closures, so we've implemented the core logic of `ValueA` in this module.
|
||||||
|
|
||||||
use crate::prelude::{NodeFactory, VNode};
|
use crate::innerlude::{NodeFactory, VNode};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
/// A concrete type provider for closures that build VNode structures.
|
/// A concrete type provider for closures that build VNode structures.
|
||||||
@@ -35,15 +35,15 @@ enum StackNodeStorage<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> LazyNodes<'a, 'b> {
|
impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||||
pub fn new_some<F>(_val: F) -> Option<Self>
|
pub fn new_some<F>(_val: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
||||||
{
|
{
|
||||||
Some(Self::new(_val))
|
Self::new(_val)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// force this call onto the stack
|
/// force this call onto the stack
|
||||||
pub fn new_boxed<F>(_val: F) -> Option<Self>
|
pub fn new_boxed<F>(_val: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
||||||
{
|
{
|
||||||
@@ -55,9 +55,9 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
|
|||||||
fac.map(inner)
|
fac.map(inner)
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Self {
|
Self {
|
||||||
inner: StackNodeStorage::Heap(Box::new(val)),
|
inner: StackNodeStorage::Heap(Box::new(val)),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new<F>(_val: F) -> Self
|
pub fn new<F>(_val: F) -> Self
|
||||||
@@ -249,9 +249,7 @@ fn it_works() {
|
|||||||
let caller = LazyNodes::new_some(|f| {
|
let caller = LazyNodes::new_some(|f| {
|
||||||
//
|
//
|
||||||
f.text(format_args!("hello world!"))
|
f.text(format_args!("hello world!"))
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let g = caller.call(factory);
|
let g = caller.call(factory);
|
||||||
|
|
||||||
dbg!(g);
|
dbg!(g);
|
||||||
@@ -291,7 +289,6 @@ fn it_drops() {
|
|||||||
log::debug!("main closure");
|
log::debug!("main closure");
|
||||||
f.fragment_from_iter(it)
|
f.fragment_from_iter(it)
|
||||||
})
|
})
|
||||||
.unwrap()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = caller.call(factory);
|
let _ = caller.call(factory);
|
||||||
|
|||||||
@@ -57,19 +57,25 @@ pub(crate) mod innerlude {
|
|||||||
/// // ...
|
/// // ...
|
||||||
/// };
|
/// };
|
||||||
/// ```
|
/// ```
|
||||||
pub type Component<P> = fn(Scope<P>) -> Element;
|
pub type Component<P = ()> = fn(Scope<P>) -> Element;
|
||||||
|
|
||||||
|
/// A list of attributes
|
||||||
|
///
|
||||||
|
pub type Attributes<'a> = Option<&'a [Attribute<'a>]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
Attribute, Component, DioxusElement, DomEdit, Element, ElementId, EventHandler, EventPriority,
|
Attribute, Component, DioxusElement, DomEdit, Element, ElementId, EventHandler, EventPriority,
|
||||||
IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, Properties, SchedulerMsg, Scope,
|
IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, Properties, SchedulerMsg, Scope,
|
||||||
ScopeId, ScopeState, UserEvent, VElement, VFragment, VNode, VirtualDom,
|
ScopeId, ScopeState, UserEvent, VComponent, VElement, VFragment, VNode, VPlaceholder, VText,
|
||||||
|
VirtualDom,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::innerlude::Scope;
|
pub use crate::innerlude::Scope;
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
Component, DioxusElement, Element, EventHandler, LazyNodes, NodeFactory, ScopeState,
|
Attributes, Component, DioxusElement, Element, EventHandler, LazyNodes, NodeFactory,
|
||||||
|
ScopeState,
|
||||||
};
|
};
|
||||||
pub use crate::nodes::VNode;
|
pub use crate::nodes::VNode;
|
||||||
pub use crate::virtual_dom::{fc_to_builder, Fragment, Properties};
|
pub use crate::virtual_dom::{fc_to_builder, Fragment, Properties};
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
//! Instructions returned by the VirtualDOM on how to modify the Real DOM.
|
//! Instructions returned by the VirtualDOM on how to modify the Real DOM.
|
||||||
//!
|
//!
|
||||||
//! This module contains an internal API to generate these instructions.
|
//! This module contains an internal API to generate these instructions.
|
||||||
|
//!
|
||||||
|
//! Beware that changing code in this module will break compatibility with
|
||||||
|
//! interpreters for these types of DomEdits.
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use std::{any::Any, fmt::Debug};
|
use std::{any::Any, fmt::Debug};
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use std::{
|
|||||||
///
|
///
|
||||||
/// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
|
/// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
|
||||||
///
|
///
|
||||||
/// - the [`rsx`] macro
|
/// - the `rsx!` macro
|
||||||
/// - the [`NodeFactory`] API
|
/// - the [`NodeFactory`] API
|
||||||
pub enum VNode<'src> {
|
pub enum VNode<'src> {
|
||||||
/// Text VNodes simply bump-allocated (or static) string slices
|
/// Text VNodes simply bump-allocated (or static) string slices
|
||||||
@@ -51,7 +51,7 @@ pub enum VNode<'src> {
|
|||||||
/// key: "a",
|
/// key: "a",
|
||||||
/// onclick: |e| log::info!("clicked"),
|
/// onclick: |e| log::info!("clicked"),
|
||||||
/// hidden: "true",
|
/// hidden: "true",
|
||||||
/// style: { background_color: "red" }
|
/// style: { background_color: "red" },
|
||||||
/// "hello"
|
/// "hello"
|
||||||
/// }
|
/// }
|
||||||
/// });
|
/// });
|
||||||
@@ -59,7 +59,7 @@ pub enum VNode<'src> {
|
|||||||
/// if let VNode::Element(velement) = node {
|
/// if let VNode::Element(velement) = node {
|
||||||
/// assert_eq!(velement.tag_name, "div");
|
/// assert_eq!(velement.tag_name, "div");
|
||||||
/// assert_eq!(velement.namespace, None);
|
/// assert_eq!(velement.namespace, None);
|
||||||
/// assert_eq!(velement.key, Some("a));
|
/// assert_eq!(velement.key, Some("a"));
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
Element(&'src VElement<'src>),
|
Element(&'src VElement<'src>),
|
||||||
@@ -720,6 +720,14 @@ impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> IntoIterator for LazyNodes<'a, 'b> {
|
||||||
|
type Item = LazyNodes<'a, 'b>;
|
||||||
|
type IntoIter = std::iter::Once<Self::Item>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
std::iter::once(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
|
impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
|
||||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||||
self.call(cx)
|
self.call(cx)
|
||||||
|
|||||||
+37
-16
@@ -351,7 +351,7 @@ impl ScopeArena {
|
|||||||
/// cx.render(rsx!{ div {"Hello, {cx.props.name}"} })
|
/// cx.render(rsx!{ div {"Hello, {cx.props.name}"} })
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Scope<'a, P> {
|
pub struct Scope<'a, P = ()> {
|
||||||
pub scope: &'a ScopeState,
|
pub scope: &'a ScopeState,
|
||||||
pub props: &'a P,
|
pub props: &'a P,
|
||||||
}
|
}
|
||||||
@@ -366,6 +366,26 @@ impl<P> Clone for Scope<'_, P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'src, P> Scope<'src, P> {
|
||||||
|
pub fn register_task(self, task: impl Future<Output = ()> + 'src) {
|
||||||
|
let fut: &'src mut dyn Future<Output = ()> = self.scope.bump().alloc(task);
|
||||||
|
let boxed_fut: BumpBox<'src, dyn Future<Output = ()> + 'src> =
|
||||||
|
unsafe { BumpBox::from_raw(fut) };
|
||||||
|
let pinned_fut: Pin<BumpBox<'src, _>> = boxed_fut.into();
|
||||||
|
|
||||||
|
// erase the 'src lifetime for self-referential storage
|
||||||
|
// todo: provide a miri test around this
|
||||||
|
// concerned about segfaulting
|
||||||
|
let self_ref_fut = unsafe { std::mem::transmute(pinned_fut) };
|
||||||
|
|
||||||
|
self.sender
|
||||||
|
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
self.scope.items.borrow_mut().tasks.push(self_ref_fut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, P> std::ops::Deref for Scope<'a, P> {
|
impl<'a, P> std::ops::Deref for Scope<'a, P> {
|
||||||
// rust will auto deref again to the original 'a lifetime at the call site
|
// rust will auto deref again to the original 'a lifetime at the call site
|
||||||
type Target = &'a ScopeState;
|
type Target = &'a ScopeState;
|
||||||
@@ -578,7 +598,7 @@ impl ScopeState {
|
|||||||
|
|
||||||
/// Schedule an update for any component given its ScopeId.
|
/// Schedule an update for any component given its ScopeId.
|
||||||
///
|
///
|
||||||
/// A component's ScopeId can be obtained from `use_hook` or the [`Context::scope_id`] method.
|
/// A component's ScopeId can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
|
||||||
///
|
///
|
||||||
/// This method should be used when you want to schedule an update for a component
|
/// This method should be used when you want to schedule an update for a component
|
||||||
pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
|
pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
|
||||||
@@ -662,29 +682,33 @@ impl ScopeState {
|
|||||||
/// Pushes the future onto the poll queue to be polled after the component renders.
|
/// Pushes the future onto the poll queue to be polled after the component renders.
|
||||||
///
|
///
|
||||||
/// The future is forcibly dropped if the component is not ready by the next render
|
/// The future is forcibly dropped if the component is not ready by the next render
|
||||||
pub fn push_task<'src, F>(&'src self, fut: impl FnOnce() -> F + 'src) -> usize
|
pub fn push_task<'src, F>(&'src self, fut: F) -> usize
|
||||||
where
|
where
|
||||||
F: Future<Output = ()>,
|
F: Future<Output = ()>,
|
||||||
F::Output: 'src,
|
F::Output: 'src,
|
||||||
F: 'src,
|
F: 'src,
|
||||||
{
|
{
|
||||||
self.sender
|
|
||||||
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// wrap it in a type that will actually drop the contents
|
// wrap it in a type that will actually drop the contents
|
||||||
//
|
//
|
||||||
// Safety: we just made the pointer above and will promise not to alias it!
|
// Safety: we just made the pointer above and will promise not to alias it!
|
||||||
// The main reason we do this through from_raw is because Bumpalo's box does
|
// The main reason we do this through from_raw is because Bumpalo's box does
|
||||||
// not support unsized coercion
|
// not support unsized coercion
|
||||||
let fut: &mut dyn Future<Output = ()> = self.bump().alloc(fut());
|
|
||||||
let boxed_fut: BumpBox<dyn Future<Output = ()>> = unsafe { BumpBox::from_raw(fut) };
|
let fut: &'src mut dyn Future<Output = ()> = self.bump().alloc(fut);
|
||||||
let pinned_fut: Pin<BumpBox<_>> = boxed_fut.into();
|
let boxed_fut: BumpBox<'src, dyn Future<Output = ()> + 'src> =
|
||||||
|
unsafe { BumpBox::from_raw(fut) };
|
||||||
|
let pinned_fut: Pin<BumpBox<'src, _>> = boxed_fut.into();
|
||||||
|
|
||||||
// erase the 'src lifetime for self-referential storage
|
// erase the 'src lifetime for self-referential storage
|
||||||
// todo: provide a miri test around this
|
// todo: provide a miri test around this
|
||||||
// concerned about segfaulting
|
// concerned about segfaulting
|
||||||
let self_ref_fut = unsafe { std::mem::transmute(pinned_fut) };
|
let self_ref_fut = unsafe {
|
||||||
|
std::mem::transmute::<Pin<BumpBox<'src, _>>, Pin<BumpBox<'static, _>>>(pinned_fut)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.sender
|
||||||
|
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Push the future into the tasks
|
// Push the future into the tasks
|
||||||
let mut items = self.items.borrow_mut();
|
let mut items = self.items.borrow_mut();
|
||||||
@@ -707,14 +731,11 @@ impl ScopeState {
|
|||||||
/// cx.render(lazy_tree)
|
/// cx.render(lazy_tree)
|
||||||
/// }
|
/// }
|
||||||
///```
|
///```
|
||||||
pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VNode<'src>> {
|
pub fn render<'src>(&'src self, rsx: LazyNodes<'src, '_>) -> Option<VNode<'src>> {
|
||||||
let fac = NodeFactory {
|
let fac = NodeFactory {
|
||||||
bump: &self.wip_frame().bump,
|
bump: &self.wip_frame().bump,
|
||||||
};
|
};
|
||||||
match rsx {
|
Some(rsx.call(fac))
|
||||||
Some(s) => Some(s.call(fac)),
|
|
||||||
None => todo!("oh no no nodes"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a value between renders
|
/// Store a value between renders
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use std::{any::Any, collections::VecDeque, iter::FromIterator, pin::Pin, sync::A
|
|||||||
///
|
///
|
||||||
/// ## Guide
|
/// ## Guide
|
||||||
///
|
///
|
||||||
/// Components are defined as simple functions that take [`Context`] and a [`Properties`] type and return an [`Element`].
|
/// Components are defined as simple functions that take [`Scope`] and return an [`Element`].
|
||||||
///
|
///
|
||||||
/// ```rust, ignore
|
/// ```rust, ignore
|
||||||
/// #[derive(Props, PartialEq)]
|
/// #[derive(Props, PartialEq)]
|
||||||
@@ -552,11 +552,11 @@ impl VirtualDom {
|
|||||||
/// let dom = VirtualDom::new(Base);
|
/// let dom = VirtualDom::new(Base);
|
||||||
/// let nodes = dom.render_nodes(rsx!("div"));
|
/// let nodes = dom.render_nodes(rsx!("div"));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
|
pub fn render_vnodes<'a>(&'a self, lazy_nodes: LazyNodes<'a, '_>) -> &'a VNode<'a> {
|
||||||
let scope = self.scopes.get_scope(ScopeId(0)).unwrap();
|
let scope = self.scopes.get_scope(ScopeId(0)).unwrap();
|
||||||
let frame = scope.wip_frame();
|
let frame = scope.wip_frame();
|
||||||
let factory = NodeFactory { bump: &frame.bump };
|
let factory = NodeFactory { bump: &frame.bump };
|
||||||
let node = lazy_nodes.unwrap().call(factory);
|
let node = lazy_nodes.call(factory);
|
||||||
frame.bump.alloc(node)
|
frame.bump.alloc(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,7 +594,7 @@ impl VirtualDom {
|
|||||||
/// let dom = VirtualDom::new(Base);
|
/// let dom = VirtualDom::new(Base);
|
||||||
/// let nodes = dom.render_nodes(rsx!("div"));
|
/// let nodes = dom.render_nodes(rsx!("div"));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn create_vnodes<'a>(&'a self, nodes: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
|
pub fn create_vnodes<'a>(&'a self, nodes: LazyNodes<'a, '_>) -> Mutations<'a> {
|
||||||
let mut machine = DiffState::new(&self.scopes);
|
let mut machine = DiffState::new(&self.scopes);
|
||||||
machine.stack.element_stack.push(ElementId(0));
|
machine.stack.element_stack.push(ElementId(0));
|
||||||
machine
|
machine
|
||||||
@@ -619,8 +619,8 @@ impl VirtualDom {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn diff_lazynodes<'a>(
|
pub fn diff_lazynodes<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
left: Option<LazyNodes<'a, '_>>,
|
left: LazyNodes<'a, '_>,
|
||||||
right: Option<LazyNodes<'a, '_>>,
|
right: LazyNodes<'a, '_>,
|
||||||
) -> (Mutations<'a>, Mutations<'a>) {
|
) -> (Mutations<'a>, Mutations<'a>) {
|
||||||
let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
|
let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
|
||||||
|
|
||||||
@@ -947,7 +947,7 @@ impl<'a> Properties for FragmentProps<'a> {
|
|||||||
#[allow(non_upper_case_globals, non_snake_case)]
|
#[allow(non_upper_case_globals, non_snake_case)]
|
||||||
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
|
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
|
||||||
let i = cx.props.0.as_ref().map(|f| f.decouple());
|
let i = cx.props.0.as_ref().map(|f| f.decouple());
|
||||||
cx.render(Some(LazyNodes::new(|f| f.fragment_from_iter(i))))
|
cx.render(LazyNodes::new(|f| f.fragment_from_iter(i)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
|
/// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_original_diff() {
|
fn test_original_diff() {
|
||||||
static APP: Component<()> = |cx| {
|
static APP: Component = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
div {
|
div {
|
||||||
@@ -57,7 +57,7 @@ fn test_original_diff() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create() {
|
fn create() {
|
||||||
static APP: Component<()> = |cx| {
|
static APP: Component = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
div {
|
div {
|
||||||
@@ -120,7 +120,7 @@ fn create() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_list() {
|
fn create_list() {
|
||||||
static APP: Component<()> = |cx| {
|
static APP: Component = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
{(0..3).map(|f| rsx!{ div {
|
{(0..3).map(|f| rsx!{ div {
|
||||||
"hello"
|
"hello"
|
||||||
@@ -169,7 +169,7 @@ fn create_list() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_simple() {
|
fn create_simple() {
|
||||||
static APP: Component<()> = |cx| {
|
static APP: Component = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {}
|
div {}
|
||||||
div {}
|
div {}
|
||||||
@@ -207,7 +207,7 @@ fn create_simple() {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn create_components() {
|
fn create_components() {
|
||||||
static App: Component<()> = |cx| {
|
static App: Component = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
Child { "abc1" }
|
Child { "abc1" }
|
||||||
Child { "abc2" }
|
Child { "abc2" }
|
||||||
@@ -273,7 +273,7 @@ fn create_components() {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn anchors() {
|
fn anchors() {
|
||||||
static App: Component<()> = |cx| {
|
static App: Component = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
{true.then(|| rsx!{ div { "hello" } })}
|
{true.then(|| rsx!{ div { "hello" } })}
|
||||||
{false.then(|| rsx!{ div { "goodbye" } })}
|
{false.then(|| rsx!{ div { "goodbye" } })}
|
||||||
|
|||||||
@@ -802,3 +802,68 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// noticed some weird behavior in the desktop interpreter
|
||||||
|
// just making sure it doesnt happen in the core implementation
|
||||||
|
#[test]
|
||||||
|
fn remove_list() {
|
||||||
|
let dom = new_dom();
|
||||||
|
|
||||||
|
let left = rsx!({
|
||||||
|
(0..10).rev().take(5).map(|i| {
|
||||||
|
rsx! { Fragment { key: "{i}", "{i}" }}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let right = rsx!({
|
||||||
|
(0..10).rev().take(2).map(|i| {
|
||||||
|
rsx! { Fragment { key: "{i}", "{i}" }}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let (create, changes) = dom.diff_lazynodes(left, right);
|
||||||
|
|
||||||
|
// dbg!(create);
|
||||||
|
// dbg!(changes);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
changes.edits,
|
||||||
|
[
|
||||||
|
// remove 5, 4, 3
|
||||||
|
Remove { root: 3 },
|
||||||
|
Remove { root: 4 },
|
||||||
|
Remove { root: 5 },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// noticed some weird behavior in the desktop interpreter
|
||||||
|
// just making sure it doesnt happen in the core implementation
|
||||||
|
#[test]
|
||||||
|
fn remove_list_nokeyed() {
|
||||||
|
let dom = new_dom();
|
||||||
|
|
||||||
|
let left = rsx!({
|
||||||
|
(0..10).rev().take(5).map(|i| {
|
||||||
|
rsx! { Fragment { "{i}" }}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let right = rsx!({
|
||||||
|
(0..10).rev().take(2).map(|i| {
|
||||||
|
rsx! { Fragment { "{i}" }}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let (create, changes) = dom.diff_lazynodes(left, right);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
changes.edits,
|
||||||
|
[
|
||||||
|
// remove 5, 4, 3
|
||||||
|
Remove { root: 3 },
|
||||||
|
Remove { root: 4 },
|
||||||
|
Remove { root: 5 },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ use dioxus_hooks::*;
|
|||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
simple_logger::init().unwrap();
|
// simple_logger::init().unwrap();
|
||||||
dioxus_desktop::launch(app);
|
dioxus_desktop::launch(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ fn app(cx: Scope<()>) -> Element {
|
|||||||
|
|
||||||
cx.push_task(|| async move {
|
cx.push_task(|| async move {
|
||||||
tokio::time::sleep(Duration::from_millis(1000)).await;
|
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||||
|
println!("count is now {:?}", count);
|
||||||
count += 1;
|
count += 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -33,4 +34,4 @@ fn app(cx: Scope<()>) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
}
|
||||||
+45
-26
@@ -23,6 +23,7 @@ use tao::{
|
|||||||
pub use wry;
|
pub use wry;
|
||||||
pub use wry::application as tao;
|
pub use wry::application as tao;
|
||||||
use wry::{
|
use wry::{
|
||||||
|
application::event_loop::EventLoopProxy,
|
||||||
webview::RpcRequest,
|
webview::RpcRequest,
|
||||||
webview::{WebView, WebViewBuilder},
|
webview::{WebView, WebViewBuilder},
|
||||||
};
|
};
|
||||||
@@ -37,7 +38,7 @@ pub fn launch_cfg(
|
|||||||
launch_with_props(root, (), config_builder)
|
launch_with_props(root, (), config_builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn launch_with_props<P: Properties + 'static + Send + Sync>(
|
pub fn launch_with_props<P: 'static + Send>(
|
||||||
root: Component<P>,
|
root: Component<P>,
|
||||||
props: P,
|
props: P,
|
||||||
builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
|
builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
|
||||||
@@ -51,7 +52,7 @@ struct Response<'a> {
|
|||||||
edits: Vec<DomEdit<'a>>,
|
edits: Vec<DomEdit<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<T: 'static + Send + Sync>(
|
pub fn run<T: 'static + Send>(
|
||||||
root: Component<T>,
|
root: Component<T>,
|
||||||
props: T,
|
props: T,
|
||||||
user_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
|
user_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
|
||||||
@@ -59,51 +60,61 @@ pub fn run<T: 'static + Send + Sync>(
|
|||||||
let mut desktop_cfg = DesktopConfig::new();
|
let mut desktop_cfg = DesktopConfig::new();
|
||||||
user_builder(&mut desktop_cfg);
|
user_builder(&mut desktop_cfg);
|
||||||
|
|
||||||
let mut state = DesktopController::new_on_tokio(root, props);
|
let event_loop = EventLoop::with_user_event();
|
||||||
|
let mut desktop = DesktopController::new_on_tokio(root, props, event_loop.create_proxy());
|
||||||
let quit_hotkey = Accelerator::new(SysMods::Cmd, KeyCode::KeyQ);
|
let quit_hotkey = Accelerator::new(SysMods::Cmd, KeyCode::KeyQ);
|
||||||
let modifiers = ModifiersState::default();
|
let modifiers = ModifiersState::default();
|
||||||
let event_loop = EventLoop::new();
|
|
||||||
|
|
||||||
log::debug!("Starting event loop");
|
|
||||||
|
|
||||||
event_loop.run(move |window_event, event_loop, control_flow| {
|
event_loop.run(move |window_event, event_loop, control_flow| {
|
||||||
*control_flow = ControlFlow::Wait;
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
match window_event {
|
match window_event {
|
||||||
Event::NewEvents(StartCause::Init) => state.new_window(&desktop_cfg, event_loop),
|
Event::NewEvents(StartCause::Init) => desktop.new_window(&desktop_cfg, event_loop),
|
||||||
|
|
||||||
Event::WindowEvent {
|
Event::WindowEvent {
|
||||||
event, window_id, ..
|
event, window_id, ..
|
||||||
} => match event {
|
} => {
|
||||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
match event {
|
||||||
WindowEvent::Destroyed { .. } => state.close_window(window_id, control_flow),
|
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||||
|
WindowEvent::Destroyed { .. } => desktop.close_window(window_id, control_flow),
|
||||||
|
|
||||||
WindowEvent::KeyboardInput { event, .. } => {
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
if quit_hotkey.matches(&modifiers, &event.physical_key) {
|
if quit_hotkey.matches(&modifiers, &event.physical_key) {
|
||||||
state.close_window(window_id, control_flow);
|
desktop.close_window(window_id, control_flow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
WindowEvent::Resized(_) | WindowEvent::Moved(_) => {
|
WindowEvent::Resized(_) | WindowEvent::Moved(_) => {
|
||||||
if let Some(view) = state.webviews.get_mut(&window_id) {
|
if let Some(view) = desktop.webviews.get_mut(&window_id) {
|
||||||
let _ = view.resize();
|
let _ = view.resize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: we want to shuttle all of these events into the user's app or provide some handler
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: we want to shuttle all of these events into the user's app or provide some handler
|
Event::UserEvent(_evt) => {
|
||||||
_ => {}
|
desktop.try_load_ready_webviews();
|
||||||
},
|
}
|
||||||
|
Event::MainEventsCleared => {
|
||||||
Event::MainEventsCleared => state.try_load_ready_webviews(),
|
desktop.try_load_ready_webviews();
|
||||||
|
}
|
||||||
Event::Resumed => {}
|
Event::Resumed => {}
|
||||||
Event::Suspended => {}
|
Event::Suspended => {}
|
||||||
Event::LoopDestroyed => {}
|
Event::LoopDestroyed => {}
|
||||||
|
Event::RedrawRequested(_id) => {}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum UserWindowEvent {
|
||||||
|
Start,
|
||||||
|
Update,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DesktopController {
|
pub struct DesktopController {
|
||||||
webviews: HashMap<WindowId, WebView>,
|
webviews: HashMap<WindowId, WebView>,
|
||||||
sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
||||||
@@ -115,7 +126,11 @@ pub struct DesktopController {
|
|||||||
impl DesktopController {
|
impl DesktopController {
|
||||||
// Launch the virtualdom on its own thread managed by tokio
|
// Launch the virtualdom on its own thread managed by tokio
|
||||||
// returns the desktop state
|
// returns the desktop state
|
||||||
pub fn new_on_tokio<P: Send + 'static>(root: Component<P>, props: P) -> Self {
|
pub fn new_on_tokio<P: Send + 'static>(
|
||||||
|
root: Component<P>,
|
||||||
|
props: P,
|
||||||
|
evt: EventLoopProxy<UserWindowEvent>,
|
||||||
|
) -> Self {
|
||||||
let edit_queue = Arc::new(RwLock::new(VecDeque::new()));
|
let edit_queue = Arc::new(RwLock::new(VecDeque::new()));
|
||||||
let pending_edits = edit_queue.clone();
|
let pending_edits = edit_queue.clone();
|
||||||
|
|
||||||
@@ -130,7 +145,6 @@ impl DesktopController {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
runtime.block_on(async move {
|
runtime.block_on(async move {
|
||||||
// LocalSet::new().block_on(&runtime, async move {
|
|
||||||
let mut dom =
|
let mut dom =
|
||||||
VirtualDom::new_with_props_and_scheduler(root, props, (sender, receiver));
|
VirtualDom::new_with_props_and_scheduler(root, props, (sender, receiver));
|
||||||
|
|
||||||
@@ -150,6 +164,7 @@ impl DesktopController {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.push_front(serde_json::to_string(&edit.edits).unwrap());
|
.push_front(serde_json::to_string(&edit.edits).unwrap());
|
||||||
}
|
}
|
||||||
|
let _ = evt.send_event(UserWindowEvent::Update);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -164,7 +179,11 @@ impl DesktopController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_window(&mut self, cfg: &DesktopConfig, event_loop: &EventLoopWindowTarget<()>) {
|
pub fn new_window(
|
||||||
|
&mut self,
|
||||||
|
cfg: &DesktopConfig,
|
||||||
|
event_loop: &EventLoopWindowTarget<UserWindowEvent>,
|
||||||
|
) {
|
||||||
let builder = cfg.window.clone().with_menu({
|
let builder = cfg.window.clone().with_menu({
|
||||||
// create main menubar menu
|
// create main menubar menu
|
||||||
let mut menu_bar_menu = MenuBar::new();
|
let mut menu_bar_menu = MenuBar::new();
|
||||||
|
|||||||
@@ -5,44 +5,71 @@ use std::{
|
|||||||
pin::Pin,
|
pin::Pin,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
/*
|
||||||
|
|
||||||
pub fn use_coroutine<'a, F: Future<Output = ()> + 'static>(
|
|
||||||
|
|
||||||
|
let g = use_coroutine(&cx, || {
|
||||||
|
// clone the items in
|
||||||
|
async move {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
pub fn use_coroutine<'a, F>(
|
||||||
cx: &'a ScopeState,
|
cx: &'a ScopeState,
|
||||||
mut f: impl FnMut() -> F + 'a,
|
create_future: impl FnOnce() -> F,
|
||||||
) -> CoroutineHandle<'a> {
|
) -> CoroutineHandle<'a>
|
||||||
|
where
|
||||||
|
F: Future<Output = ()>,
|
||||||
|
F: 'static,
|
||||||
|
{
|
||||||
cx.use_hook(
|
cx.use_hook(
|
||||||
move |_| State {
|
move |_| State {
|
||||||
running: Default::default(),
|
running: Default::default(),
|
||||||
fut: Default::default(),
|
pending_fut: Default::default(),
|
||||||
submit: Default::default(),
|
running_fut: Default::default(),
|
||||||
},
|
},
|
||||||
|state| {
|
|state| {
|
||||||
let fut_slot = state.fut.clone();
|
let f = create_future();
|
||||||
let running = state.running.clone();
|
state.pending_fut.set(Some(Box::pin(f)));
|
||||||
let submit: Box<dyn FnOnce() + 'a> = Box::new(move || {
|
|
||||||
let g = async move {
|
|
||||||
running.set(true);
|
|
||||||
f().await;
|
|
||||||
running.set(false);
|
|
||||||
};
|
|
||||||
let p: Pin<Box<dyn Future<Output = ()>>> = Box::pin(g);
|
|
||||||
fut_slot
|
|
||||||
.borrow_mut()
|
|
||||||
.replace(unsafe { std::mem::transmute(p) });
|
|
||||||
});
|
|
||||||
|
|
||||||
let submit = unsafe { std::mem::transmute(submit) };
|
if let Some(fut) = state.running_fut.as_mut() {
|
||||||
state.submit.get_mut().replace(submit);
|
cx.push_task(fut);
|
||||||
|
|
||||||
if state.running.get() {
|
|
||||||
let mut fut = state.fut.borrow_mut();
|
|
||||||
cx.push_task(|| fut.as_mut().unwrap().as_mut());
|
|
||||||
} else {
|
|
||||||
// make sure to drop the old future
|
|
||||||
if let Some(fut) = state.fut.borrow_mut().take() {
|
|
||||||
drop(fut);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if let Some(fut) = state.running_fut.take() {
|
||||||
|
// state.running.set(true);
|
||||||
|
// fut.resume();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let submit: Box<dyn FnOnce() + 'a> = Box::new(move || {
|
||||||
|
// let g = async move {
|
||||||
|
// running.set(true);
|
||||||
|
// create_future().await;
|
||||||
|
// running.set(false);
|
||||||
|
// };
|
||||||
|
// let p: Pin<Box<dyn Future<Output = ()>>> = Box::pin(g);
|
||||||
|
// fut_slot
|
||||||
|
// .borrow_mut()
|
||||||
|
// .replace(unsafe { std::mem::transmute(p) });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// let submit = unsafe { std::mem::transmute(submit) };
|
||||||
|
// state.submit.get_mut().replace(submit);
|
||||||
|
|
||||||
|
// if state.running.get() {
|
||||||
|
// // let mut fut = state.fut.borrow_mut();
|
||||||
|
// // cx.push_task(|| fut.as_mut().unwrap().as_mut());
|
||||||
|
// } else {
|
||||||
|
// // make sure to drop the old future
|
||||||
|
// if let Some(fut) = state.fut.borrow_mut().take() {
|
||||||
|
// drop(fut);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
CoroutineHandle { cx, inner: state }
|
CoroutineHandle { cx, inner: state }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -50,14 +77,20 @@ pub fn use_coroutine<'a, F: Future<Output = ()> + 'static>(
|
|||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
running: Rc<Cell<bool>>,
|
running: Rc<Cell<bool>>,
|
||||||
submit: RefCell<Option<Box<dyn FnOnce()>>>,
|
|
||||||
fut: Rc<RefCell<Option<Pin<Box<dyn Future<Output = ()>>>>>>,
|
// the way this is structure, you can toggle the coroutine without re-rendering the comppnent
|
||||||
|
// this means every render *generates* the future, which is a bit of a waste
|
||||||
|
// todo: allocate pending futures in the bump allocator and then have a true promotion
|
||||||
|
pending_fut: Cell<Option<Pin<Box<dyn Future<Output = ()> + 'static>>>>,
|
||||||
|
running_fut: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>,
|
||||||
|
// running_fut: Rc<RefCell<Option<Pin<Box<dyn Future<Output = ()> + 'static>>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CoroutineHandle<'a> {
|
pub struct CoroutineHandle<'a> {
|
||||||
cx: &'a ScopeState,
|
cx: &'a ScopeState,
|
||||||
inner: &'a State,
|
inner: &'a State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for CoroutineHandle<'_> {
|
impl Clone for CoroutineHandle<'_> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
CoroutineHandle {
|
CoroutineHandle {
|
||||||
@@ -73,10 +106,11 @@ impl<'a> CoroutineHandle<'a> {
|
|||||||
if self.is_running() {
|
if self.is_running() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(submit) = self.inner.submit.borrow_mut().take() {
|
|
||||||
submit();
|
if let Some(submit) = self.inner.pending_fut.take() {
|
||||||
let mut fut = self.inner.fut.borrow_mut();
|
// submit();
|
||||||
self.cx.push_task(|| fut.as_mut().unwrap().as_mut());
|
// let inner = self.inner;
|
||||||
|
// self.cx.push_task(submit());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,5 +118,11 @@ impl<'a> CoroutineHandle<'a> {
|
|||||||
self.inner.running.get()
|
self.inner.running.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resume(&self) {}
|
pub fn resume(&self) {
|
||||||
|
// self.cx.push_task(fut)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&self) {}
|
||||||
|
|
||||||
|
pub fn restart(&self) {}
|
||||||
}
|
}
|
||||||
@@ -89,9 +89,9 @@ pub fn use_model_coroutine<'a, T, F: Future<Output = ()> + 'static>(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|inner| {
|
|inner| {
|
||||||
if let Some(task) = inner.task.get_mut() {
|
// if let Some(task) = inner.task.get_mut() {
|
||||||
cx.push_task(|| task);
|
// cx.push_task(|| task);
|
||||||
}
|
// }
|
||||||
//
|
//
|
||||||
todo!()
|
todo!()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -171,7 +171,11 @@ impl<'a, T: 'static> UseState<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
|
impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
|
||||||
/// Gain mutable access to the new value. This method is only available when the value is a `ToOwned` type.
|
/// Gain mutable access to the new value via RefMut.
|
||||||
|
///
|
||||||
|
/// If `modify` is called, then the component will re-render.
|
||||||
|
///
|
||||||
|
/// This method is only available when the value is a `ToOwned` type.
|
||||||
///
|
///
|
||||||
/// Mutable access is derived by calling "ToOwned" (IE cloning) on the current value.
|
/// Mutable access is derived by calling "ToOwned" (IE cloning) on the current value.
|
||||||
///
|
///
|
||||||
|
|||||||
+34
-11
@@ -2,14 +2,28 @@ mod utils;
|
|||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use dioxus::Attribute;
|
||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
|
|
||||||
use dioxus_core::prelude::*;
|
use dioxus_core::prelude::*;
|
||||||
use dioxus_core_macro::{rsx, Props};
|
use dioxus_core_macro::{format_args_f, rsx, Props};
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
// use wasm_bindgen::{JsCast, JsValue};
|
// use wasm_bindgen::{JsCast, JsValue};
|
||||||
|
|
||||||
use crate::utils::strip_slash_suffix;
|
use crate::utils::strip_slash_suffix;
|
||||||
|
|
||||||
|
/// Initialize the app's router service and provide access to `Link` components
|
||||||
|
pub fn use_router<R: 'static>(cx: &ScopeState, f: impl Fn(&str) -> R) -> &R {
|
||||||
|
let r = f("/");
|
||||||
|
cx.use_hook(
|
||||||
|
|_| {
|
||||||
|
//
|
||||||
|
r
|
||||||
|
},
|
||||||
|
|f| f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Routable: 'static + Send + Clone + PartialEq {}
|
pub trait Routable: 'static + Send + Clone + PartialEq {}
|
||||||
impl<T> Routable for T where T: 'static + Send + Clone + PartialEq {}
|
impl<T> Routable for T where T: 'static + Send + Clone + PartialEq {}
|
||||||
|
|
||||||
@@ -30,20 +44,29 @@ pub struct LinkProps<'a, R: Routable> {
|
|||||||
/// Link { to: Route::Home, href: Route::as_url }
|
/// Link { to: Route::Home, href: Route::as_url }
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
href: fn(&R) -> String,
|
#[props(default, setter(strip_option))]
|
||||||
|
href: Option<&'a str>,
|
||||||
|
|
||||||
|
#[props(default, setter(strip_option))]
|
||||||
|
class: Option<&'a str>,
|
||||||
|
|
||||||
#[builder(default)]
|
|
||||||
children: Element<'a>,
|
children: Element<'a>,
|
||||||
|
|
||||||
|
#[props(default)]
|
||||||
|
attributes: Option<&'a [Attribute<'a>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element {
|
pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element {
|
||||||
let service = todo!();
|
// let service = todo!();
|
||||||
// let service: todo!() = use_router_service::<R>(&cx)?;
|
// let service: todo!() = use_router_service::<R>(&cx)?;
|
||||||
// cx.render(rsx! {
|
let class = cx.props.class.unwrap_or("");
|
||||||
// a {
|
cx.render(rsx! {
|
||||||
// href: format_args!("{}", (cx.props.href)(&cx.props.to)),
|
a {
|
||||||
// onclick: move |_| service.push_route(cx.props.to.clone()),
|
href: "#",
|
||||||
// // todo!() {&cx.props.children},
|
class: "{class}",
|
||||||
// }
|
{&cx.props.children}
|
||||||
// })
|
// onclick: move |_| service.push_route(cx.props.to.clone()),
|
||||||
|
// href: format_args!("{}", (cx.props.href)(&cx.props.to)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
@@ -15,26 +15,27 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static App: Component<()> = |cx| {
|
static App: Component<()> = |cx| {
|
||||||
let doggo = cx.suspend(|| async move {
|
todo!("suspense is broken")
|
||||||
#[derive(serde::Deserialize)]
|
// let doggo = suspend(|| async move {
|
||||||
struct Doggo {
|
// #[derive(serde::Deserialize)]
|
||||||
message: String,
|
// struct Doggo {
|
||||||
}
|
// message: String,
|
||||||
|
// }
|
||||||
|
|
||||||
let src = reqwest::get("https://dog.ceo/api/breeds/image/random")
|
// let src = reqwest::get("https://dog.ceo/api/breeds/image/random")
|
||||||
.await
|
// .await
|
||||||
.expect("Failed to fetch doggo")
|
// .expect("Failed to fetch doggo")
|
||||||
.json::<Doggo>()
|
// .json::<Doggo>()
|
||||||
.await
|
// .await
|
||||||
.expect("Failed to parse doggo")
|
// .expect("Failed to parse doggo")
|
||||||
.message;
|
// .message;
|
||||||
|
|
||||||
rsx!(cx, img { src: "{src}" })
|
// rsx!(cx, img { src: "{src}" })
|
||||||
});
|
// });
|
||||||
|
|
||||||
rsx!(cx, div {
|
// rsx!(cx, div {
|
||||||
h1 {"One doggo coming right up"}
|
// h1 {"One doggo coming right up"}
|
||||||
button { onclick: move |_| cx.needs_update(), "Get a new doggo" }
|
// button { onclick: move |_| cx.needs_update(), "Get a new doggo" }
|
||||||
{doggo}
|
// {doggo}
|
||||||
})
|
// })
|
||||||
};
|
};
|
||||||
@@ -89,7 +89,7 @@ mod ric_raf;
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn launch(root_component: Component<()>) {
|
pub fn launch(root_component: Component<()>) {
|
||||||
launch_with_props(root_component, ());
|
launch_with_props(root_component, (), |c| c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Launches the VirtualDOM from the specified component function and props.
|
/// Launches the VirtualDOM from the specified component function and props.
|
||||||
|
|||||||
Reference in new issue
Block a user