finishing touches and benchmarks

This commit is contained in:
Evan Almloff committed 2022-03-30 20:45:41 -05:00
1 parent 05d04165ba
commit 5b25500c0b
15 files changed
+305 -206

No files matched your search

+6 -2
View File
@@ -28,6 +28,8 @@ dioxus-tui = { path = "./packages/tui", version = "^0.2.0", optional = true }
dioxus-liveview = { path = "./packages/liveview", 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-mobile = { path = "./packages/mobile", version = "^0.2.0", optional = true }
# dioxus-rsx = { path = "./packages/rsx", optional = true } # dioxus-rsx = { path = "./packages/rsx", optional = true }
# macro = ["dioxus-core-macro", "dioxus-rsx"] # macro = ["dioxus-core-macro", "dioxus-rsx"]
@@ -45,6 +47,7 @@ ayatana = ["dioxus-desktop/ayatana"]
router = ["dioxus-router"] router = ["dioxus-router"]
tui = ["dioxus-tui"] tui = ["dioxus-tui"]
liveview = ["dioxus-liveview"] liveview = ["dioxus-liveview"]
native-core = ["dioxus-native-core"]
[workspace] [workspace]
@@ -90,5 +93,6 @@ harness = false
name = "jsframework" name = "jsframework"
harness = false harness = false
[profile.release] [[bench]]
debug = true name = "tui_update"
harness = false
+196
View 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,
}
}
})
}
-88
View File
@@ -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,
}
})
}
)
}
})
}
)
}
})
}
+4 -11
View File
@@ -32,12 +32,7 @@
use stretch2::{prelude::*, style::PositionType}; use stretch2::{prelude::*, style::PositionType};
/// applies the entire html namespace defined in dioxus-html /// applies the entire html namespace defined in dioxus-html
pub fn apply_layout_attributes( pub fn apply_layout_attributes(name: &str, value: &str, style: &mut Style) {
//
name: &str,
value: &str,
style: &mut Style,
) {
match name { match name {
"align-content" "align-content"
| "align-items" | "align-items"
@@ -253,6 +248,7 @@ pub fn apply_layout_attributes(
} }
} }
/// a relative or absolute size
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum UnitSystem { pub enum UnitSystem {
Percent(f32), Percent(f32),
@@ -268,6 +264,7 @@ impl Into<Dimension> for UnitSystem {
} }
} }
/// parse relative or absolute value
pub fn parse_value(value: &str) -> Option<UnitSystem> { pub fn parse_value(value: &str) -> Option<UnitSystem> {
if value.ends_with("px") { if value.ends_with("px") {
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() { 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) { fn apply_margin(name: &str, value: &str, style: &mut Style) {
//
}
pub fn apply_margin(name: &str, value: &str, style: &mut Style) {
match parse_value(value) { match parse_value(value) {
Some(UnitSystem::Percent(v)) => match name { Some(UnitSystem::Percent(v)) => match name {
"margin" => { "margin" => {
+1 -2
View File
@@ -1,3 +1,2 @@
pub mod client_tree;
pub mod layout;
pub mod layout_attributes; pub mod layout_attributes;
pub mod real_dom;
@@ -6,20 +6,20 @@ use std::{
use dioxus_core::{ElementId, Mutations, VNode, VirtualDom}; use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
/// A tree that can sync with dioxus mutations backed by a hashmap. /// A tree that can sync with the VirtualDom mutations intended for use in lazy renderers.
/// Intended for use in lazy native renderers with a state that passes from parrent to children and or accumulates state from children to parrents. /// 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] and [Tree::update_state]. /// 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)] #[derive(Debug)]
pub struct ClientTree<US: BubbledUpState = (), DS: PushedDownState = ()> { pub struct RealDom<US: BubbledUpState = (), DS: PushedDownState = ()> {
root: usize, root: usize,
nodes: Vec<Option<TreeNode<US, DS>>>, nodes: Vec<Option<TreeNode<US, DS>>>,
nodes_listening: FxHashMap<&'static str, FxHashSet<usize>>, nodes_listening: FxHashMap<&'static str, FxHashSet<usize>>,
node_stack: smallvec::SmallVec<[usize; 10]>, node_stack: smallvec::SmallVec<[usize; 10]>,
} }
impl<US: BubbledUpState, DS: PushedDownState> ClientTree<US, DS> { impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
pub fn new() -> ClientTree<US, DS> { pub fn new() -> RealDom<US, DS> {
ClientTree { RealDom {
root: 0, root: 0,
nodes: { nodes: {
let mut v = Vec::new(); let mut v = Vec::new();
@@ -187,6 +187,7 @@ impl<US: BubbledUpState, DS: PushedDownState> ClientTree<US, DS> {
ds_ctx: &mut DS::Ctx, ds_ctx: &mut DS::Ctx,
) -> Option<FxHashSet<usize>> { ) -> Option<FxHashSet<usize>> {
let mut to_rerender = FxHashSet::default(); let mut to_rerender = FxHashSet::default();
to_rerender.extend(nodes_updated.iter());
let mut nodes_updated: Vec<_> = nodes_updated let mut nodes_updated: Vec<_> = nodes_updated
.into_iter() .into_iter()
.map(|id| (id, self[id].height)) .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>> { 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. // We do not need to remove the node from the parent's children list for children.
fn inner<US: BubbledUpState, DS: PushedDownState>( fn inner<US: BubbledUpState, DS: PushedDownState>(
tree: &mut ClientTree<US, DS>, tree: &mut RealDom<US, DS>,
id: usize, id: usize,
) -> Option<TreeNode<US, DS>> { ) -> Option<TreeNode<US, DS>> {
let mut node = tree.nodes[id as usize].take()?; 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. /// 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>( fn inner<US: BubbledUpState, DS: PushedDownState>(
tree: &ClientTree<US, DS>, tree: &RealDom<US, DS>,
id: ElementId, id: ElementId,
f: &mut impl FnMut(&TreeNode<US, DS>), 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>; type Output = TreeNode<US, DS>;
fn index(&self, idx: usize) -> &Self::Output { 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>; type Output = TreeNode<US, DS>;
fn index(&self, idx: ElementId) -> &Self::Output { 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 { fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
self.get_mut(idx).expect("Node does not exist") 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 { fn index_mut(&mut self, idx: ElementId) -> &mut Self::Output {
&mut self[idx.0] &mut self[idx.0]
} }
@@ -557,7 +559,7 @@ impl PushedDownState for () {
fn reduce(&mut self, _parent: Option<&Self>, _vnode: &VNode, _ctx: &mut Self::Ctx) {} 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 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. /// Called at most once per update.
pub trait BubbledUpState: Default + PartialEq + Clone { pub trait BubbledUpState: Default + PartialEq + Clone {
+3 -3
View File
@@ -2,7 +2,7 @@ use dioxus_core::VNode;
use dioxus_core::*; use dioxus_core::*;
use dioxus_core_macro::*; use dioxus_core_macro::*;
use dioxus_html as dioxus_elements; use dioxus_html as dioxus_elements;
use dioxus_native_core::client_tree::ClientTree; use dioxus_native_core::real_dom::RealDom;
use std::cell::Cell; use std::cell::Cell;
#[test] #[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 _to_update = tree.apply_mutations(vec![mutations]);
let child_div = VElement { let child_div = VElement {
@@ -92,7 +92,7 @@ fn tree_add_node() {
div{} div{}
}); });
let mut tree: ClientTree<(), ()> = ClientTree::new(); let mut tree: RealDom<(), ()> = RealDom::new();
let _to_update = tree.apply_mutations(vec![mutations]); let _to_update = tree.apply_mutations(vec![mutations]);
+3 -3
View File
@@ -4,7 +4,7 @@ use dioxus_core::VNode;
use dioxus_core::*; use dioxus_core::*;
use dioxus_core_macro::*; use dioxus_core_macro::*;
use dioxus_html as dioxus_elements; use dioxus_html as dioxus_elements;
use dioxus_native_core::client_tree::ClientTree; use dioxus_native_core::real_dom::RealDom;
#[test] #[test]
fn tree_initial_build_simple() { fn tree_initial_build_simple() {
@@ -21,7 +21,7 @@ fn tree_initial_build_simple() {
div{} div{}
}); });
let mut tree: ClientTree<(), ()> = ClientTree::new(); let mut tree: RealDom<(), ()> = RealDom::new();
let _to_update = tree.apply_mutations(vec![mutations]); let _to_update = tree.apply_mutations(vec![mutations]);
let root_div = VElement { 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 _to_update = tree.apply_mutations(vec![mutations]);
let first_text = VText { let first_text = VText {
+8 -8
View File
@@ -2,7 +2,7 @@ use dioxus_core::VNode;
use dioxus_core::*; use dioxus_core::*;
use dioxus_core_macro::*; use dioxus_core_macro::*;
use dioxus_html as dioxus_elements; use dioxus_html as dioxus_elements;
use dioxus_native_core::client_tree::*; use dioxus_native_core::real_dom::*;
#[derive(Debug, Clone, PartialEq, Default)] #[derive(Debug, Clone, PartialEq, Default)]
struct CallCounter(u32); 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 nodes_updated = tree.apply_mutations(vec![mutations]);
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut 42, &mut 42); 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 nodes_updated = tree.apply_mutations(vec![mutations]);
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ()); 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.up_state.0, 1);
assert_eq!(n.down_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 nodes_updated = tree.apply_mutations(vec![mutations]);
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ()); 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 ()); 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); 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 nodes_updated = tree.apply_mutations(vec![mutations]);
let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ()); 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 ()); 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 }); assert_eq!(n.up_state.0, if n.id.0 > 4 { 1 } else { 2 });
}); });
} }
+6 -8
View File
@@ -5,10 +5,7 @@ use dioxus_core::*;
use fxhash::{FxHashMap, FxHashSet}; use fxhash::{FxHashMap, FxHashSet};
use dioxus_html::{on::*, KeyCode}; use dioxus_html::{on::*, KeyCode};
use dioxus_native_core::{ use dioxus_native_core::real_dom::{RealDom, TreeNode};
client_tree::{ClientTree, TreeNode},
layout::StretchLayout,
};
use futures::{channel::mpsc::UnboundedReceiver, StreamExt}; use futures::{channel::mpsc::UnboundedReceiver, StreamExt};
use std::{ use std::{
any::Any, any::Any,
@@ -19,6 +16,7 @@ use std::{
}; };
use stretch2::{prelude::Layout, Stretch}; use stretch2::{prelude::Layout, Stretch};
use crate::layout::StretchLayout;
use crate::style_attributes::StyleModifier; use crate::style_attributes::StyleModifier;
// a wrapper around the input state for easier access // a wrapper around the input state for easier access
@@ -169,7 +167,7 @@ impl InnerInputState {
evts: &mut Vec<EventCore>, evts: &mut Vec<EventCore>,
resolved_events: &mut Vec<UserEvent>, resolved_events: &mut Vec<UserEvent>,
layout: &Stretch, layout: &Stretch,
tree: &mut ClientTree<StretchLayout, StyleModifier>, tree: &mut RealDom<StretchLayout, StyleModifier>,
) { ) {
let previous_mouse = self let previous_mouse = self
.mouse .mouse
@@ -194,7 +192,7 @@ impl InnerInputState {
previous_mouse: Option<(MouseData, Vec<u16>)>, previous_mouse: Option<(MouseData, Vec<u16>)>,
resolved_events: &mut Vec<UserEvent>, resolved_events: &mut Vec<UserEvent>,
layout: &Stretch, layout: &Stretch,
tree: &mut ClientTree<StretchLayout, StyleModifier>, tree: &mut RealDom<StretchLayout, StyleModifier>,
) { ) {
struct Data<'b> { struct Data<'b> {
new_pos: (i32, i32), new_pos: (i32, i32),
@@ -219,7 +217,7 @@ impl InnerInputState {
will_bubble: &mut FxHashSet<ElementId>, will_bubble: &mut FxHashSet<ElementId>,
resolved_events: &mut Vec<UserEvent>, resolved_events: &mut Vec<UserEvent>,
node: &TreeNode<StretchLayout, StyleModifier>, 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 // only trigger event if the event was not triggered already by a child
if will_bubble.insert(node.id) { if will_bubble.insert(node.id) {
@@ -546,7 +544,7 @@ impl RinkInputHandler {
pub fn get_events<'a>( pub fn get_events<'a>(
&self, &self,
layout: &Stretch, layout: &Stretch,
tree: &mut ClientTree<StretchLayout, StyleModifier>, tree: &mut RealDom<StretchLayout, StyleModifier>,
) -> Vec<UserEvent> { ) -> Vec<UserEvent> {
let mut resolved_events = Vec::new(); let mut resolved_events = Vec::new();
@@ -1,15 +1,9 @@
use crate::client_tree::BubbledUpState;
use dioxus_core::*; use dioxus_core::*;
use dioxus_native_core::layout_attributes::apply_layout_attributes;
use dioxus_native_core::real_dom::BubbledUpState;
use stretch2::prelude::*; use stretch2::prelude::*;
use crate::layout_attributes::apply_layout_attributes; /// the size
/*
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
*/
#[derive(Clone, PartialEq, Default, Debug)] #[derive(Clone, PartialEq, Default, Debug)]
pub struct StretchLayout { pub struct StretchLayout {
pub style: Style, pub style: Style,
@@ -19,7 +13,7 @@ pub struct StretchLayout {
impl BubbledUpState for StretchLayout { impl BubbledUpState for StretchLayout {
type Ctx = Stretch; 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) fn reduce<'a, I>(&mut self, children: I, vnode: &VNode, stretch: &mut Self::Ctx)
where where
I: Iterator<Item = &'a Self>, I: Iterator<Item = &'a Self>,
+51 -49
View File
@@ -9,16 +9,20 @@ use crossterm::{
}; };
use dioxus_core::exports::futures_channel::mpsc::unbounded; use dioxus_core::exports::futures_channel::mpsc::unbounded;
use dioxus_core::*; use dioxus_core::*;
use dioxus_native_core::{client_tree::ClientTree, layout::StretchLayout}; use dioxus_native_core::real_dom::RealDom;
use futures::{channel::mpsc::UnboundedSender, pin_mut, StreamExt}; use futures::{
channel::mpsc::{UnboundedReceiver, UnboundedSender},
pin_mut, StreamExt,
};
use layout::StretchLayout;
use std::{io, time::Duration}; use std::{io, time::Duration};
use stretch2::{prelude::Size, Stretch}; use stretch2::{prelude::Size, Stretch};
use style_attributes::StyleModifier; use style_attributes::StyleModifier;
use tokio::time::Instant;
use tui::{backend::CrosstermBackend, Terminal}; use tui::{backend::CrosstermBackend, Terminal};
mod config; mod config;
mod hooks; mod hooks;
mod layout;
mod render; mod render;
mod style; mod style;
mod style_attributes; mod style_attributes;
@@ -28,6 +32,16 @@ pub use config::*;
pub use hooks::*; pub use hooks::*;
pub use render::*; 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<()>) { pub fn launch(app: Component<()>) {
launch_cfg(app, Config::default()) launch_cfg(app, Config::default())
} }
@@ -40,9 +54,26 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
let (handler, state) = RinkInputHandler::new(rx, cx); 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 mutations = dom.rebuild();
let to_update = tree.apply_mutations(vec![mutations]); let to_update = tree.apply_mutations(vec![mutations]);
let mut stretch = Stretch::new(); 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 ()) .update_state(&dom, to_update, &mut stretch, &mut ())
.unwrap(); .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, vdom: &mut VirtualDom,
ctx: UnboundedSender<TermEvent>, crossterm_event_sender: UnboundedSender<TermEvent>,
mut event_reciever: UnboundedReceiver<InputEvent>,
handler: RinkInputHandler, handler: RinkInputHandler,
cfg: Config, cfg: Config,
mut tree: ClientTree<StretchLayout, StyleModifier>, mut tree: RealDom<StretchLayout, StyleModifier>,
mut stretch: Stretch, mut stretch: Stretch,
) -> Result<()> { ) -> 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() tokio::runtime::Builder::new_current_thread()
.enable_all() .enable_all()
.build()? .build()?
.block_on(async { .block_on(async {
/*
Get the terminal to calcualte the layout from
*/
enable_raw_mode().unwrap(); enable_raw_mode().unwrap();
let mut stdout = std::io::stdout(); let mut stdout = std::io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap(); execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap();
@@ -103,10 +109,9 @@ pub fn render_vdom(
loop { loop {
/* /*
-> collect all the nodes
-> resolve events
-> render the nodes in the right place with tui/crossterm -> 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 -> lazily update the layout and style based on nodes changed
use simd to compare lines for diffing? use simd to compare lines for diffing?
@@ -119,11 +124,11 @@ pub fn render_vdom(
terminal.draw(|frame| { terminal.draw(|frame| {
// size is guaranteed to not change when rendering // size is guaranteed to not change when rendering
let dims = frame.size(); let dims = frame.size();
// println!("{dims:?}");
let width = dims.width; let width = dims.width;
let height = dims.height; let height = dims.height;
let root_id = tree.root_id(); let root_id = tree.root_id();
let root_node = tree[root_id].up_state.node.unwrap(); let root_node = tree[root_id].up_state.node.unwrap();
stretch stretch
.compute_layout( .compute_layout(
root_node, 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}; use futures::future::{select, Either};
{ {
let wait = vdom.wait_for_work(); let wait = vdom.wait_for_work();
pin_mut!(wait); pin_mut!(wait);
match select(wait, rx.next()).await { match select(wait, event_reciever.next()).await {
Either::Left((_a, _b)) => { Either::Left((_a, _b)) => {
// //
} }
@@ -166,17 +165,21 @@ pub fn render_vdom(
TermEvent::Resize(_, _) => redraw = true, TermEvent::Resize(_, _) => redraw = true,
TermEvent::Mouse(_) => {} TermEvent::Mouse(_) => {}
}, },
InputEvent::Tick => {} // tick
InputEvent::Close => break, InputEvent::Close => break,
}; };
if let InputEvent::UserInput(evt) = evt.unwrap() { 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); let mutations = vdom.work_with_deadline(|| false);
// updates the tree's nodes // updates the tree's nodes
let to_update = tree.apply_mutations(mutations); let to_update = tree.apply_mutations(mutations);
@@ -200,7 +203,6 @@ pub fn render_vdom(
enum InputEvent { enum InputEvent {
UserInput(TermEvent), UserInput(TermEvent),
Tick,
#[allow(dead_code)] #[allow(dead_code)]
Close, Close,
} }
+4 -5
View File
@@ -1,7 +1,7 @@
use crate::layout::StretchLayout;
use dioxus_native_core::{ use dioxus_native_core::{
client_tree::{ClientTree, TreeNode},
layout::StretchLayout,
layout_attributes::UnitSystem, layout_attributes::UnitSystem,
real_dom::{RealDom, TreeNode},
}; };
use std::io::Stdout; use std::io::Stdout;
use stretch2::{ use stretch2::{
@@ -23,11 +23,11 @@ const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
pub fn render_vnode<'a>( pub fn render_vnode<'a>(
frame: &mut tui::Frame<CrosstermBackend<Stdout>>, frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
layout: &Stretch, layout: &Stretch,
tree: &ClientTree<StretchLayout, StyleModifier>, tree: &RealDom<StretchLayout, StyleModifier>,
node: &TreeNode<StretchLayout, StyleModifier>, node: &TreeNode<StretchLayout, StyleModifier>,
cfg: Config, cfg: Config,
) { ) {
use dioxus_native_core::client_tree::TreeNodeType; use dioxus_native_core::real_dom::TreeNodeType;
match &node.node_type { match &node.node_type {
TreeNodeType::Placeholder => return, TreeNodeType::Placeholder => return,
@@ -51,7 +51,6 @@ pub fn render_vnode<'a>(
fn render(self, area: Rect, mut buf: RinkBuffer) { fn render(self, area: Rect, mut buf: RinkBuffer) {
for (i, c) in self.text.char_indices() { for (i, c) in self.text.char_indices() {
let mut new_cell = RinkCell::default(); let mut new_cell = RinkCell::default();
// println!("{:?}", self.style);
new_cell.set_style(self.style); new_cell.set_style(self.style);
new_cell.symbol = c.to_string(); new_cell.symbol = c.to_string();
buf.set(area.left() + i as u16, area.top(), new_cell); buf.set(area.left() + i as u16, area.top(), new_cell);
+1 -1
View File
@@ -28,7 +28,7 @@ impl RinkColor {
} else { } else {
let [sr, sg, sb] = to_rgb(self.color).map(|e| e as u16); 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 [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; let rsa = 255 - sa;
Color::Rgb( Color::Rgb(
((sr * sa + or * rsa) / 255) as u8, ((sr * sa + or * rsa) / 255) as u8,
+1 -1
View File
@@ -31,8 +31,8 @@
use dioxus_core::{Attribute, VNode}; use dioxus_core::{Attribute, VNode};
use dioxus_native_core::{ use dioxus_native_core::{
client_tree::PushedDownState,
layout_attributes::{parse_value, UnitSystem}, layout_attributes::{parse_value, UnitSystem},
real_dom::PushedDownState,
}; };
use crate::style::{RinkColor, RinkStyle}; use crate::style::{RinkColor, RinkStyle};