finishing touches and benchmarks
This commit is contained in:
1 parent
05d04165ba
commit
5b25500c0b
15 files changed
+305
-206
No files matched your search
@@ -1,89 +0,0 @@
|
||||
use crate::client_tree::BubbledUpState;
|
||||
use dioxus_core::*;
|
||||
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
|
||||
*/
|
||||
#[derive(Clone, PartialEq, Default, Debug)]
|
||||
pub struct StretchLayout {
|
||||
pub style: Style,
|
||||
pub node: Option<Node>,
|
||||
}
|
||||
|
||||
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
|
||||
fn reduce<'a, I>(&mut self, children: I, vnode: &VNode, stretch: &mut Self::Ctx)
|
||||
where
|
||||
I: Iterator<Item = &'a Self>,
|
||||
Self: 'a,
|
||||
{
|
||||
match vnode {
|
||||
VNode::Text(t) => {
|
||||
let char_len = t.text.chars().count();
|
||||
|
||||
let style = Style {
|
||||
size: Size {
|
||||
// characters are 1 point tall
|
||||
height: Dimension::Points(1.0),
|
||||
|
||||
// text is as long as it is declared
|
||||
width: Dimension::Points(char_len as f32),
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(n) = self.node {
|
||||
if self.style != style {
|
||||
stretch.set_style(n, style).unwrap();
|
||||
}
|
||||
} else {
|
||||
self.node = Some(stretch.new_node(style, &[]).unwrap());
|
||||
}
|
||||
|
||||
self.style = style;
|
||||
}
|
||||
VNode::Element(el) => {
|
||||
// gather up all the styles from the attribute list
|
||||
let mut style = Style::default();
|
||||
|
||||
for &Attribute { name, value, .. } in el.attributes {
|
||||
apply_layout_attributes(name, value, &mut style);
|
||||
}
|
||||
|
||||
// the root node fills the entire area
|
||||
if el.id.get() == Some(ElementId(0)) {
|
||||
apply_layout_attributes("width", "100%", &mut style);
|
||||
apply_layout_attributes("height", "100%", &mut style);
|
||||
}
|
||||
|
||||
// Set all direct nodes as our children
|
||||
let mut child_layout = vec![];
|
||||
for l in children {
|
||||
child_layout.push(l.node.unwrap());
|
||||
}
|
||||
|
||||
if let Some(n) = self.node {
|
||||
if &stretch.children(n).unwrap() != &child_layout {
|
||||
stretch.set_children(n, &child_layout).unwrap();
|
||||
}
|
||||
if self.style != style {
|
||||
stretch.set_style(n, style).unwrap();
|
||||
}
|
||||
} else {
|
||||
self.node = Some(stretch.new_node(style, &child_layout).unwrap());
|
||||
}
|
||||
|
||||
self.style = style;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
Reference in new issue
Block a user