feat: support desktop more completely

This commit is contained in:
Jonathan Kelley 2021-10-05 03:37:15 -04:00
parent a2b0c50a34
commit efd0e9b564
25 changed files with 701 additions and 441 deletions

View file

@ -15,8 +15,7 @@ fn main() {
dioxus::desktop::wry::application::dpi::LogicalSize::new(400.0, 800.0),
)
})
})
.unwrap();
});
}
static App: FC<()> = |cx, props| {

View file

@ -14,13 +14,16 @@ fn main() {
let edits = vec![
// create a container and push it onto the stack
CreateElement { tag: "div", id: 0 },
CreateElement {
tag: "div",
root: 0,
},
// create an element and push it onto the stack
CreateElement { tag: "h1", id: 2 },
CreateElement { tag: "h1", root: 2 },
// create a text node and push it onto the stack
CreateTextNode {
text: "hello world",
id: 3,
root: 3,
},
// append the text node to the h1 element
AppendChildren { many: 1 },
@ -30,7 +33,7 @@ fn main() {
AppendChildren { many: 1 },
];
dioxus_desktop::run(APP, (), |c| c.with_edits(edits)).unwrap();
dioxus_desktop::run(APP, (), |c| c.with_edits(edits));
}
const APP: FC<()> = |cx, _props| {

View file

@ -467,7 +467,7 @@ impl<'bump> DiffMachine<'bump> {
if !has_comitted {
has_comitted = true;
edits.push(PushRoot {
id: root.unwrap().as_u64(),
root: root.unwrap().as_u64(),
});
}
};
@ -1026,14 +1026,6 @@ impl<'bump> DiffMachine<'bump> {
}
}
fn replace_and_create_one_with_many(
&mut self,
old: &'bump VNode<'bump>,
new: &'bump [VNode<'bump>],
) {
//
}
fn replace_and_create_many_with_one(
&mut self,
old: &'bump [VNode<'bump>],
@ -1130,13 +1122,3 @@ impl<'bump> DiffMachine<'bump> {
}
}
}
fn compare_strs(a: &str, b: &str) -> bool {
// Check by pointer, optimizing for static strs
if !std::ptr::eq(a, b) {
// If the pointers are different then check by value
a == b
} else {
true
}
}

View file

@ -39,7 +39,6 @@ pub(crate) struct DiffStack<'bump> {
instructions: Vec<DiffInstruction<'bump>>,
nodes_created_stack: SmallVec<[usize; 10]>,
pub scope_stack: SmallVec<[ScopeId; 5]>,
pub element_id_stack: SmallVec<[ElementId; 5]>,
}
impl<'bump> DiffStack<'bump> {
@ -48,7 +47,6 @@ impl<'bump> DiffStack<'bump> {
instructions: Vec::with_capacity(1000),
nodes_created_stack: smallvec![],
scope_stack: smallvec![],
element_id_stack: smallvec![],
}
}
@ -82,10 +80,6 @@ impl<'bump> DiffStack<'bump> {
self.nodes_created_stack.push(count);
}
pub fn _push_element_id(&mut self, id: ElementId) {
self.element_id_stack.push(id);
}
pub fn create_node(&mut self, node: &'bump VNode<'bump>, and: MountType<'bump>) {
self.nodes_created_stack.push(0);
self.instructions.push(DiffInstruction::Mount { and });

View file

@ -13,8 +13,6 @@ use std::{
cell::{Cell, RefCell},
fmt::Debug,
ops::Deref,
rc::Rc,
sync::Arc,
};
pub use on::*;

View file

@ -23,7 +23,7 @@ impl<'a> Mutations<'a> {
// Navigation
pub(crate) fn push_root(&mut self, root: ElementId) {
let id = root.as_u64();
self.edits.push(PushRoot { id });
self.edits.push(PushRoot { root: id });
}
pub(crate) fn pop(&mut self) {
@ -53,7 +53,7 @@ impl<'a> Mutations<'a> {
// Create
pub(crate) fn create_text_node(&mut self, text: &'a str, id: ElementId) {
let id = id.as_u64();
self.edits.push(CreateTextNode { text, id });
self.edits.push(CreateTextNode { text, root: id });
}
pub(crate) fn create_element(
@ -64,14 +64,14 @@ impl<'a> Mutations<'a> {
) {
let id = id.as_u64();
match ns {
Some(ns) => self.edits.push(CreateElementNs { id, ns, tag }),
None => self.edits.push(CreateElement { id, tag }),
Some(ns) => self.edits.push(CreateElementNs { root: id, ns, tag }),
None => self.edits.push(CreateElement { root: id, tag }),
}
}
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
pub(crate) fn create_placeholder(&mut self, id: ElementId) {
let id = id.as_u64();
self.edits.push(CreatePlaceholder { id });
self.edits.push(CreatePlaceholder { root: id });
}
// events
@ -87,7 +87,7 @@ impl<'a> Mutations<'a> {
self.edits.push(NewEventListener {
scope,
event_name: event,
mounted_node_id: element_id,
root: element_id,
});
}
pub(crate) fn remove_event_listener(&mut self, event: &'static str) {
@ -159,7 +159,7 @@ impl<'a> NodeRefMutation<'a> {
)]
pub enum DomEdit<'bump> {
PushRoot {
id: u64,
root: u64,
},
PopRoot,
@ -187,24 +187,24 @@ pub enum DomEdit<'bump> {
CreateTextNode {
text: &'bump str,
id: u64,
root: u64,
},
CreateElement {
tag: &'bump str,
id: u64,
root: u64,
},
CreateElementNs {
tag: &'bump str,
id: u64,
root: u64,
ns: &'static str,
},
CreatePlaceholder {
id: u64,
root: u64,
},
NewEventListener {
event_name: &'static str,
scope: ScopeId,
mounted_node_id: u64,
root: u64,
},
RemoveEventListener {
event: &'static str,

View file

@ -506,6 +506,8 @@ impl Scheduler {
}
}
log::debug!("work with deadline completed: {:#?}", committed_mutations);
committed_mutations
}

View file

@ -7,7 +7,6 @@ use std::{
future::Future,
pin::Pin,
rc::Rc,
sync::Arc,
};
/// Every component in Dioxus is represented by a `Scope`.

View file

@ -34,10 +34,16 @@ fn test_original_diff() {
assert_eq!(
mutations.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement { id: 1, tag: "div" },
CreateElement {
root: 0,
tag: "div"
},
CreateElement {
root: 1,
tag: "div"
},
CreateTextNode {
id: 2,
root: 2,
text: "Hello, world!"
},
AppendChildren { many: 1 },
@ -73,20 +79,32 @@ fn create() {
assert_eq!(
mutations.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement { id: 1, tag: "div" },
CreateElement {
root: 0,
tag: "div"
},
CreateElement {
root: 1,
tag: "div"
},
CreateTextNode {
id: 2,
root: 2,
text: "Hello, world!"
},
CreateElement { id: 3, tag: "div" },
CreateElement { id: 4, tag: "div" },
CreateElement {
root: 3,
tag: "div"
},
CreateElement {
root: 4,
tag: "div"
},
CreateTextNode {
id: 5,
root: 5,
text: "hello"
},
CreateTextNode {
id: 6,
root: 6,
text: "world"
},
AppendChildren { many: 2 },
@ -115,21 +133,30 @@ fn create_list() {
assert_eq!(
mutations.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement {
root: 0,
tag: "div"
},
CreateTextNode {
id: 1,
root: 1,
text: "hello"
},
AppendChildren { many: 1 },
CreateElement { id: 2, tag: "div" },
CreateElement {
root: 2,
tag: "div"
},
CreateTextNode {
id: 3,
root: 3,
text: "hello"
},
AppendChildren { many: 1 },
CreateElement { id: 4, tag: "div" },
CreateElement {
root: 4,
tag: "div"
},
CreateTextNode {
id: 5,
root: 5,
text: "hello"
},
AppendChildren { many: 1 },
@ -156,10 +183,22 @@ fn create_simple() {
assert_eq!(
mutations.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement { id: 1, tag: "div" },
CreateElement { id: 2, tag: "div" },
CreateElement { id: 3, tag: "div" },
CreateElement {
root: 0,
tag: "div"
},
CreateElement {
root: 1,
tag: "div"
},
CreateElement {
root: 2,
tag: "div"
},
CreateElement {
root: 3,
tag: "div"
},
AppendChildren { many: 4 },
]
);
@ -188,30 +227,39 @@ fn create_components() {
assert_eq!(
mutations.edits,
[
CreateElement { id: 0, tag: "h1" },
CreateElement { id: 1, tag: "div" },
CreateElement { root: 0, tag: "h1" },
CreateElement {
root: 1,
tag: "div"
},
CreateTextNode {
id: 2,
root: 2,
text: "abc1"
},
AppendChildren { many: 1 },
CreateElement { id: 3, tag: "p" },
CreateElement { id: 4, tag: "h1" },
CreateElement { id: 5, tag: "div" },
CreateElement { root: 3, tag: "p" },
CreateElement { root: 4, tag: "h1" },
CreateElement {
root: 5,
tag: "div"
},
CreateTextNode {
id: 6,
root: 6,
text: "abc2"
},
AppendChildren { many: 1 },
CreateElement { id: 7, tag: "p" },
CreateElement { id: 8, tag: "h1" },
CreateElement { id: 9, tag: "div" },
CreateElement { root: 7, tag: "p" },
CreateElement { root: 8, tag: "h1" },
CreateElement {
root: 9,
tag: "div"
},
CreateTextNode {
id: 10,
root: 10,
text: "abc3"
},
AppendChildren { many: 1 },
CreateElement { id: 11, tag: "p" },
CreateElement { root: 11, tag: "p" },
AppendChildren { many: 9 },
]
);
@ -230,13 +278,16 @@ fn anchors() {
assert_eq!(
mutations.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement {
root: 0,
tag: "div"
},
CreateTextNode {
id: 1,
root: 1,
text: "hello"
},
AppendChildren { many: 1 },
CreatePlaceholder { id: 2 },
CreatePlaceholder { root: 2 },
AppendChildren { many: 2 },
]
);
@ -260,6 +311,6 @@ fn suspended() {
assert_eq!(
mutations.edits,
[CreatePlaceholder { id: 0 }, AppendChildren { many: 1 },]
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
);
}

View file

@ -30,9 +30,12 @@ fn html_and_rsx_generate_the_same_output() {
assert_eq!(
create.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement {
root: 0,
tag: "div"
},
CreateTextNode {
id: 1,
root: 1,
text: "Hello world"
},
AppendChildren { many: 1 },
@ -43,7 +46,7 @@ fn html_and_rsx_generate_the_same_output() {
assert_eq!(
change.edits,
[
PushRoot { id: 1 },
PushRoot { root: 1 },
SetText {
text: "Goodbye world"
},
@ -66,21 +69,30 @@ fn fragments_create_properly() {
assert_eq!(
create.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement {
root: 0,
tag: "div"
},
CreateTextNode {
id: 1,
root: 1,
text: "Hello a"
},
AppendChildren { many: 1 },
CreateElement { id: 2, tag: "div" },
CreateElement {
root: 2,
tag: "div"
},
CreateTextNode {
id: 3,
root: 3,
text: "Hello b"
},
AppendChildren { many: 1 },
CreateElement { id: 4, tag: "div" },
CreateElement {
root: 4,
tag: "div"
},
CreateTextNode {
id: 5,
root: 5,
text: "Hello c"
},
AppendChildren { many: 1 },
@ -101,12 +113,15 @@ fn empty_fragments_create_anchors() {
assert_eq!(
create.edits,
[CreatePlaceholder { id: 0 }, AppendChildren { many: 1 }]
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
);
assert_eq!(
change.edits,
[
CreateElement { id: 1, tag: "div" },
CreateElement {
root: 1,
tag: "div"
},
ReplaceWith { m: 1, root: 0 }
]
);
@ -123,16 +138,31 @@ fn empty_fragments_create_many_anchors() {
let (create, change) = dom.lazy_diff(left, right);
assert_eq!(
create.edits,
[CreatePlaceholder { id: 0 }, AppendChildren { many: 1 }]
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
);
assert_eq!(
change.edits,
[
CreateElement { id: 1, tag: "div" },
CreateElement { id: 2, tag: "div" },
CreateElement { id: 3, tag: "div" },
CreateElement { id: 4, tag: "div" },
CreateElement { id: 5, tag: "div" },
CreateElement {
root: 1,
tag: "div"
},
CreateElement {
root: 2,
tag: "div"
},
CreateElement {
root: 3,
tag: "div"
},
CreateElement {
root: 4,
tag: "div"
},
CreateElement {
root: 5,
tag: "div"
},
ReplaceWith { m: 5, root: 0 }
]
);
@ -154,27 +184,36 @@ fn empty_fragments_create_anchors_with_many_children() {
let (create, change) = dom.lazy_diff(left, right);
assert_eq!(
create.edits,
[CreatePlaceholder { id: 0 }, AppendChildren { many: 1 }]
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
);
assert_eq!(
change.edits,
[
CreateElement { id: 1, tag: "div" },
CreateElement {
root: 1,
tag: "div"
},
CreateTextNode {
text: "hello: 0",
id: 2
root: 2
},
AppendChildren { many: 1 },
CreateElement { id: 3, tag: "div" },
CreateElement {
root: 3,
tag: "div"
},
CreateTextNode {
text: "hello: 1",
id: 4
root: 4
},
AppendChildren { many: 1 },
CreateElement { id: 5, tag: "div" },
CreateElement {
root: 5,
tag: "div"
},
CreateTextNode {
text: "hello: 2",
id: 6
root: 6
},
AppendChildren { many: 1 },
ReplaceWith { m: 3, root: 0 }
@ -198,16 +237,22 @@ fn many_items_become_fragment() {
assert_eq!(
create.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement {
root: 0,
tag: "div"
},
CreateTextNode {
text: "hello",
id: 1
root: 1
},
AppendChildren { many: 1 },
CreateElement { id: 2, tag: "div" },
CreateElement {
root: 2,
tag: "div"
},
CreateTextNode {
text: "hello",
id: 3
root: 3
},
AppendChildren { many: 1 },
AppendChildren { many: 2 },
@ -219,7 +264,7 @@ fn many_items_become_fragment() {
change.edits,
[
Remove { root: 2 },
CreatePlaceholder { id: 4 },
CreatePlaceholder { root: 4 },
ReplaceWith { root: 0, m: 1 },
]
);
@ -265,14 +310,14 @@ fn two_fragments_with_differrent_elements_are_differet() {
changes.edits,
[
// create the new h1s
CreateElement { tag: "h1", id: 3 },
CreateElement { tag: "h1", id: 4 },
CreateElement { tag: "h1", id: 5 },
CreateElement { tag: "h1", root: 3 },
CreateElement { tag: "h1", root: 4 },
CreateElement { tag: "h1", root: 5 },
InsertAfter { root: 1, n: 3 },
// replace the divs with new h1s
CreateElement { tag: "h1", id: 6 },
CreateElement { tag: "h1", root: 6 },
ReplaceWith { root: 0, m: 1 },
CreateElement { tag: "h1", id: 7 },
CreateElement { tag: "h1", root: 7 },
ReplaceWith { root: 1, m: 1 },
]
);
@ -296,12 +341,27 @@ fn two_fragments_with_differrent_elements_are_differet_shorter() {
assert_eq!(
create.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement { id: 1, tag: "div" },
CreateElement { id: 2, tag: "div" },
CreateElement { id: 3, tag: "div" },
CreateElement { id: 4, tag: "div" },
CreateElement { id: 5, tag: "p" },
CreateElement {
root: 0,
tag: "div"
},
CreateElement {
root: 1,
tag: "div"
},
CreateElement {
root: 2,
tag: "div"
},
CreateElement {
root: 3,
tag: "div"
},
CreateElement {
root: 4,
tag: "div"
},
CreateElement { root: 5, tag: "p" },
AppendChildren { many: 6 },
]
);
@ -311,9 +371,9 @@ fn two_fragments_with_differrent_elements_are_differet_shorter() {
Remove { root: 2 },
Remove { root: 3 },
Remove { root: 4 },
CreateElement { id: 6, tag: "h1" },
CreateElement { root: 6, tag: "h1" },
ReplaceWith { root: 0, m: 1 },
CreateElement { id: 7, tag: "h1" },
CreateElement { root: 7, tag: "h1" },
ReplaceWith { root: 1, m: 1 },
]
);
@ -337,18 +397,33 @@ fn two_fragments_with_same_elements_are_differet() {
assert_eq!(
create.edits,
[
CreateElement { id: 0, tag: "div" },
CreateElement { id: 1, tag: "div" },
CreateElement { id: 2, tag: "p" },
CreateElement {
root: 0,
tag: "div"
},
CreateElement {
root: 1,
tag: "div"
},
CreateElement { root: 2, tag: "p" },
AppendChildren { many: 3 },
]
);
assert_eq!(
change.edits,
[
CreateElement { id: 3, tag: "div" },
CreateElement { id: 4, tag: "div" },
CreateElement { id: 5, tag: "div" },
CreateElement {
root: 3,
tag: "div"
},
CreateElement {
root: 4,
tag: "div"
},
CreateElement {
root: 5,
tag: "div"
},
InsertAfter { root: 1, n: 3 },
]
);
@ -396,7 +471,7 @@ fn keyed_diffing_out_of_order() {
log::debug!("{:?}", &changes);
assert_eq!(
changes.edits,
[PushRoot { id: 6 }, InsertBefore { root: 4, n: 1 }]
[PushRoot { root: 6 }, InsertBefore { root: 4, n: 1 }]
);
}
@ -421,8 +496,8 @@ fn keyed_diffing_out_of_order_adds() {
assert_eq!(
change.edits,
[
PushRoot { id: 4 },
PushRoot { id: 3 },
PushRoot { root: 4 },
PushRoot { root: 3 },
InsertBefore { n: 2, root: 0 }
]
);
@ -448,8 +523,8 @@ fn keyed_diffing_out_of_order_adds_2() {
assert_eq!(
change.edits,
[
PushRoot { id: 3 },
PushRoot { id: 4 },
PushRoot { root: 3 },
PushRoot { root: 4 },
InsertBefore { n: 2, root: 0 }
]
);
@ -476,8 +551,8 @@ fn keyed_diffing_out_of_order_adds_3() {
assert_eq!(
change.edits,
[
PushRoot { id: 4 },
PushRoot { id: 3 },
PushRoot { root: 4 },
PushRoot { root: 3 },
InsertBefore { n: 2, root: 1 }
]
);
@ -504,8 +579,8 @@ fn keyed_diffing_out_of_order_adds_4() {
assert_eq!(
change.edits,
[
PushRoot { id: 4 },
PushRoot { id: 3 },
PushRoot { root: 4 },
PushRoot { root: 3 },
InsertBefore { n: 2, root: 2 }
]
);
@ -531,7 +606,7 @@ fn keyed_diffing_out_of_order_adds_5() {
let (_, change) = dom.lazy_diff(left, right);
assert_eq!(
change.edits,
[PushRoot { id: 4 }, InsertBefore { n: 1, root: 3 }]
[PushRoot { root: 4 }, InsertBefore { n: 1, root: 3 }]
);
}
@ -555,8 +630,14 @@ fn keyed_diffing_additions() {
assert_eq!(
change.edits,
[
CreateElement { id: 5, tag: "div" },
CreateElement { id: 6, tag: "div" },
CreateElement {
root: 5,
tag: "div"
},
CreateElement {
root: 6,
tag: "div"
},
InsertAfter { n: 2, root: 4 }
]
);
@ -584,11 +665,17 @@ fn keyed_diffing_additions_and_moves_on_ends() {
change.edits,
[
// create 11, 12
CreateElement { tag: "div", id: 4 },
CreateElement { tag: "div", id: 5 },
CreateElement {
tag: "div",
root: 4
},
CreateElement {
tag: "div",
root: 5
},
InsertAfter { root: 2, n: 2 },
// move 7 to the front
PushRoot { id: 3 },
PushRoot { root: 3 },
InsertBefore { root: 0, n: 1 }
]
);
@ -617,15 +704,27 @@ fn keyed_diffing_additions_and_moves_in_middle() {
change.edits,
[
// create 13, 17
CreateElement { tag: "div", id: 4 },
CreateElement { tag: "div", id: 5 },
CreateElement {
tag: "div",
root: 4
},
CreateElement {
tag: "div",
root: 5
},
InsertBefore { root: 1, n: 2 },
// create 11, 12
CreateElement { tag: "div", id: 6 },
CreateElement { tag: "div", id: 7 },
CreateElement {
tag: "div",
root: 6
},
CreateElement {
tag: "div",
root: 7
},
InsertBefore { root: 2, n: 2 },
// move 7
PushRoot { id: 3 },
PushRoot { root: 3 },
InsertBefore { root: 0, n: 1 }
]
);
@ -654,15 +753,21 @@ fn controlled_keyed_diffing_out_of_order() {
changes.edits,
[
// move 4 to after 6
PushRoot { id: 0 },
PushRoot { root: 0 },
InsertAfter { n: 1, root: 2 },
// remove 7
// create 9 and insert before 6
CreateElement { id: 4, tag: "div" },
CreateElement {
root: 4,
tag: "div"
},
InsertBefore { n: 1, root: 2 },
// create 0 and insert before 5
CreateElement { id: 5, tag: "div" },
CreateElement {
root: 5,
tag: "div"
},
InsertBefore { n: 1, root: 1 },
]
);
@ -689,9 +794,12 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
assert_eq!(
changes.edits,
[
CreateElement { id: 5, tag: "div" },
CreateElement {
root: 5,
tag: "div"
},
InsertBefore { n: 1, root: 2 },
PushRoot { id: 3 },
PushRoot { root: 3 },
InsertBefore { n: 1, root: 0 },
]
);
@ -711,6 +819,6 @@ fn suspense() {
}));
assert_eq!(
edits.edits,
[CreatePlaceholder { id: 0 }, AppendChildren { many: 1 }]
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
);
}

View file

@ -34,7 +34,7 @@ fn shared_state_test() {
edits,
[
CreateTextNode {
id: 0,
root: 0,
text: "Hello, world!"
},
AppendChildren { many: 1 },

View file

@ -1,4 +1,3 @@
{
"rust-analyzer.inlayHints.enable": true,
"rust-analyzer.cargo.allFeatures": true
}

View file

@ -0,0 +1 @@
WebviewWindow

View file

@ -9,35 +9,26 @@ license = "MIT/Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# web-view = { git = "https://github.com/Boscop/web-view" }
dioxus-core = { path = "../core", version = "0.1.2", features = ["serialize"] }
anyhow = "1.0"
argh = "0.1.4"
serde = "1.0.120"
serde_json = "1.0.61"
thiserror = "1.0.23"
log = "0.4.13"
fern = { version = "0.6.0", features = ["colored"] }
html-escape = "0.2.9"
# wry = { version = "0.12.2", git = "https://github.com/jkelleyrtp/wry.git", branch = "jk/fnmut_rpc" }
wry = "0.12.2"
tokio = { version = "1.12.0", features = ["full"] }
futures-channel = "0.3.16"
tokio = { version = "1.12.0", features = ["full"], optional = true }
dioxus-core-macro = { path = "../core-macro" }
[features]
default = ["tokio_runtime"]
tokio_runtime = ["tokio"]
[dev-dependencies]
dioxus-html = { path = "../html" }
dioxus-core-macro = { path = "../core-macro" }
dioxus-hooks = { path = "../hooks" }
# thiserror = "1.0.23"
# log = "0.4.13"
# fern = { version = "0.6.0", features = ["colored"] }
# wasm-bindgen-cli-support = "0.2.71"
# anyhow = "1.0.38"
# argh = "0.1.4"
# async-std = { version = "1.9.0", features = ["attributes"] }
# serde = "1.0.120"
# serde_json = "1.0.61"
[build-dependencies]

View file

@ -1,26 +1,148 @@
# Dioxus-webview
# Dioxus-Desktop
Dioxus-webview bridges virtual and Webview DOMs together to make simple, portable, desktop applications.
Dioxus-webview is an attempt at making a simpler "Tauri" where creating desktop applications is as simple as:
This crate provides an ergonomic API for Dioxus to build desktop apps.
```rust
// main.rs
fn main() {
dioxus_desktop::new(App, |c| c)
.launch()
.await;
dioxus::desktop::launch(App, |c| c)
}
static App: FC<()> = |cx, props|{
static App: FC<()> = |cx, props| {
let (count, set_count) = use_state(cx, || 0);
rsx!(cx, div {
h1 { "Dioxus Desktop Demo" }
p { "Count is {count}"}
button { onclick: move |_| count += 1}
})
cx.render(rsx!(
WebviewWindow {
onclose: move |e| log::info!("save our counter state to disk"),
div {
h1 { "Dioxus Desktop Demo" }
p { "Count is {count}"}
button { onclick: move |_| count += 1}
}
}
))
}
```
Window management, system trays, notifications, and other desktop-related functionality is managed using the declarative Dioxus API, making it easy to add new features without having to jump through hoops.
## Features
- Your rust code runs natively and under a Tokio runtime
- Declarative application management (dedicated components for windows, models, handlers, task tray, etc)
- Cross platform (runs on Mac, Linux, Windows, etc and mobile through the dioxus-mobile sub crate)
## Managing Windows
Managing windows is done by simply rendering content into a `WebviewWindow` component.
```rust
static App: FC<()> = |cx, props| {
rsx!(cx, WebviewWindow { "hello world" } )
}
```
This will create a new window with only the content "hello world". As this crate matures, we'll have new types of windows for different functionality.
## Managing Notifications
Notifications also use a declarative approach. Sending a notification has never been easier!
The api has been somewhat modeled after https://github.com/mikaelbr/node-notifier
```rust
static Notifications: FC<()> = |cx, props| {
cx.render(rsx!(
Notification {
title: "title"
subtitle: "subtitle"
message: "message"
sound: "Basso"
icon: "Terminal"
contentImage: "image.png"
open: "https://github.com"
wait: true,
timeout: 5,
closeLabel: "Cancel"
actions: ["send", "receive"]
dropdownLabel: "messaging"
reply: true
onclose: move |e| {}
onreply: move |e| {}
ondropdownselected: move |e| {}
ontimeout: move |e| {}
onerror: move |e| {}
}
))
}
```
## App Tray
Dioxus Desktop supports app trays, which can be built with native menu groups or with a custom window.
```rust
static Tray: FC<()> = |cx, props| {
cx.render(rsx!(
GlobalTray {
MenuGroup {
MenuGroupItem { title: "New File", shortcut: "cmd+N", onclick: move |e| {} }
MenuGroupItem { title: "New Window", shortcut: "shift+cmd+N", onclick: move |e| {} }
}
}
))
};
// using a builder
static Tray: FC<()> = |cx, props| {
let menu = MenuGroup::builder(cx)
.with_items([
MenuGroupItem::builder()
.title()
.shortcut()
.onclick(move |e| {}),
MenuGroupItem::builder()
.title()
.shortcut()
.onclick(move |e| {})
]).build();
rsx!(cx, GlobalTray { rawmenu: menu })
}
// or with a custom window
static Tray: FC<()> = |cx, props| {
rsx!(cx, GlobalTray { div { "custom buttons here" } })
};
```
## Menu Bar
Declaring menus is convenient and cross-platform.
```rust
static Menu: FC<()> = |cx, props| {
cx.render(rsx!(
MenuBarMajorItem { title: "File"
MenuGroup {
MenuGroupItem { title: "New File", shortcut: "cmd+N", onclick: move |e| {} }
MenuGroupItem { title: "New Window", shortcut: "shift+cmd+N", onclick: move |e| {} }
}
MenuGroup {
MenuGroupList {
title: "Open Recent", shortcut: "cmd+N"
MenuGroup {
(recent_items.iter().map(|file| rsx!(
MenuGroupItem {
onclick: move |_| open_file(file),
title: "{file}"
}
)))
}
}
}
}
))
};
```
## Building, bundling, etc
and then to create a native .app:
```
@ -29,17 +151,6 @@ dioxus bundle --platform macOS
## Goals
Because the host VirtualDOM is running in its own native process, native applications can unlock their full potential. Dioxus-webview is designed to be a 100% rust alternative to ElectronJS without the memory overhead or bloat of ElectronJS apps.
By bridging the native process, desktop apps can access full multithreading power, peripheral support, hardware access, and native filesystem controls without the hassle of web technologies. Our goal with Dioxus-webview is to make it easy to ship both a web and native application, and quickly see large performance boosts without having to re-write the whole stack. As the dioxus ecosystem grows, we hope to see 3rd parties providing wrappers for storage, offline mode, etc that supports both web and native technologies.
## Tech
Dioxus-desktop is a pure liveview application where all of the state and event handlers are proxied through the liveview and into the native process. For pure server-based liveview, this would normally be too slow (in both render performance and latency), but because the VDom is local, desktop apps are just as fast as Electron.
Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime _is_ needed - we recommend async-std in Tokio mode.
## Async Runtime
Because the host VirtualDOM is running in its own native process, native applications can unlock their full potential. Dioxus-Desktop is designed to be a 100% rust alternative to ElectronJS without the memory overhead or bloat of ElectronJS apps.
By bridging the native process, desktop apps can access full multithreading power, peripheral support, hardware access, and native filesystem controls without the hassle of web technologies. Our goal with this desktop crate is to make it easy to ship both a web and native application, and quickly see large performance boosts without having to re-write the whole stack. As the dioxus ecosystem grows, we hope to see 3rd parties providing wrappers for storage, offline mode, etc that supports both web and native technologies.

View file

@ -2,32 +2,4 @@ use dioxus_core::prelude::*;
use dioxus_core_macro::*;
use dioxus_html as dioxus_elements;
fn main() {
let (window_loop, tasks) = dioxus_desktop::start(App, |c| c);
std::thread::spawn(move || {
//
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
runtime.block_on(async move {
let mut vir = VirtualDom::new_with_props(root, props);
let channel = vir.get_event_sender();
loop {
vir.wait_for_work().await;
let edits = vir.run_with_deadline(|| false);
let edit_string = serde_json::to_string(&edits[0].edits).unwrap();
event_tx.send(edit_string).unwrap();
}
})
});
window_loop.run();
}
static App: FC<()> = |cx| {
//
cx.render(rsx!(div {}))
};
fn main() {}

View file

@ -2,12 +2,10 @@ use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_core_macro::*;
use dioxus_hooks::*;
use dioxus_html as dioxus_elements;
fn main() {
dioxus_desktop::set_up_logging(true);
dioxus_desktop::launch(App, |c| c).unwrap();
dioxus_desktop::launch(App, |c| c)
}
enum Scene {

View file

@ -0,0 +1,8 @@
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_core_macro::*;
use dioxus_hooks::*;
use dioxus_html as dioxus_elements;
fn main() {}

View file

@ -1,11 +1 @@
fn main() {
tauri::AppBuilder::default().setup(move |app, name| {
//
let window = app.get_window();
let window = app.get_window();
tauri::spawn(|| async move {
//
});
});
}
fn main() {}

View file

@ -0,0 +1,106 @@
use std::cell::RefCell;
use dioxus_core as dioxus;
use dioxus_core::{Context, DomTree, LazyNodes, NodeFactory, Properties};
use dioxus_core_macro::Props;
/*
This module provides a set of Dioxus components to easily manage windows, tabs, etc.
Windows can be created anywhere in the tree, making them very flexible for things like modals, etc.
*/
pub struct DesktopContext {}
impl DesktopContext {
fn add_window(&mut self) {
//
}
fn close_window(&mut self) {
//
}
}
enum WindowHandlers {
Resized(Box<dyn Fn()>),
Moved(Box<dyn Fn()>),
CloseRequested(Box<dyn Fn()>),
Destroyed(Box<dyn Fn()>),
DroppedFile(Box<dyn Fn()>),
HoveredFile(Box<dyn Fn()>),
HoverFileCancelled(Box<dyn Fn()>),
ReceivedTimeText(Box<dyn Fn()>),
Focused(Box<dyn Fn()>),
}
#[derive(Props)]
pub struct WebviewWindowProps<'a> {
onclose: &'a dyn FnMut(()),
onopen: &'a dyn FnMut(()),
/// focuse me
onfocused: &'a dyn FnMut(()),
}
/// A handle to a
///
///
///
///
///
///
///
///
///
pub fn WebviewWindow<'a>(cx: Context<'a>, props: &'a WebviewWindowProps) -> DomTree<'a> {
let dtcx = cx.use_consume_state::<RefCell<DesktopContext>>()?;
cx.use_hook(
|_| {
//
},
|state| {
//
},
|hook| {
//
},
);
// render the children directly
cx.render(LazyNodes::new(move |f: NodeFactory| {
f.fragment_from_iter(cx.children())
}))
}
pub struct WindowHandle {}
/// Get a handle to the current window from inside a component
pub fn use_current_window(cx: Context) -> Option<WindowHandle> {
todo!()
}
#[test]
fn syntax_works() {
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_core_macro::*;
use dioxus_hooks::*;
use dioxus_html as dioxus_elements;
static App: FC<()> = |cx, props| {
cx.render(rsx! {
// left window
WebviewWindow {
onclose: move |evt| {}
onopen: move |evt| {}
onfocused: move |evt| {}
div {
}
}
})
};
}

View file

View file

@ -7,7 +7,7 @@ class Interpreter {
"onclick": {}
};
this.lastNodeWasText = false;
this.nodes = [root, root, root, root];
this.nodes = [root];
}
top() {
@ -19,9 +19,8 @@ class Interpreter {
}
PushRoot(edit) {
const id = edit.id;
const id = edit.root;
const node = this.nodes[id];
console.log("pushing root ", node, "with id", id);
this.stack.push(node);
}
@ -52,34 +51,36 @@ class Interpreter {
}
Remove(edit) {
let node = this.nodes[edit.element_id];
node.remove();
let node = this.nodes[edit.root];
if (node !== undefined) {
node.remove();
}
}
CreateTextNode(edit) {
const node = document.createTextNode(edit.text);
this.nodes[edit.id] = node;
this.nodes[edit.root] = node;
this.stack.push(node);
}
CreateElement(edit) {
const tagName = edit.tag;
const el = document.createElement(tagName);
this.nodes[edit.id] = el;
this.nodes[edit.root] = el;
this.stack.push(el);
}
CreateElementNs(edit) {
let el = document.createElementNS(edit.ns, edit.tag);
this.stack.push(el);
this.nodes[edit.id] = el;
this.nodes[edit.root] = el;
}
CreatePlaceholder(edit) {
let el = document.createElement("pre");
// let el = document.createComment("vroot");
this.stack.push(el);
this.nodes[edit.id] = el;
this.nodes[edit.root] = el;
}
RemoveEventListener(edit) { }
@ -127,30 +128,31 @@ class Interpreter {
}
InsertAfter(edit) {
let old = this.nodes[edit.element_id];
let new_nodes = this.stack.splice(edit.many);
let old = this.nodes[edit.root];
let new_nodes = this.stack.splice(this.stack.length - edit.n);
// console.log("inserting nodes after", new_nodes, old);
old.after(...new_nodes);
}
InsertBefore(edit) {
let old = this.nodes[edit.element_id];
let new_nodes = this.stack.splice(edit.many);
let old = this.nodes[edit.root];
let new_nodes = this.stack.splice(this.stack.length - edit.n);
old.before(...new_nodes);
}
NewEventListener(edit) {
const event_name = edit.event_name;
const mounted_node_id = edit.mounted_node_id;
const mounted_node_id = edit.root;
const scope = edit.scope;
const element = this.top();
element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`);
console.log("listener map is", this.listeners);
if (this.listeners[event_name] === undefined) {
console.log("adding listener!");
this.listeners[event_name] = "bla";
this.root.addEventListener(event_name, (event) => {
console.log("CLICKED");
const target = event.target;
const val = target.getAttribute(`dioxus-event-${event_name}`);
if (val == null) {
@ -161,8 +163,9 @@ class Interpreter {
const scope_id = parseInt(fields[0]);
const real_id = parseInt(fields[1]);
console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
// console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
console.log("message fired");
let contents = serialize_event(event);
rpc.call('user_event', {
event: event_name,
@ -170,33 +173,31 @@ class Interpreter {
mounted_dom_id: real_id,
contents: contents,
}).then((reply) => {
console.log(reply);
console.log("reply received");
// console.log(reply);
this.stack.push(this.root);
let edits = reply.edits;
for (let x = 0; x < edits.length; x++) {
let edit = edits[x];
console.log(edit);
let f = this[edit.type];
f.call(this, edit);
}
console.log("initiated");
}).catch((err) => {
console.log("failed to initiate", err);
});
// console.log("initiated");
})
});
}
}
}
async function initialize() {
const reply = await rpc.call('initiate');
let root = window.document.getElementById("_dioxusroot");
const interpreter = new Interpreter(root);
console.log(reply);
const reply = await rpc.call('initiate');
let pre_rendered = reply.pre_rendered;
if (pre_rendered !== undefined) {
@ -209,14 +210,14 @@ async function initialize() {
}
function apply_edits(edits, interpreter) {
console.log(edits);
for (let x = 0; x < edits.length; x++) {
let edit = edits[x];
console.log(edit);
let f = interpreter[edit.type];
f.call(interpreter, edit);
}
console.log("stack completed: ", interpreter.stack);
// console.log("stack completed: ", interpreter.stack);
}
function serialize_event(event) {

View file

@ -4,7 +4,8 @@
//!
use std::borrow::BorrowMut;
use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::sync::atomic::AtomicBool;
@ -14,15 +15,11 @@ use std::sync::{Arc, RwLock};
use cfg::DesktopConfig;
use dioxus_core::scheduler::SchedulerMsg;
use dioxus_core::*;
// use futures_channel::mpsc::UnboundedSender;
use serde::{Deserialize, Serialize};
mod logging;
pub use logging::set_up_logging;
pub use wry;
use wry::application::event::{Event, WindowEvent};
use wry::application::event::{Event, StartCause, WindowEvent};
use wry::application::event_loop::{self, ControlFlow, EventLoop};
use wry::application::window::Fullscreen;
use wry::webview::{WebView, WebViewBuilder};
@ -32,6 +29,7 @@ use wry::{
};
mod cfg;
mod desktop_context;
mod dom;
mod escape;
mod events;
@ -41,7 +39,7 @@ static HTML_CONTENT: &'static str = include_str!("./index.html");
pub fn launch(
root: FC<()>,
config_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
) -> anyhow::Result<()> {
) {
launch_with_props(root, (), config_builder)
}
@ -49,7 +47,7 @@ pub fn launch_with_props<P: Properties + 'static + Send + Sync>(
root: FC<P>,
props: P,
builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
) -> anyhow::Result<()> {
) {
run(root, props, builder)
}
@ -58,6 +56,7 @@ enum RpcEvent<'a> {
Initialize { edits: Vec<DomEdit<'a>> },
}
#[derive(Debug)]
enum BridgeEvent {
Initialize(serde_json::Value),
Update(serde_json::Value),
@ -69,11 +68,12 @@ struct Response<'a> {
edits: Vec<DomEdit<'a>>,
}
pub fn run<T: Properties + 'static + Send + Sync>(
pub fn run<T: 'static + Send + Sync>(
root: FC<T>,
props: T,
user_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
) -> anyhow::Result<()> {
) {
// Generate the config
let mut cfg = DesktopConfig::new();
user_builder(&mut cfg);
let DesktopConfig {
@ -83,67 +83,95 @@ pub fn run<T: Properties + 'static + Send + Sync>(
..
} = cfg;
// All of our webview windows are stored in a way that we can look them up later
// The "DesktopContext" will provide functionality for spawning these windows
let mut webviews = HashMap::new();
let event_loop = EventLoop::new();
let window = window.build(&event_loop)?;
let (event_tx, mut event_rx) = tokio::sync::mpsc::unbounded_channel();
let props_shared = Cell::new(Some(props));
let sender = launch_vdom_with_tokio(root, props, event_tx.clone());
event_loop.run(move |event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
let locked_receiver = Rc::new(RefCell::new(event_rx));
match event {
Event::NewEvents(StartCause::Init) => {
let window = WindowBuilder::new().build(&event_loop).unwrap();
let window_id = window.id();
let webview = WebViewBuilder::new(window)?
.with_url("wry://index.html")?
.with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
//
match req.method.as_str() {
"initiate" => {
let mut rx = (*locked_receiver).borrow_mut();
match rx.try_recv() {
Ok(BridgeEvent::Initialize(edits)) => {
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel();
let my_props = props_shared.take().unwrap();
let sender = launch_vdom_with_tokio(root, my_props, event_tx.clone());
let locked_receiver = Rc::new(RefCell::new(event_rx));
let webview = WebViewBuilder::new(window)
.unwrap()
.with_url("wry://index.html")
.unwrap()
.with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
let mut rx = (*locked_receiver).borrow_mut();
match req.method.as_str() {
"initiate" => {
if let Ok(BridgeEvent::Initialize(edits)) = rx.try_recv() {
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
} else {
None
}
}
"user_event" => {
let event = events::trigger_from_serialized(req.params.unwrap());
sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap();
if let Some(BridgeEvent::Update(edits)) = rx.blocking_recv() {
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
} else {
None
}
}
_ => None,
}
_ => None,
}
}
"user_event" => {
let data = req.params.unwrap();
let event = events::trigger_from_serialized(data);
sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap();
})
// Any content that that uses the `wry://` scheme will be shuttled through this handler as a "special case"
// For now, we only serve two pieces of content which get included as bytes into the final binary.
.with_custom_protocol("wry".into(), move |request| {
let path = request.uri().replace("wry://", "");
let (data, meta) = match path.as_str() {
"index.html" => (include_bytes!("./index.html").to_vec(), "text/html"),
"index.html/index.js" => {
(include_bytes!("./index.js").to_vec(), "text/javascript")
}
_ => unimplemented!("path {}", path),
};
let mut rx = (*locked_receiver).borrow_mut();
match rx.blocking_recv() {
Some(BridgeEvent::Update(edits)) => {
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
}
_ => None,
}
}
_ => todo!("this message failed"),
wry::http::ResponseBuilder::new().mimetype(meta).body(data)
})
.build()
.unwrap();
webviews.insert(window_id, webview);
}
})
// this isn't quite portable unfortunately :(
// todo: figure out a way to allow us to create the index.html with the index.js file separately
// it's a bit easier to hack with
.with_custom_protocol("wry".into(), move |request| {
use std::fs::{canonicalize, read};
use wry::http::ResponseBuilder;
// Remove url scheme
let path = request.uri().replace("wry://", "");
let (data, meta) = match path.as_str() {
"index.html" => (include_bytes!("./index.html").to_vec(), "text/html"),
"index.html/index.js" => (include_bytes!("./index.js").to_vec(), "text/javascript"),
_ => unimplemented!("path {}", path),
};
Event::WindowEvent {
event, window_id, ..
} => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(_) | WindowEvent::Moved(_) => {
if let Some(view) = webviews.get_mut(&window_id) {
let _ = view.resize();
}
}
// TODO: we want to shuttle all of these events into the user's app
_ => {}
},
ResponseBuilder::new().mimetype(meta).body(data)
})
.build()?;
Event::MainEventsCleared => {}
Event::Resumed => {}
Event::Suspended => {}
Event::LoopDestroyed => {}
run_event_loop(event_loop, webview, event_tx);
Ok(())
_ => {}
}
})
}
pub fn start<P: 'static + Send>(
@ -155,17 +183,14 @@ pub fn start<P: 'static + Send>(
}
// Create a new tokio runtime on a dedicated thread and then launch the apps VirtualDom.
fn launch_vdom_with_tokio<C: Send + 'static>(
root: FC<C>,
props: C,
pub(crate) fn launch_vdom_with_tokio<P: Send + 'static>(
root: FC<P>,
props: P,
event_tx: tokio::sync::mpsc::UnboundedSender<BridgeEvent>,
) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
// Spawn the virtualdom onto its own thread
// if it wants to spawn multithreaded tasks, it can use the executor directly
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
let return_sender = sender.clone();
let sender_2 = sender.clone();
std::thread::spawn(move || {
// We create the runtim as multithreaded, so you can still "spawn" onto multiple threads
let runtime = tokio::runtime::Builder::new_multi_thread()
@ -186,58 +211,29 @@ fn launch_vdom_with_tokio<C: Send + 'static>(
}
let edit_string = serde_json::to_value(Evt { edits: edits.edits }).unwrap();
match event_tx.send(BridgeEvent::Initialize(edit_string)) {
Ok(_) => {}
Err(_) => {}
}
event_tx
.send(BridgeEvent::Initialize(edit_string))
.expect("Sending should not fail");
loop {
vir.wait_for_work().await;
let mut muts = vir.run_with_deadline(|| false);
while let Some(edit) = muts.pop() {
let edit_string = serde_json::to_value(Evt { edits: edit.edits }).unwrap();
match event_tx.send(BridgeEvent::Update(edit_string)) {
Ok(_) => {}
Err(er) => {
log::error!("Sending should not fail {}", er);
}
}
}
// we're running on our own thread, so we don't need to worry about blocking anything
// todo: maybe we want to schedule ourselves in
// on average though, the virtualdom running natively is stupid fast
log::info!("mutations sent on channel");
let mut muts = vir.run_with_deadline(|| false);
while let Some(edit) = muts.pop() {
let edit_string = serde_json::to_value(Evt { edits: edit.edits })
.expect("serializing edits should never fail");
event_tx
.send(BridgeEvent::Update(edit_string))
.expect("Sending should not fail");
}
}
})
});
sender_2
}
fn run_event_loop(
event_loop: EventLoop<()>,
webview: WebView,
event_tx: tokio::sync::mpsc::UnboundedSender<BridgeEvent>,
) {
let _ = event_tx.clone();
event_loop.run(move |event, target, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event, window_id, ..
} => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(_) | WindowEvent::Moved(_) => {
let _ = webview.resize();
}
_ => {}
},
Event::MainEventsCleared => {
webview.resize();
// window.request_redraw();
}
_ => {}
}
})
return_sender
}

View file

@ -1,51 +0,0 @@
pub fn set_up_logging(enabled: bool) {
use fern::colors::{Color, ColoredLevelConfig};
if !enabled {
return;
}
// configure colors for the whole line
let colors_line = ColoredLevelConfig::new()
.error(Color::Red)
.warn(Color::Yellow)
// we actually don't need to specify the color for debug and info, they are white by default
.info(Color::White)
.debug(Color::White)
// depending on the terminals color scheme, this is the same as the background color
.trace(Color::BrightBlack);
// configure colors for the name of the level.
// since almost all of them are the same as the color for the whole line, we
// just clone `colors_line` and overwrite our changes
let colors_level = colors_line.clone().info(Color::Green);
// here we set up our fern Dispatch
// when running tests in batch, the logger is re-used, so ignore the logger error
let _ = fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{color_line}[{level}{color_line}] {message}\x1B[0m",
color_line = format_args!(
"\x1B[{}m",
colors_line.get_color(&record.level()).to_fg_str()
),
level = colors_level.color(record.level()),
message = message,
));
})
// set the default log level. to filter out verbose log messages from dependencies, set
// this to Warn and overwrite the log level for your crate.
.level(log::LevelFilter::Debug)
// .level(log::LevelFilter::Warn)
// change log levels for individual modules. Note: This looks for the record's target
// field which defaults to the module path but can be overwritten with the `target`
// parameter:
// `info!(target="special_target", "This log message is about special_target");`
// .level_for("dioxus", log::LevelFilter::Debug)
// .level_for("dioxus", log::LevelFilter::Info)
// .level_for("pretty_colored", log::LevelFilter::Trace)
// output to stdout
.chain(std::io::stdout())
.apply();
}

View file

@ -105,19 +105,21 @@ impl WebsysDom {
pub fn process_edits(&mut self, edits: &mut Vec<DomEdit>) {
for edit in edits.drain(..) {
match edit {
DomEdit::PushRoot { id: root } => self.push(root),
DomEdit::PushRoot { root } => self.push(root),
DomEdit::PopRoot => self.pop(),
DomEdit::AppendChildren { many } => self.append_children(many),
DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
DomEdit::Remove { root } => self.remove(root),
DomEdit::CreateTextNode { text, id } => self.create_text_node(text, id),
DomEdit::CreateElement { tag, id } => self.create_element(tag, None, id),
DomEdit::CreateElementNs { tag, id, ns } => self.create_element(tag, Some(ns), id),
DomEdit::CreatePlaceholder { id } => self.create_placeholder(id),
DomEdit::CreateTextNode { text, root: id } => self.create_text_node(text, id),
DomEdit::CreateElement { tag, root: id } => self.create_element(tag, None, id),
DomEdit::CreateElementNs { tag, root: id, ns } => {
self.create_element(tag, Some(ns), id)
}
DomEdit::CreatePlaceholder { root: id } => self.create_placeholder(id),
DomEdit::NewEventListener {
event_name,
scope,
mounted_node_id,
root: mounted_node_id,
} => self.new_event_listener(event_name, scope, mounted_node_id),
DomEdit::RemoveEventListener { event } => todo!(),