Working on cleaning up the reconciliation phase a bit
This commit is contained in:
parent
5c267b37c8
commit
4bf89f5d91
2 changed files with 22 additions and 31 deletions
|
|
@ -1,9 +1,5 @@
|
||||||
//! render/diff.rs
|
|
||||||
//!
|
|
||||||
//! Implements tree diffing, and attempts to cache Component instances where
|
//! Implements tree diffing, and attempts to cache Component instances where
|
||||||
//! possible.
|
//! possible.
|
||||||
//!
|
|
||||||
//! @created 05/03/2019
|
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::mem::{discriminant, swap};
|
use std::mem::{discriminant, swap};
|
||||||
|
|
@ -14,6 +10,7 @@ use alchemy_styles::styles::Style;
|
||||||
use alchemy_lifecycle::traits::Component;
|
use alchemy_lifecycle::traits::Component;
|
||||||
use alchemy_lifecycle::rsx::{StylesList, RSX, VirtualNode};
|
use alchemy_lifecycle::rsx::{StylesList, RSX, VirtualNode};
|
||||||
|
|
||||||
|
/// Given two node trees, will compare, diff, and apply changes in a recursive fashion.
|
||||||
pub fn diff_and_patch_tree(old: RSX, new: RSX, stretch: &mut Stretch, depth: usize) -> Result<RSX, Box<Error>> {
|
pub fn diff_and_patch_tree(old: RSX, new: RSX, stretch: &mut Stretch, depth: usize) -> Result<RSX, Box<Error>> {
|
||||||
// Whether we replace or not depends on a few things. If we're working on two different node
|
// Whether we replace or not depends on a few things. If we're working on two different node
|
||||||
// types (text vs node), if the node tags are different, or if the key (in some cases) is
|
// types (text vs node), if the node tags are different, or if the key (in some cases) is
|
||||||
|
|
@ -76,8 +73,8 @@ pub fn diff_and_patch_tree(old: RSX, new: RSX, stretch: &mut Stretch, depth: usi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This None path should never be hit. If it does, the algorithm is doing something way
|
// This None path should never be hit, we just need to use a rather verbose pattern
|
||||||
// off base.
|
// here. It's unsightly, I know.
|
||||||
let is_native_backed = match &new_element.instance {
|
let is_native_backed = match &new_element.instance {
|
||||||
Some(instance) => instance.has_native_backing_node(),
|
Some(instance) => instance.has_native_backing_node(),
|
||||||
None => false
|
None => false
|
||||||
|
|
@ -86,13 +83,17 @@ pub fn diff_and_patch_tree(old: RSX, new: RSX, stretch: &mut Stretch, depth: usi
|
||||||
// There is probably a nicer way to do this that doesn't allocate as much, and I'm open
|
// There is probably a nicer way to do this that doesn't allocate as much, and I'm open
|
||||||
// to revisiting it. Platforms outside of Rust allocate far more than this, though, and
|
// to revisiting it. Platforms outside of Rust allocate far more than this, though, and
|
||||||
// in general the whole "avoid allocations" thing is fear mongering IMO. Revisit later.
|
// in general the whole "avoid allocations" thing is fear mongering IMO. Revisit later.
|
||||||
|
//
|
||||||
|
// tl;dr we allocate a new Vec<RSX> that's equal to the length of our new children, and
|
||||||
|
// then swap it on our (owned) node... it's safe, as we own it. This allows us to
|
||||||
|
// iterate and dodge the borrow checker.
|
||||||
let mut children: Vec<RSX> = Vec::with_capacity(new_element.children.len());
|
let mut children: Vec<RSX> = Vec::with_capacity(new_element.children.len());
|
||||||
std::mem::swap(&mut children, &mut new_element.children);
|
std::mem::swap(&mut children, &mut new_element.children);
|
||||||
|
|
||||||
old_element.children.reverse();
|
old_element.children.reverse();
|
||||||
for new_child_tree in children {
|
for new_child_tree in children {
|
||||||
match old_element.children.pop() {
|
match old_element.children.pop() {
|
||||||
// A matching child in the old tree means we can pass right back into the
|
// A matching child in the old tree means we can recurse right back into the
|
||||||
// update phase.
|
// update phase.
|
||||||
Some(old_child_tree) => {
|
Some(old_child_tree) => {
|
||||||
let updated = diff_and_patch_tree(old_child_tree, new_child_tree, stretch, depth + 1)?;
|
let updated = diff_and_patch_tree(old_child_tree, new_child_tree, stretch, depth + 1)?;
|
||||||
|
|
@ -108,9 +109,7 @@ pub fn diff_and_patch_tree(old: RSX, new: RSX, stretch: &mut Stretch, depth: usi
|
||||||
// Link the layout nodes, handle the appending, etc.
|
// Link the layout nodes, handle the appending, etc.
|
||||||
// This happens inside mount_component_tree, but that only handles that
|
// This happens inside mount_component_tree, but that only handles that
|
||||||
// specific tree. Think of this step as joining two trees in the graph.
|
// specific tree. Think of this step as joining two trees in the graph.
|
||||||
|
|
||||||
if is_native_backed {
|
if is_native_backed {
|
||||||
println!("Linking 1");
|
|
||||||
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
|
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,13 +138,8 @@ pub fn diff_and_patch_tree(old: RSX, new: RSX, stretch: &mut Stretch, depth: usi
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're comparing two text nodes. Realistically... this requires nothing from us, because
|
// We're comparing two text nodes. Realistically... this requires nothing from us, because
|
||||||
// the <Text> tag should handle it. We'll do a quick sanity check to make sure that it
|
// the <Text> tag (or any other component instance, if it desires) should handle it.
|
||||||
// actually has a parent <Text>, though.
|
|
||||||
(RSX::VirtualText(_), RSX::VirtualText(text)) => {
|
(RSX::VirtualText(_), RSX::VirtualText(text)) => {
|
||||||
//match &parent {
|
|
||||||
// RSX::VirtualText(_) => { panic!("Raw text must be surrounded by a <Text></Text> component!"); },
|
|
||||||
// _ => {}
|
|
||||||
// }
|
|
||||||
Ok(RSX::VirtualText(text))
|
Ok(RSX::VirtualText(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,30 +172,28 @@ fn configure_styles(style_keys: &StylesList, style: &mut Style) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walks the tree and applies styles. This happens after a layout computation, typically.
|
/// Walks the tree and applies styles. This happens after a layout computation, typically.
|
||||||
pub(crate) fn walk_and_apply_styles(node: &VirtualNode, layout_manager: &mut Stretch) {
|
pub(crate) fn walk_and_apply_styles(node: &VirtualNode, layout_manager: &mut Stretch) -> Result<(), Box<Error>> {
|
||||||
if let (Some(layout_node), Some(instance)) = (node.layout_node, &node.instance) {
|
if let (Some(layout_node), Some(instance)) = (node.layout_node, &node.instance) {
|
||||||
match (layout_manager.layout(layout_node), layout_manager.style(layout_node)) {
|
instance.apply_styles(
|
||||||
(Ok(layout), Ok(style)) => { instance.apply_styles(layout, style); },
|
layout_manager.layout(layout_node)?,
|
||||||
(Err(e), Err(e2)) => { eprintln!("Error retrieving computed style? {:?} {:?}", e, e2); },
|
layout_manager.style(layout_node)?
|
||||||
_ => { eprintln!("Error retrieving computed style!"); }
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for child in &node.children {
|
for child in &node.children {
|
||||||
println!("IN CHILD!");
|
|
||||||
if let RSX::VirtualNode(child_node) = child {
|
if let RSX::VirtualNode(child_node) = child {
|
||||||
walk_and_apply_styles(child_node, layout_manager);
|
walk_and_apply_styles(child_node, layout_manager)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a tree, will walk the branches until it finds the next root nodes to connect.
|
/// Given a tree, will walk the branches until it finds the next root nodes to connect.
|
||||||
/// While this sounds slow, in practice it rarely has to go far in any direction.
|
/// While this sounds slow, in practice it rarely has to go far in any direction.
|
||||||
fn find_and_link_layout_nodes(parent_node: &mut VirtualNode, child_tree: &mut VirtualNode, stretch: &mut Stretch) -> Result<(), Box<Error>> {
|
fn find_and_link_layout_nodes(parent_node: &mut VirtualNode, child_tree: &mut VirtualNode, stretch: &mut Stretch) -> Result<(), Box<Error>> {
|
||||||
// First, check if the tree has a layout node we can use...
|
|
||||||
if let (Some(parent_instance), Some(child_instance)) = (&mut parent_node.instance, &mut child_tree.instance) {
|
if let (Some(parent_instance), Some(child_instance)) = (&mut parent_node.instance, &mut child_tree.instance) {
|
||||||
if let (Some(parent_layout_node), Some(child_layout_node)) = (&parent_node.layout_node, &child_tree.layout_node) {
|
if let (Some(parent_layout_node), Some(child_layout_node)) = (&parent_node.layout_node, &child_tree.layout_node) {
|
||||||
println!("--- LINKING");
|
|
||||||
stretch.add_child(*parent_layout_node, *child_layout_node)?;
|
stretch.add_child(*parent_layout_node, *child_layout_node)?;
|
||||||
parent_instance.append_child_component(child_instance);
|
parent_instance.append_child_component(child_instance);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
@ -222,7 +214,6 @@ fn find_and_link_layout_nodes(parent_node: &mut VirtualNode, child_tree: &mut Vi
|
||||||
/// passes are configured.
|
/// passes are configured.
|
||||||
fn mount_component_tree(mut new_element: VirtualNode, stretch: &mut Stretch) -> Result<VirtualNode, Box<Error>> {
|
fn mount_component_tree(mut new_element: VirtualNode, stretch: &mut Stretch) -> Result<VirtualNode, Box<Error>> {
|
||||||
let mut instance = (new_element.create_component_fn)();
|
let mut instance = (new_element.create_component_fn)();
|
||||||
println!("> Mounting {}", new_element.tag);
|
|
||||||
// "compute" props, set on instance
|
// "compute" props, set on instance
|
||||||
// instance.get_derived_state_from_props(props)
|
// instance.get_derived_state_from_props(props)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ impl AppWindow {
|
||||||
/// async, so relying on underlying behavior in here is considered... suspect.
|
/// async, so relying on underlying behavior in here is considered... suspect.
|
||||||
///
|
///
|
||||||
/// This method is called on window resize and show events.
|
/// This method is called on window resize and show events.
|
||||||
pub fn configure_and_apply_styles(&mut self) {
|
fn configure_and_apply_styles(&mut self) -> Result<(), Box<std::error::Error>> {
|
||||||
let window_size = Size {
|
let window_size = Size {
|
||||||
width: Number::Defined(600.),
|
width: Number::Defined(600.),
|
||||||
height: Number::Defined(600.)
|
height: Number::Defined(600.)
|
||||||
|
|
@ -121,12 +121,12 @@ impl AppWindow {
|
||||||
|
|
||||||
if let RSX::VirtualNode(root_node) = &mut self.root_node {
|
if let RSX::VirtualNode(root_node) = &mut self.root_node {
|
||||||
if let Some(layout_node) = &root_node.layout_node {
|
if let Some(layout_node) = &root_node.layout_node {
|
||||||
match &self.layout.compute_layout(*layout_node, window_size) {
|
self.layout.compute_layout(*layout_node, window_size)?;
|
||||||
Ok(_) => { walk_and_apply_styles(&root_node, &mut self.layout); },
|
walk_and_apply_styles(&root_node, &mut self.layout)?;
|
||||||
Err(e) => { eprintln!("Error computing layout: {:?}", e); }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders and calls through to the native platform window show method.
|
/// Renders and calls through to the native platform window show method.
|
||||||
|
|
|
||||||
Reference in a new issue