mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-17 06:08:26 +00:00
finishing touches and benchmarks
This commit is contained in:
parent
05d04165ba
commit
5b25500c0b
15 changed files with 305 additions and 206 deletions
|
@ -28,6 +28,8 @@ dioxus-tui = { path = "./packages/tui", version = "^0.2.0", optional = true }
|
|||
|
||||
dioxus-liveview = { path = "./packages/liveview", optional = true }
|
||||
|
||||
dioxus-native-core = { path = "./packages/native-core", optional = true }
|
||||
|
||||
# dioxus-mobile = { path = "./packages/mobile", version = "^0.2.0", optional = true }
|
||||
# dioxus-rsx = { path = "./packages/rsx", optional = true }
|
||||
# macro = ["dioxus-core-macro", "dioxus-rsx"]
|
||||
|
@ -45,6 +47,7 @@ ayatana = ["dioxus-desktop/ayatana"]
|
|||
router = ["dioxus-router"]
|
||||
tui = ["dioxus-tui"]
|
||||
liveview = ["dioxus-liveview"]
|
||||
native-core = ["dioxus-native-core"]
|
||||
|
||||
|
||||
[workspace]
|
||||
|
@ -90,5 +93,6 @@ harness = false
|
|||
name = "jsframework"
|
||||
harness = false
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
[[bench]]
|
||||
name = "tui_update"
|
||||
harness = false
|
196
benches/tui_update.rs
Normal file
196
benches/tui_update.rs
Normal file
|
@ -0,0 +1,196 @@
|
|||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_tui::TuiContext;
|
||||
|
||||
criterion_group!(mbenches, update_boxes);
|
||||
criterion_main!(mbenches);
|
||||
|
||||
/// This benchmarks the cache performance of the TUI for small edits by changing one box at a time.
|
||||
fn update_boxes(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("Update boxes");
|
||||
// We can override the configuration on a per-group level
|
||||
group.sample_size(10);
|
||||
|
||||
// We can also use loops to define multiple benchmarks, even over multiple dimensions.
|
||||
for size in 1..=6 {
|
||||
let parameter_string = format!("{}", 5 * size);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("size", parameter_string),
|
||||
&size,
|
||||
|b, size| {
|
||||
b.iter(|| match size {
|
||||
1 => dioxus::tui::launch(app5),
|
||||
2 => dioxus::tui::launch(app10),
|
||||
3 => dioxus::tui::launch(app15),
|
||||
4 => dioxus::tui::launch(app20),
|
||||
5 => dioxus::tui::launch(app25),
|
||||
6 => dioxus::tui::launch(app30),
|
||||
_ => (),
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Props, PartialEq)]
|
||||
struct BoxProps {
|
||||
x: usize,
|
||||
y: usize,
|
||||
hue: f32,
|
||||
alpha: f32,
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
fn Box(cx: Scope<BoxProps>) -> Element {
|
||||
let count = use_state(&cx, || 0);
|
||||
|
||||
let x = cx.props.x * 2;
|
||||
let y = cx.props.y * 2;
|
||||
let hue = cx.props.hue;
|
||||
let display_hue = cx.props.hue as u32 / 10;
|
||||
let count = count.get();
|
||||
let alpha = cx.props.alpha + (count % 100) as f32;
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
left: "{x}%",
|
||||
top: "{y}%",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
background_color: "hsl({hue}, 100%, 50%, {alpha}%)",
|
||||
align_items: "center",
|
||||
p{"{display_hue:03}"}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Props, PartialEq)]
|
||||
struct GridProps {
|
||||
size: usize,
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
fn Grid(cx: Scope<GridProps>) -> Element {
|
||||
let size = cx.props.size;
|
||||
let count = use_state(&cx, || 0);
|
||||
let counts = use_ref(&cx, || vec![0; size * size]);
|
||||
|
||||
let ctx: TuiContext = cx.consume_context().unwrap();
|
||||
if *count.get() + 1 >= (size * size) {
|
||||
ctx.quit();
|
||||
} else {
|
||||
counts.with_mut(|c| {
|
||||
let i = *count.current();
|
||||
c[i] += 1;
|
||||
c[i] = c[i] % 360;
|
||||
});
|
||||
count.with_mut(|i| {
|
||||
*i += 1;
|
||||
*i = *i % (size * size);
|
||||
});
|
||||
}
|
||||
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
flex_direction: "column",
|
||||
(0..size).map(|x|
|
||||
{
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
flex_direction: "row",
|
||||
(0..size).map(|y|
|
||||
{
|
||||
let alpha = y as f32*100.0/size as f32 + counts.read()[x*size + y] as f32;
|
||||
let key = format!("{}-{}", x, y);
|
||||
cx.render(rsx! {
|
||||
Box{
|
||||
x: x,
|
||||
y: y,
|
||||
alpha: 100.0,
|
||||
hue: alpha,
|
||||
key: "{key}",
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn app5(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
Grid{
|
||||
size: 5,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn app10(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
Grid{
|
||||
size: 10,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn app15(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
Grid{
|
||||
size: 15,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn app20(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
Grid{
|
||||
size: 20,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn app25(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
Grid{
|
||||
size: 25,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn app30(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
Grid{
|
||||
size: 30,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus::tui::launch_cfg(
|
||||
app,
|
||||
dioxus::tui::Config {
|
||||
rendering_mode: dioxus::tui::RenderingMode::Rgb,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Props, PartialEq)]
|
||||
struct BoxProps {
|
||||
x: i32,
|
||||
y: i32,
|
||||
hue: f32,
|
||||
alpha: f32,
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
fn Box(cx: Scope<BoxProps>) -> Element {
|
||||
let count = use_state(&cx, || 0);
|
||||
|
||||
use_future(&cx, (), move |_| {
|
||||
let count = count.to_owned();
|
||||
let update = cx.schedule_update();
|
||||
async move {
|
||||
loop {
|
||||
count.with_mut(|i| *i += 1);
|
||||
tokio::time::sleep(std::time::Duration::from_millis(800)).await;
|
||||
update();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let x = cx.props.x * 2;
|
||||
let y = cx.props.y * 2;
|
||||
let hue = cx.props.hue;
|
||||
let count = count.get();
|
||||
let alpha = cx.props.alpha + (count % 100) as f32;
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
left: "{x}%",
|
||||
top: "{y}%",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
background_color: "hsl({hue}, 100%, 50%, {alpha}%)",
|
||||
align_items: "center",
|
||||
p{"{count}"}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let steps = 50;
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
flex_direction: "column",
|
||||
(0..=steps).map(|x|
|
||||
{
|
||||
let hue = x as f32*360.0/steps as f32;
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
flex_direction: "row",
|
||||
(0..=steps).map(|y|
|
||||
{
|
||||
let alpha = y as f32*100.0/steps as f32;
|
||||
cx.render(rsx! {
|
||||
Box{
|
||||
x: x,
|
||||
y: y,
|
||||
alpha: alpha,
|
||||
hue: hue,
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -32,12 +32,7 @@
|
|||
use stretch2::{prelude::*, style::PositionType};
|
||||
|
||||
/// applies the entire html namespace defined in dioxus-html
|
||||
pub fn apply_layout_attributes(
|
||||
//
|
||||
name: &str,
|
||||
value: &str,
|
||||
style: &mut Style,
|
||||
) {
|
||||
pub fn apply_layout_attributes(name: &str, value: &str, style: &mut Style) {
|
||||
match name {
|
||||
"align-content"
|
||||
| "align-items"
|
||||
|
@ -253,6 +248,7 @@ pub fn apply_layout_attributes(
|
|||
}
|
||||
}
|
||||
|
||||
/// a relative or absolute size
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum UnitSystem {
|
||||
Percent(f32),
|
||||
|
@ -268,6 +264,7 @@ impl Into<Dimension> for UnitSystem {
|
|||
}
|
||||
}
|
||||
|
||||
/// parse relative or absolute value
|
||||
pub fn parse_value(value: &str) -> Option<UnitSystem> {
|
||||
if value.ends_with("px") {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
|
@ -605,11 +602,7 @@ fn apply_align(name: &str, value: &str, style: &mut Style) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn apply_size(_name: &str, _value: &str, _style: &mut Style) {
|
||||
//
|
||||
}
|
||||
|
||||
pub fn apply_margin(name: &str, value: &str, style: &mut Style) {
|
||||
fn apply_margin(name: &str, value: &str, style: &mut Style) {
|
||||
match parse_value(value) {
|
||||
Some(UnitSystem::Percent(v)) => match name {
|
||||
"margin" => {
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
pub mod client_tree;
|
||||
pub mod layout;
|
||||
pub mod layout_attributes;
|
||||
pub mod real_dom;
|
||||
|
|
|
@ -6,20 +6,20 @@ use std::{
|
|||
|
||||
use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
||||
|
||||
/// A tree that can sync with dioxus mutations backed by a hashmap.
|
||||
/// Intended for use in lazy native renderers with a state that passes from parrent to children and or accumulates state from children to parrents.
|
||||
/// To get started implement [PushedDownState] and or [BubbledUpState] and call [Tree::apply_mutations] and [Tree::update_state].
|
||||
/// A tree that can sync with the VirtualDom mutations intended for use in lazy renderers.
|
||||
/// The render state passes from parent to children and or accumulates state from children to parents.
|
||||
/// To get started implement [PushedDownState] and or [BubbledUpState] and call [Tree::apply_mutations] to update the tree and [Tree::update_state] to update the state of the nodes.
|
||||
#[derive(Debug)]
|
||||
pub struct ClientTree<US: BubbledUpState = (), DS: PushedDownState = ()> {
|
||||
pub struct RealDom<US: BubbledUpState = (), DS: PushedDownState = ()> {
|
||||
root: usize,
|
||||
nodes: Vec<Option<TreeNode<US, DS>>>,
|
||||
nodes_listening: FxHashMap<&'static str, FxHashSet<usize>>,
|
||||
node_stack: smallvec::SmallVec<[usize; 10]>,
|
||||
}
|
||||
|
||||
impl<US: BubbledUpState, DS: PushedDownState> ClientTree<US, DS> {
|
||||
pub fn new() -> ClientTree<US, DS> {
|
||||
ClientTree {
|
||||
impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||
pub fn new() -> RealDom<US, DS> {
|
||||
RealDom {
|
||||
root: 0,
|
||||
nodes: {
|
||||
let mut v = Vec::new();
|
||||
|
@ -187,6 +187,7 @@ impl<US: BubbledUpState, DS: PushedDownState> ClientTree<US, DS> {
|
|||
ds_ctx: &mut DS::Ctx,
|
||||
) -> Option<FxHashSet<usize>> {
|
||||
let mut to_rerender = FxHashSet::default();
|
||||
to_rerender.extend(nodes_updated.iter());
|
||||
let mut nodes_updated: Vec<_> = nodes_updated
|
||||
.into_iter()
|
||||
.map(|id| (id, self[id].height))
|
||||
|
@ -296,10 +297,11 @@ impl<US: BubbledUpState, DS: PushedDownState> ClientTree<US, DS> {
|
|||
}
|
||||
}
|
||||
|
||||
// remove a node and it's children from the tree.
|
||||
fn remove(&mut self, id: usize) -> Option<TreeNode<US, DS>> {
|
||||
// We do not need to remove the node from the parent's children list for children.
|
||||
fn inner<US: BubbledUpState, DS: PushedDownState>(
|
||||
tree: &mut ClientTree<US, DS>,
|
||||
tree: &mut RealDom<US, DS>,
|
||||
id: usize,
|
||||
) -> Option<TreeNode<US, DS>> {
|
||||
let mut node = tree.nodes[id as usize].take()?;
|
||||
|
@ -418,9 +420,9 @@ impl<US: BubbledUpState, DS: PushedDownState> ClientTree<US, DS> {
|
|||
}
|
||||
|
||||
/// Call a function for each node in the tree, depth first.
|
||||
pub fn traverse(&self, mut f: impl FnMut(&TreeNode<US, DS>)) {
|
||||
pub fn traverse_depth_first(&self, mut f: impl FnMut(&TreeNode<US, DS>)) {
|
||||
fn inner<US: BubbledUpState, DS: PushedDownState>(
|
||||
tree: &ClientTree<US, DS>,
|
||||
tree: &RealDom<US, DS>,
|
||||
id: ElementId,
|
||||
f: &mut impl FnMut(&TreeNode<US, DS>),
|
||||
) {
|
||||
|
@ -446,7 +448,7 @@ impl<US: BubbledUpState, DS: PushedDownState> ClientTree<US, DS> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<US: BubbledUpState, DS: PushedDownState> Index<usize> for ClientTree<US, DS> {
|
||||
impl<US: BubbledUpState, DS: PushedDownState> Index<usize> for RealDom<US, DS> {
|
||||
type Output = TreeNode<US, DS>;
|
||||
|
||||
fn index(&self, idx: usize) -> &Self::Output {
|
||||
|
@ -454,7 +456,7 @@ impl<US: BubbledUpState, DS: PushedDownState> Index<usize> for ClientTree<US, DS
|
|||
}
|
||||
}
|
||||
|
||||
impl<US: BubbledUpState, DS: PushedDownState> Index<ElementId> for ClientTree<US, DS> {
|
||||
impl<US: BubbledUpState, DS: PushedDownState> Index<ElementId> for RealDom<US, DS> {
|
||||
type Output = TreeNode<US, DS>;
|
||||
|
||||
fn index(&self, idx: ElementId) -> &Self::Output {
|
||||
|
@ -462,12 +464,12 @@ impl<US: BubbledUpState, DS: PushedDownState> Index<ElementId> for ClientTree<US
|
|||
}
|
||||
}
|
||||
|
||||
impl<US: BubbledUpState, DS: PushedDownState> IndexMut<usize> for ClientTree<US, DS> {
|
||||
impl<US: BubbledUpState, DS: PushedDownState> IndexMut<usize> for RealDom<US, DS> {
|
||||
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
|
||||
self.get_mut(idx).expect("Node does not exist")
|
||||
}
|
||||
}
|
||||
impl<US: BubbledUpState, DS: PushedDownState> IndexMut<ElementId> for ClientTree<US, DS> {
|
||||
impl<US: BubbledUpState, DS: PushedDownState> IndexMut<ElementId> for RealDom<US, DS> {
|
||||
fn index_mut(&mut self, idx: ElementId) -> &mut Self::Output {
|
||||
&mut self[idx.0]
|
||||
}
|
||||
|
@ -557,7 +559,7 @@ impl PushedDownState for () {
|
|||
fn reduce(&mut self, _parent: Option<&Self>, _vnode: &VNode, _ctx: &mut Self::Ctx) {}
|
||||
}
|
||||
|
||||
/// This state is derived from children. For example a non-flexbox div's size could be derived from the size of children.
|
||||
/// This state is derived from children. For example a node's size could be derived from the size of children.
|
||||
/// Called when the current node's node properties are modified, a child's [BubbledUpState] is modified or a child is removed.
|
||||
/// Called at most once per update.
|
||||
pub trait BubbledUpState: Default + PartialEq + Clone {
|
|
@ -2,7 +2,7 @@ use dioxus_core::VNode;
|
|||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus_native_core::client_tree::ClientTree;
|
||||
use dioxus_native_core::real_dom::RealDom;
|
||||
use std::cell::Cell;
|
||||
|
||||
#[test]
|
||||
|
@ -20,7 +20,7 @@ fn tree_remove_node() {
|
|||
}
|
||||
});
|
||||
|
||||
let mut tree: ClientTree<(), ()> = ClientTree::new();
|
||||
let mut tree: RealDom<(), ()> = RealDom::new();
|
||||
|
||||
let _to_update = tree.apply_mutations(vec![mutations]);
|
||||
let child_div = VElement {
|
||||
|
@ -92,7 +92,7 @@ fn tree_add_node() {
|
|||
div{}
|
||||
});
|
||||
|
||||
let mut tree: ClientTree<(), ()> = ClientTree::new();
|
||||
let mut tree: RealDom<(), ()> = RealDom::new();
|
||||
|
||||
let _to_update = tree.apply_mutations(vec![mutations]);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use dioxus_core::VNode;
|
|||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus_native_core::client_tree::ClientTree;
|
||||
use dioxus_native_core::real_dom::RealDom;
|
||||
|
||||
#[test]
|
||||
fn tree_initial_build_simple() {
|
||||
|
@ -21,7 +21,7 @@ fn tree_initial_build_simple() {
|
|||
div{}
|
||||
});
|
||||
|
||||
let mut tree: ClientTree<(), ()> = ClientTree::new();
|
||||
let mut tree: RealDom<(), ()> = RealDom::new();
|
||||
|
||||
let _to_update = tree.apply_mutations(vec![mutations]);
|
||||
let root_div = VElement {
|
||||
|
@ -60,7 +60,7 @@ fn tree_initial_build_with_children() {
|
|||
}
|
||||
});
|
||||
|
||||
let mut tree: ClientTree<(), ()> = ClientTree::new();
|
||||
let mut tree: RealDom<(), ()> = RealDom::new();
|
||||
|
||||
let _to_update = tree.apply_mutations(vec![mutations]);
|
||||
let first_text = VText {
|
||||
|
|
|
@ -2,7 +2,7 @@ use dioxus_core::VNode;
|
|||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus_native_core::client_tree::*;
|
||||
use dioxus_native_core::real_dom::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
struct CallCounter(u32);
|
||||
|
@ -77,7 +77,7 @@ fn tree_state_initial() {
|
|||
}
|
||||
});
|
||||
|
||||
let mut tree: ClientTree<BubbledUpStateTester, PushedDownStateTester> = ClientTree::new();
|
||||
let mut tree: RealDom<BubbledUpStateTester, PushedDownStateTester> = RealDom::new();
|
||||
|
||||
let nodes_updated = tree.apply_mutations(vec![mutations]);
|
||||
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut 42, &mut 42);
|
||||
|
@ -178,12 +178,12 @@ fn tree_state_reduce_initally_called_minimally() {
|
|||
}
|
||||
});
|
||||
|
||||
let mut tree: ClientTree<CallCounter, CallCounter> = ClientTree::new();
|
||||
let mut tree: RealDom<CallCounter, CallCounter> = RealDom::new();
|
||||
|
||||
let nodes_updated = tree.apply_mutations(vec![mutations]);
|
||||
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
|
||||
|
||||
tree.traverse(|n| {
|
||||
tree.traverse_depth_first(|n| {
|
||||
assert_eq!(n.up_state.0, 1);
|
||||
assert_eq!(n.down_state.0, 1);
|
||||
});
|
||||
|
@ -233,7 +233,7 @@ fn tree_state_reduce_down_called_minimally_on_update() {
|
|||
}
|
||||
});
|
||||
|
||||
let mut tree: ClientTree<CallCounter, CallCounter> = ClientTree::new();
|
||||
let mut tree: RealDom<CallCounter, CallCounter> = RealDom::new();
|
||||
|
||||
let nodes_updated = tree.apply_mutations(vec![mutations]);
|
||||
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
|
||||
|
@ -249,7 +249,7 @@ fn tree_state_reduce_down_called_minimally_on_update() {
|
|||
}]);
|
||||
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
|
||||
|
||||
tree.traverse(|n| {
|
||||
tree.traverse_depth_first(|n| {
|
||||
assert_eq!(n.down_state.0, 2);
|
||||
});
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ fn tree_state_reduce_up_called_minimally_on_update() {
|
|||
}
|
||||
});
|
||||
|
||||
let mut tree: ClientTree<CallCounter, CallCounter> = ClientTree::new();
|
||||
let mut tree: RealDom<CallCounter, CallCounter> = RealDom::new();
|
||||
|
||||
let nodes_updated = tree.apply_mutations(vec![mutations]);
|
||||
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
|
||||
|
@ -314,7 +314,7 @@ fn tree_state_reduce_up_called_minimally_on_update() {
|
|||
}]);
|
||||
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
|
||||
|
||||
tree.traverse(|n| {
|
||||
tree.traverse_depth_first(|n| {
|
||||
assert_eq!(n.up_state.0, if n.id.0 > 4 { 1 } else { 2 });
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,10 +5,7 @@ use dioxus_core::*;
|
|||
use fxhash::{FxHashMap, FxHashSet};
|
||||
|
||||
use dioxus_html::{on::*, KeyCode};
|
||||
use dioxus_native_core::{
|
||||
client_tree::{ClientTree, TreeNode},
|
||||
layout::StretchLayout,
|
||||
};
|
||||
use dioxus_native_core::real_dom::{RealDom, TreeNode};
|
||||
use futures::{channel::mpsc::UnboundedReceiver, StreamExt};
|
||||
use std::{
|
||||
any::Any,
|
||||
|
@ -19,6 +16,7 @@ use std::{
|
|||
};
|
||||
use stretch2::{prelude::Layout, Stretch};
|
||||
|
||||
use crate::layout::StretchLayout;
|
||||
use crate::style_attributes::StyleModifier;
|
||||
|
||||
// a wrapper around the input state for easier access
|
||||
|
@ -169,7 +167,7 @@ impl InnerInputState {
|
|||
evts: &mut Vec<EventCore>,
|
||||
resolved_events: &mut Vec<UserEvent>,
|
||||
layout: &Stretch,
|
||||
tree: &mut ClientTree<StretchLayout, StyleModifier>,
|
||||
tree: &mut RealDom<StretchLayout, StyleModifier>,
|
||||
) {
|
||||
let previous_mouse = self
|
||||
.mouse
|
||||
|
@ -194,7 +192,7 @@ impl InnerInputState {
|
|||
previous_mouse: Option<(MouseData, Vec<u16>)>,
|
||||
resolved_events: &mut Vec<UserEvent>,
|
||||
layout: &Stretch,
|
||||
tree: &mut ClientTree<StretchLayout, StyleModifier>,
|
||||
tree: &mut RealDom<StretchLayout, StyleModifier>,
|
||||
) {
|
||||
struct Data<'b> {
|
||||
new_pos: (i32, i32),
|
||||
|
@ -219,7 +217,7 @@ impl InnerInputState {
|
|||
will_bubble: &mut FxHashSet<ElementId>,
|
||||
resolved_events: &mut Vec<UserEvent>,
|
||||
node: &TreeNode<StretchLayout, StyleModifier>,
|
||||
tree: &ClientTree<StretchLayout, StyleModifier>,
|
||||
tree: &RealDom<StretchLayout, StyleModifier>,
|
||||
) {
|
||||
// only trigger event if the event was not triggered already by a child
|
||||
if will_bubble.insert(node.id) {
|
||||
|
@ -546,7 +544,7 @@ impl RinkInputHandler {
|
|||
pub fn get_events<'a>(
|
||||
&self,
|
||||
layout: &Stretch,
|
||||
tree: &mut ClientTree<StretchLayout, StyleModifier>,
|
||||
tree: &mut RealDom<StretchLayout, StyleModifier>,
|
||||
) -> Vec<UserEvent> {
|
||||
let mut resolved_events = Vec::new();
|
||||
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
use crate::client_tree::BubbledUpState;
|
||||
use dioxus_core::*;
|
||||
use dioxus_native_core::layout_attributes::apply_layout_attributes;
|
||||
use dioxus_native_core::real_dom::BubbledUpState;
|
||||
use stretch2::prelude::*;
|
||||
|
||||
use crate::layout_attributes::apply_layout_attributes;
|
||||
|
||||
/*
|
||||
The layout system uses the lineheight as one point.
|
||||
|
||||
stretch uses fractional points, so we can rasterize if we need too, but not with characters
|
||||
this means anything thats "1px" is 1 lineheight. Unfortunately, text cannot be smaller or bigger
|
||||
*/
|
||||
/// the size
|
||||
#[derive(Clone, PartialEq, Default, Debug)]
|
||||
pub struct StretchLayout {
|
||||
pub style: Style,
|
||||
|
@ -19,7 +13,7 @@ pub struct StretchLayout {
|
|||
impl BubbledUpState for StretchLayout {
|
||||
type Ctx = Stretch;
|
||||
|
||||
// Although we pass in the parent, the state of RinkLayout must only depend on the parent for optimiztion purposes
|
||||
/// Setup the layout
|
||||
fn reduce<'a, I>(&mut self, children: I, vnode: &VNode, stretch: &mut Self::Ctx)
|
||||
where
|
||||
I: Iterator<Item = &'a Self>,
|
|
@ -9,16 +9,20 @@ use crossterm::{
|
|||
};
|
||||
use dioxus_core::exports::futures_channel::mpsc::unbounded;
|
||||
use dioxus_core::*;
|
||||
use dioxus_native_core::{client_tree::ClientTree, layout::StretchLayout};
|
||||
use futures::{channel::mpsc::UnboundedSender, pin_mut, StreamExt};
|
||||
use dioxus_native_core::real_dom::RealDom;
|
||||
use futures::{
|
||||
channel::mpsc::{UnboundedReceiver, UnboundedSender},
|
||||
pin_mut, StreamExt,
|
||||
};
|
||||
use layout::StretchLayout;
|
||||
use std::{io, time::Duration};
|
||||
use stretch2::{prelude::Size, Stretch};
|
||||
use style_attributes::StyleModifier;
|
||||
use tokio::time::Instant;
|
||||
use tui::{backend::CrosstermBackend, Terminal};
|
||||
|
||||
mod config;
|
||||
mod hooks;
|
||||
mod layout;
|
||||
mod render;
|
||||
mod style;
|
||||
mod style_attributes;
|
||||
|
@ -28,6 +32,16 @@ pub use config::*;
|
|||
pub use hooks::*;
|
||||
pub use render::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TuiContext {
|
||||
tx: UnboundedSender<InputEvent>,
|
||||
}
|
||||
impl TuiContext {
|
||||
pub fn quit(&self) {
|
||||
self.tx.unbounded_send(InputEvent::Close).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn launch(app: Component<()>) {
|
||||
launch_cfg(app, Config::default())
|
||||
}
|
||||
|
@ -40,9 +54,26 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
|||
|
||||
let (handler, state) = RinkInputHandler::new(rx, cx);
|
||||
|
||||
cx.provide_root_context(state);
|
||||
// Setup input handling
|
||||
let (event_tx, event_rx) = unbounded();
|
||||
let event_tx_clone = event_tx.clone();
|
||||
std::thread::spawn(move || {
|
||||
let tick_rate = Duration::from_millis(1000);
|
||||
loop {
|
||||
if crossterm::event::poll(tick_rate).unwrap() {
|
||||
// if crossterm::event::poll(timeout).unwrap() {
|
||||
let evt = crossterm::event::read().unwrap();
|
||||
if event_tx.unbounded_send(InputEvent::UserInput(evt)).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut tree: ClientTree<StretchLayout, StyleModifier> = ClientTree::new();
|
||||
cx.provide_root_context(state);
|
||||
cx.provide_root_context(TuiContext { tx: event_tx_clone });
|
||||
|
||||
let mut tree: RealDom<StretchLayout, StyleModifier> = RealDom::new();
|
||||
let mutations = dom.rebuild();
|
||||
let to_update = tree.apply_mutations(vec![mutations]);
|
||||
let mut stretch = Stretch::new();
|
||||
|
@ -50,47 +81,22 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
|||
.update_state(&dom, to_update, &mut stretch, &mut ())
|
||||
.unwrap();
|
||||
|
||||
render_vdom(&mut dom, tx, handler, cfg, tree, stretch).unwrap();
|
||||
render_vdom(&mut dom, tx, event_rx, handler, cfg, tree, stretch).unwrap();
|
||||
}
|
||||
|
||||
pub fn render_vdom(
|
||||
fn render_vdom(
|
||||
vdom: &mut VirtualDom,
|
||||
ctx: UnboundedSender<TermEvent>,
|
||||
crossterm_event_sender: UnboundedSender<TermEvent>,
|
||||
mut event_reciever: UnboundedReceiver<InputEvent>,
|
||||
handler: RinkInputHandler,
|
||||
cfg: Config,
|
||||
mut tree: ClientTree<StretchLayout, StyleModifier>,
|
||||
mut tree: RealDom<StretchLayout, StyleModifier>,
|
||||
mut stretch: Stretch,
|
||||
) -> Result<()> {
|
||||
// Setup input handling
|
||||
let (tx, mut rx) = unbounded();
|
||||
std::thread::spawn(move || {
|
||||
let tick_rate = Duration::from_millis(100);
|
||||
let mut last_tick = Instant::now();
|
||||
loop {
|
||||
// poll for tick rate duration, if no events, sent tick event.
|
||||
let timeout = tick_rate
|
||||
.checked_sub(last_tick.elapsed())
|
||||
.unwrap_or_else(|| Duration::from_secs(0));
|
||||
|
||||
if crossterm::event::poll(timeout).unwrap() {
|
||||
let evt = crossterm::event::read().unwrap();
|
||||
tx.unbounded_send(InputEvent::UserInput(evt)).unwrap();
|
||||
}
|
||||
|
||||
if last_tick.elapsed() >= tick_rate {
|
||||
tx.unbounded_send(InputEvent::Tick).unwrap();
|
||||
last_tick = Instant::now();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()?
|
||||
.block_on(async {
|
||||
/*
|
||||
Get the terminal to calcualte the layout from
|
||||
*/
|
||||
enable_raw_mode().unwrap();
|
||||
let mut stdout = std::io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap();
|
||||
|
@ -103,10 +109,9 @@ pub fn render_vdom(
|
|||
|
||||
loop {
|
||||
/*
|
||||
-> collect all the nodes
|
||||
-> resolve events
|
||||
-> render the nodes in the right place with tui/crossterm
|
||||
-> rendering
|
||||
-> wait for changes
|
||||
-> resolve events
|
||||
-> lazily update the layout and style based on nodes changed
|
||||
|
||||
use simd to compare lines for diffing?
|
||||
|
@ -119,11 +124,11 @@ pub fn render_vdom(
|
|||
terminal.draw(|frame| {
|
||||
// size is guaranteed to not change when rendering
|
||||
let dims = frame.size();
|
||||
// println!("{dims:?}");
|
||||
let width = dims.width;
|
||||
let height = dims.height;
|
||||
let root_id = tree.root_id();
|
||||
let root_node = tree[root_id].up_state.node.unwrap();
|
||||
|
||||
stretch
|
||||
.compute_layout(
|
||||
root_node,
|
||||
|
@ -138,18 +143,12 @@ pub fn render_vdom(
|
|||
})?;
|
||||
}
|
||||
|
||||
// resolve events before rendering
|
||||
// todo: events do not trigger update?
|
||||
for e in handler.get_events(&stretch, &mut tree) {
|
||||
vdom.handle_message(SchedulerMsg::Event(e));
|
||||
}
|
||||
|
||||
use futures::future::{select, Either};
|
||||
{
|
||||
let wait = vdom.wait_for_work();
|
||||
pin_mut!(wait);
|
||||
|
||||
match select(wait, rx.next()).await {
|
||||
match select(wait, event_reciever.next()).await {
|
||||
Either::Left((_a, _b)) => {
|
||||
//
|
||||
}
|
||||
|
@ -166,17 +165,21 @@ pub fn render_vdom(
|
|||
TermEvent::Resize(_, _) => redraw = true,
|
||||
TermEvent::Mouse(_) => {}
|
||||
},
|
||||
InputEvent::Tick => {} // tick
|
||||
InputEvent::Close => break,
|
||||
};
|
||||
|
||||
if let InputEvent::UserInput(evt) = evt.unwrap() {
|
||||
ctx.unbounded_send(evt).unwrap();
|
||||
crossterm_event_sender.unbounded_send(evt).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolve events before rendering
|
||||
for e in handler.get_events(&stretch, &mut tree) {
|
||||
vdom.handle_message(SchedulerMsg::Event(e));
|
||||
}
|
||||
vdom.process_all_messages();
|
||||
let mutations = vdom.work_with_deadline(|| false);
|
||||
// updates the tree's nodes
|
||||
let to_update = tree.apply_mutations(mutations);
|
||||
|
@ -200,7 +203,6 @@ pub fn render_vdom(
|
|||
|
||||
enum InputEvent {
|
||||
UserInput(TermEvent),
|
||||
Tick,
|
||||
#[allow(dead_code)]
|
||||
Close,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::layout::StretchLayout;
|
||||
use dioxus_native_core::{
|
||||
client_tree::{ClientTree, TreeNode},
|
||||
layout::StretchLayout,
|
||||
layout_attributes::UnitSystem,
|
||||
real_dom::{RealDom, TreeNode},
|
||||
};
|
||||
use std::io::Stdout;
|
||||
use stretch2::{
|
||||
|
@ -23,11 +23,11 @@ const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
|||
pub fn render_vnode<'a>(
|
||||
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
|
||||
layout: &Stretch,
|
||||
tree: &ClientTree<StretchLayout, StyleModifier>,
|
||||
tree: &RealDom<StretchLayout, StyleModifier>,
|
||||
node: &TreeNode<StretchLayout, StyleModifier>,
|
||||
cfg: Config,
|
||||
) {
|
||||
use dioxus_native_core::client_tree::TreeNodeType;
|
||||
use dioxus_native_core::real_dom::TreeNodeType;
|
||||
|
||||
match &node.node_type {
|
||||
TreeNodeType::Placeholder => return,
|
||||
|
@ -51,7 +51,6 @@ pub fn render_vnode<'a>(
|
|||
fn render(self, area: Rect, mut buf: RinkBuffer) {
|
||||
for (i, c) in self.text.char_indices() {
|
||||
let mut new_cell = RinkCell::default();
|
||||
// println!("{:?}", self.style);
|
||||
new_cell.set_style(self.style);
|
||||
new_cell.symbol = c.to_string();
|
||||
buf.set(area.left() + i as u16, area.top(), new_cell);
|
||||
|
|
|
@ -28,7 +28,7 @@ impl RinkColor {
|
|||
} else {
|
||||
let [sr, sg, sb] = to_rgb(self.color).map(|e| e as u16);
|
||||
let [or, og, ob] = to_rgb(other).map(|e| e as u16);
|
||||
let sa: u16 = self.alpha as u16;
|
||||
let sa = self.alpha as u16;
|
||||
let rsa = 255 - sa;
|
||||
Color::Rgb(
|
||||
((sr * sa + or * rsa) / 255) as u8,
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
|
||||
use dioxus_core::{Attribute, VNode};
|
||||
use dioxus_native_core::{
|
||||
client_tree::PushedDownState,
|
||||
layout_attributes::{parse_value, UnitSystem},
|
||||
real_dom::PushedDownState,
|
||||
};
|
||||
|
||||
use crate::style::{RinkColor, RinkStyle};
|
||||
|
|
Loading…
Add table
Reference in a new issue