Almost have new reconciler working, recursion hurts le brain
This commit is contained in:
parent
91266cc841
commit
da96abff6a
10 changed files with 365 additions and 90 deletions
|
|
@ -24,7 +24,6 @@ use alchemy_cocoa::text::{Text as PlatformTextBridge};
|
|||
/// <Text styles=["styleKey1", "styleKey2"] />
|
||||
/// ```
|
||||
pub struct Text {
|
||||
key: ComponentKey,
|
||||
text: String,
|
||||
bridge: Mutex<PlatformTextBridge>
|
||||
}
|
||||
|
|
@ -48,9 +47,8 @@ impl Text {
|
|||
}
|
||||
|
||||
impl Component for Text {
|
||||
fn constructor(key: ComponentKey) -> Text {
|
||||
fn constructor(_: ComponentKey) -> Text {
|
||||
Text {
|
||||
key: key,
|
||||
text: "".into(),
|
||||
bridge: Mutex::new(PlatformTextBridge::new())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ impl AppWindow {
|
|||
}
|
||||
};
|
||||
|
||||
match RENDER_ENGINE.diff_and_render_root(&self.render_key, children) {
|
||||
Ok(_) => {}
|
||||
match RENDER_ENGINE.diff_and_render_root(self.render_key, children) {
|
||||
Ok(_) => { println!("RENDERED!!!!"); }
|
||||
Err(e) => { eprintln!("Error rendering window! {}", e); }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
|
||||
use cocoa::base::{id, nil, YES, NO};
|
||||
use cocoa::base::{id, nil, /*YES,*/ NO};
|
||||
use cocoa::appkit::{NSWindow, NSWindowStyleMask, NSBackingStoreType};
|
||||
use cocoa::foundation::{NSRect, NSPoint, NSSize, NSString, NSAutoreleasePool};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
//! run. These are mostly internal to the rendering engine itself, but could potentially
|
||||
//! show up elsewhere.
|
||||
|
||||
use core::any::Any;
|
||||
|
||||
use crate::reconciler::key::ComponentKey;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
33
lifecycle/src/reconciler/instance.rs
Normal file
33
lifecycle/src/reconciler/instance.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
//! Internal struct used for tracking component instances and their
|
||||
//! associated metadata (layout, appearance, etc).
|
||||
|
||||
use alchemy_styles::Appearance;
|
||||
use alchemy_styles::stretch::node::{Node as LayoutNode};
|
||||
|
||||
use crate::rsx::Props;
|
||||
use crate::traits::Component;
|
||||
|
||||
pub(crate) struct Instance {
|
||||
pub(crate) tag: &'static str,
|
||||
pub(crate) component: Box<Component>,
|
||||
pub(crate) props: Props,
|
||||
pub(crate) appearance: Appearance,
|
||||
pub(crate) layout: Option<LayoutNode>
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
pub(crate) fn new(
|
||||
tag: &'static str,
|
||||
component: Box<Component>,
|
||||
props: Props,
|
||||
layout: Option<LayoutNode>
|
||||
) -> Instance {
|
||||
Instance {
|
||||
tag: tag,
|
||||
component: component,
|
||||
props: props,
|
||||
appearance: Appearance::default(),
|
||||
layout: layout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
//! Implements tree diffing, and attempts to cache Component instances where
|
||||
//! possible.
|
||||
//! Implements tree diffing, updating, and so on. Unlike a lot of the VDom implementations
|
||||
//! you find littered around the web, this is a bit more ECS-ish, and expects Components to retain
|
||||
//! their `ComponentKey` passed in their constructor if they want to update. Doing this
|
||||
//! enables us to avoid re-scanning or diffing an entire tree.
|
||||
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use std::error::Error;
|
||||
use std::mem::{discriminant, swap};
|
||||
|
||||
use alchemy_styles::THEME_ENGINE;
|
||||
use alchemy_styles::styles::{Appearance,Dimension, Number, Size, Style};
|
||||
use alchemy_styles::styles::{Appearance, Number, Size, Style};
|
||||
|
||||
use crate::traits::Component;
|
||||
use crate::rsx::{Props, RSX, VirtualNode};
|
||||
|
||||
use alchemy_styles::stretch::node::{Node as StyleNode, Stretch as LayoutStore};
|
||||
use alchemy_styles::stretch::node::{Node as LayoutNode, Stretch as LayoutStore};
|
||||
|
||||
pub mod key;
|
||||
use key::ComponentKey;
|
||||
|
|
@ -23,13 +23,17 @@ use storage::ComponentStore;
|
|||
pub mod error;
|
||||
use error::RenderEngineError;
|
||||
|
||||
// This is never actually created, it's just to satisfy the fact that View
|
||||
// is defined in the core crate, which we can't import here without creating a
|
||||
// circular dependency.
|
||||
struct StubView;
|
||||
impl Component for StubView {
|
||||
fn constructor(key: ComponentKey) -> StubView {
|
||||
StubView {}
|
||||
mod instance;
|
||||
use instance::Instance;
|
||||
|
||||
/// This is never actually created, and is here primarily to avoid a circular
|
||||
/// depedency issue (we can't import the View from alchemy's core crate, since the core crate
|
||||
/// depends on this crate).
|
||||
|
||||
pub struct GenericRootView;
|
||||
impl Component for GenericRootView {
|
||||
fn constructor(key: ComponentKey) -> GenericRootView {
|
||||
GenericRootView {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,66 +68,307 @@ impl RenderEngine {
|
|||
return Err(Box::new(RenderEngineError::InvalidRootComponent {}));
|
||||
}
|
||||
|
||||
let layout_key = {
|
||||
let mut component_store = self.components.lock().unwrap();
|
||||
let component_key = component_store.new_key();
|
||||
component_store.insert(component_key, Instance::new("root", Box::new(instance), {
|
||||
let mut props = Props::default();
|
||||
props.styles = "root".into();
|
||||
props
|
||||
}, {
|
||||
let mut layouts_store = self.layouts.lock().unwrap();
|
||||
let style = Style::default();
|
||||
let mut layouts = self.layouts.lock().unwrap();
|
||||
Some(layouts.new_node(style, vec![])?)
|
||||
};
|
||||
Some(layouts_store.new_node(style, vec![])?)
|
||||
}))?;
|
||||
|
||||
let mut components = self.components.lock().unwrap();
|
||||
let component_key = components.new_node(instance, layout_key, vec![])?;
|
||||
Ok(component_key)
|
||||
}
|
||||
|
||||
/// Given a key, and a new root tree, will diff the tree structure (position, components,
|
||||
/// attributes and so on), and then queue the changes for application to the backing
|
||||
/// framework tree. As it goes through the tree, if a `Component` at a given position
|
||||
/// in the two trees is deemed to be the same, it will move instances from the old tree to
|
||||
/// the new tree before discarding the old tree.
|
||||
///
|
||||
/// This calls the necessary component lifecycles per-component.
|
||||
pub fn diff_and_render_root(&self, key: &ComponentKey, child: RSX) -> Result<(), Box<Error>> {
|
||||
/*
|
||||
let mut new_root = RSX::node("root", || {
|
||||
Box::new(StubView {})
|
||||
}, {
|
||||
/// Rendering the root node is a bit different than rendering or updating other nodes, as we
|
||||
/// never want to unmount it, and the results come from a non-`Component` entity (e.g, a
|
||||
/// `Window`). Thus, for this one, we do some manual mucking with what we know is the
|
||||
/// root view (a `Window` or such root component would call this with it's registered
|
||||
/// `ComponentKey`), and then recurse based on the children.
|
||||
pub fn diff_and_render_root(&self, key: ComponentKey, child: RSX) -> Result<(), Box<Error>> {
|
||||
let mut component_store = self.components.lock().unwrap();
|
||||
let mut layout_store = self.layouts.lock().unwrap();
|
||||
|
||||
println!("Child: {:?}", child);
|
||||
let new_root_node = RSX::node("root", |_| Box::new(GenericRootView {}), {
|
||||
let mut props = Props::default();
|
||||
props.styles = "root".into();
|
||||
props
|
||||
}, match child {
|
||||
RSX::VirtualNode(mut child) => {
|
||||
let mut children = vec![];
|
||||
|
||||
if child.tag == "Fragment" {
|
||||
children.append(&mut child.children);
|
||||
RSX::VirtualNode(node) => {
|
||||
if node.tag == "Fragment" {
|
||||
node.children
|
||||
} else {
|
||||
children.push(RSX::VirtualNode(child));
|
||||
println!("Def here...");
|
||||
vec![RSX::VirtualNode(node)]
|
||||
}
|
||||
|
||||
children
|
||||
},
|
||||
|
||||
// If it's an RSX::None or RSX::VirtualText, we'll just do nothing, as... one
|
||||
// requires nothing, and one isn't supported unless it's inside a <Text> tag, and
|
||||
// we know the root element isn't a <Text> if we're here.
|
||||
_ => vec![]
|
||||
});
|
||||
|
||||
let mut trees = self.trees.lock().unwrap();
|
||||
let (old_root, mut stretch) = trees.remove(key).ok_or_else(|| RenderEngineError::InvalidKey {})?;
|
||||
let patched_new_root = diff_and_patch_trees(old_root, new_root, &mut stretch, 0)?;
|
||||
recursively_diff_tree(key, new_root_node, &mut component_store, &mut layout_store)?;
|
||||
|
||||
if let RSX::VirtualNode(node) = &patched_new_root {
|
||||
if let Some(layout_node) = &node.layout_node {
|
||||
stretch.compute_layout(*layout_node, Size {
|
||||
let layout_node = {
|
||||
let root_instance = component_store.get(key)?;
|
||||
root_instance.layout.unwrap()
|
||||
};
|
||||
|
||||
layout_store.compute_layout(layout_node, Size {
|
||||
width: Number::Defined(600.),
|
||||
height: Number::Defined(600.),
|
||||
height: Number::Defined(600.)
|
||||
})?;
|
||||
walk_and_apply_styles(node, &mut stretch)?;
|
||||
}
|
||||
}
|
||||
|
||||
trees.insert(*key, (patched_new_root, stretch));*/
|
||||
println!("Applying layout...");
|
||||
walk_and_apply_styles(key, &mut component_store, &mut layout_store)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Given two trees, will diff them to see if we need to replace or update. Depending on the
|
||||
/// result, we'll either recurse down a level, or tear down and build up a new tree. The final
|
||||
/// parameter on this method, `is_root_entity_view`, should only be passed for `Window` or other
|
||||
/// such instances, as it instructs us to skip the first level since these ones act different.
|
||||
fn recursively_diff_tree(
|
||||
key: ComponentKey,
|
||||
new_tree: RSX,
|
||||
component_store: &mut ComponentStore,
|
||||
layout_store: &mut LayoutStore
|
||||
) -> Result<(), Box<Error>> {
|
||||
// First we need to determine if this node is being replaced or updated. A replace happens if
|
||||
// two nodes are different types - in this case, we check their tag values. This is also a case
|
||||
// where, for instance, if the RSX tag is `::None` or `::VirtualText`, we'll treat it as
|
||||
// replacing with nothing.
|
||||
let is_replace = match &new_tree {
|
||||
RSX::VirtualNode(new_tree) => {
|
||||
let old_tree = component_store.get(key)?;
|
||||
old_tree.tag != new_tree.tag
|
||||
},
|
||||
|
||||
// The algorithm will know below not to recurse if we're trying to diff text or empty
|
||||
// values. We return false here to avoid entering the `is_replace` phase; `Component`
|
||||
// instances (like <Text />) handle taking the child VirtualText instances and working with
|
||||
// them to pass to a native widget.
|
||||
_ => false
|
||||
};
|
||||
|
||||
if is_replace {
|
||||
println!("here, what?!");
|
||||
unmount_component_tree(key, component_store, layout_store)?;
|
||||
//mount_component_tree(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// At this point, we know it's an update pass. Now we need to do a few things:
|
||||
//
|
||||
// - Diff our `props` and figure out what actions we can take or shortcut.
|
||||
// - Let the `Component` instance determine what it should render.
|
||||
// - Recurse into the child trees if necessary.
|
||||
let mut old_children = component_store.children(key)?;
|
||||
old_children.reverse();
|
||||
|
||||
if let RSX::VirtualNode(mut child) = new_tree {
|
||||
for new_child_tree in child.children {
|
||||
match old_children.pop() {
|
||||
// If there's a key in the old children for this position, it's
|
||||
// something we need to update, so let's recurse right back into it.
|
||||
Some(old_child_key) => {
|
||||
recursively_diff_tree(
|
||||
old_child_key,
|
||||
new_child_tree,
|
||||
component_store,
|
||||
layout_store
|
||||
)?;
|
||||
},
|
||||
|
||||
// If there's no matching old key in this position, then we've got a
|
||||
// new component instance to mount. This part now diverts into the Mount
|
||||
// phase.
|
||||
None => {
|
||||
if let RSX::VirtualNode(tr33amimustfeelohlol) = new_child_tree {
|
||||
let new_child_key = mount_component_tree(
|
||||
tr33amimustfeelohlol,
|
||||
component_store,
|
||||
layout_store
|
||||
)?;
|
||||
|
||||
link_nodes(key, new_child_key, component_store, layout_store)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trim the fat. If we still have child nodes after diffing in the new child trees,
|
||||
// then they're ones that simply need to be unmounted and dropped.
|
||||
if old_children.len() > 0 {
|
||||
for child in old_children {
|
||||
unmount_component_tree(child, component_store, layout_store)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Given a new `RSX` tree, a `ComponentStore`, and a `LayoutStore`, will recursively construct the
|
||||
/// tree, emitting required lifecycle events and persisting values. This happens in an inward-out
|
||||
/// fashion, which helps avoid unnecessary reflow in environments where it can get tricky.
|
||||
///
|
||||
/// This method returns a Result, the `Ok` variant containing a tuple of Vecs. These are the child
|
||||
/// Component instances and Layout instances that need to be set in the stores.
|
||||
fn mount_component_tree(
|
||||
tree: VirtualNode,
|
||||
component_store: &mut ComponentStore,
|
||||
layout_store: &mut LayoutStore
|
||||
) -> Result<ComponentKey, Box<Error>> {
|
||||
println!(" Mounting Component");
|
||||
let key = component_store.new_key();
|
||||
let component = (tree.create_component_fn)(key);
|
||||
let is_native_backed = component.has_native_backing_node();
|
||||
|
||||
// let state = get_derived_state_from_props()
|
||||
let mut instance = Instance::new(tree.tag, component, tree.props, None);
|
||||
if is_native_backed {
|
||||
let mut style = Style::default();
|
||||
THEME_ENGINE.configure_styles_for_keys(&instance.props.styles, &mut style, &mut instance.appearance);
|
||||
instance.layout = Some(layout_store.new_node(style, vec![])?);
|
||||
}
|
||||
|
||||
let rendered = instance.component.render(&instance.props);
|
||||
// instance.get_snapshot_before_update()
|
||||
println!("Rendered... {}", instance.tag);
|
||||
component_store.insert(key, instance)?;
|
||||
|
||||
match rendered {
|
||||
Ok(child) => if let RSX::VirtualNode(child) = child {
|
||||
// We want to support Components being able to return arbitrary iteratable
|
||||
// elements, but... well, it's not quite that simple. Thus we'll offer a <Fragment>
|
||||
// tag similar to what React does, which just hoists the children out of it and
|
||||
// discards the rest.
|
||||
if child.tag == "Fragment" {
|
||||
println!(" In Fragment {}", child.children.len());
|
||||
for child_tree in child.children {
|
||||
println!(" > WHAT");
|
||||
if let RSX::VirtualNode(child_tree) = child_tree {
|
||||
let child_key = mount_component_tree(child_tree, component_store, layout_store)?;
|
||||
|
||||
if is_native_backed {
|
||||
link_nodes(key, child_key, component_store, layout_store)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!(" In regular");
|
||||
let child_key = mount_component_tree(child, component_store, layout_store)?;
|
||||
if is_native_backed {
|
||||
link_nodes(key, child_key, component_store, layout_store)?;
|
||||
}
|
||||
}
|
||||
} else { println!("WTF"); },
|
||||
|
||||
Err(e) => {
|
||||
// return an RSX::VirtualNode(ErrorComponentView) or something?
|
||||
/* instance.get_derived_state_from_error(e) */
|
||||
// render error state or something I guess?
|
||||
/* instance.component_did_catch(e, info) */
|
||||
eprintln!("Error rendering: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
let instance_lol = component_store.get_mut(key)?;
|
||||
instance_lol.component.component_did_mount(&instance_lol.props);
|
||||
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
/// Given a `ComponentKey`, a `ComponentStore`, and a `LayoutStore`, will recursively walk the tree found at
|
||||
/// said key, emitting required lifecycle events and dropping values. This happens in an inward-out
|
||||
/// fashion, so deepest nodes/components get destroyed first to ensure that the backing widget tree
|
||||
/// doesn't get some weird dangling issue.
|
||||
fn unmount_component_tree(
|
||||
key: ComponentKey,
|
||||
component_store: &mut ComponentStore,
|
||||
layout_store: &mut LayoutStore
|
||||
) -> Result<Vec<LayoutNode>, Box<Error>> {
|
||||
let mut instance = component_store.remove(key)?;
|
||||
instance.component.component_will_unmount(&instance.props);
|
||||
|
||||
let mut layout_nodes = vec![];
|
||||
|
||||
let children = component_store.children(key)?;
|
||||
for child in children {
|
||||
match unmount_component_tree(child, component_store, layout_store) {
|
||||
Ok(mut child_layout_nodes) => {
|
||||
if let Some(parent_layout_node) = instance.layout {
|
||||
for node in child_layout_nodes {
|
||||
layout_store.remove_child(parent_layout_node, node)?;
|
||||
}
|
||||
} else {
|
||||
layout_nodes.append(&mut child_layout_nodes);
|
||||
}
|
||||
},
|
||||
|
||||
Err(e) => { eprintln!("Error unmounting a component tree: {}", e); }
|
||||
}
|
||||
}
|
||||
|
||||
// remove node from backing tree
|
||||
|
||||
Ok(layout_nodes)
|
||||
}
|
||||
|
||||
/// 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. This could
|
||||
/// potentially be done away with some hoisting magic in the `mount()` recursion, but I couldn't
|
||||
/// find a pattern that didn't feel like some utter magic in Rust.
|
||||
///
|
||||
/// It might be because I'm writing this at 3AM. Feel free to improve it.
|
||||
fn link_nodes(
|
||||
parent: ComponentKey,
|
||||
child: ComponentKey,
|
||||
components: &mut ComponentStore,
|
||||
layouts: &mut LayoutStore
|
||||
) -> Result<(), Box<Error>> {
|
||||
if let (Ok(parent_instance), Ok(child_instance)) = (components.get(parent), components.get(child)) {
|
||||
if let (Some(parent_layout), Some(child_layout)) = (parent_instance.layout, child_instance.layout) {
|
||||
layouts.add_child(parent_layout, child_layout)?;
|
||||
parent_instance.component.append_child_component(&*child_instance.component);
|
||||
println!("APPENDED NODE!");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let children = components.children(child)?;
|
||||
for child_key in children {
|
||||
link_nodes(parent, child_key, components, layouts)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Walks the tree and passes necessary Layout and Appearance-based styles to Components so they can
|
||||
/// update their backing widgets accordingly. This happens after a layout computation, typically.
|
||||
fn walk_and_apply_styles(
|
||||
key: ComponentKey,
|
||||
components: &mut ComponentStore,
|
||||
layouts: &mut LayoutStore
|
||||
) -> Result<(), Box<Error>> {
|
||||
let instance = components.get_mut(key)?;
|
||||
|
||||
if let Some(layout_key) = instance.layout {
|
||||
instance.component.apply_styles(
|
||||
&instance.appearance,
|
||||
layouts.layout(layout_key)?
|
||||
);
|
||||
}
|
||||
|
||||
for child in components.children(key)? {
|
||||
walk_and_apply_styles(child, components, layouts)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub use alchemy_styles::Appearance;
|
||||
use alchemy_styles::stretch::node::{Node as LayoutNode};
|
||||
|
||||
use crate::reconciler::error::{RenderEngineError as Error};
|
||||
use crate::reconciler::instance::Instance;
|
||||
use crate::reconciler::key::{Allocator, Id, INSTANCE_ALLOCATOR, ComponentKey};
|
||||
use crate::traits::Component;
|
||||
|
||||
/// This is a clone of a structure you'll also find over in stretch. We do this separately
|
||||
/// here for two reasons.
|
||||
|
|
@ -40,6 +39,13 @@ impl<T> Storage<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: ComponentKey) -> Result<T, Error> {
|
||||
match self.0.remove(&key) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::InvalidComponentKey(key))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: ComponentKey, value: T) -> Option<T> {
|
||||
self.0.insert(key, value)
|
||||
}
|
||||
|
|
@ -53,12 +59,6 @@ impl<T> std::ops::Index<&ComponentKey> for Storage<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Instance {
|
||||
component: Box<Component>,
|
||||
appearance: Appearance,
|
||||
layout: Option<LayoutNode>
|
||||
}
|
||||
|
||||
pub(crate) struct ComponentStore {
|
||||
id: Id,
|
||||
nodes: Allocator,
|
||||
|
|
@ -78,28 +78,31 @@ impl ComponentStore {
|
|||
}
|
||||
}
|
||||
|
||||
fn allocate_node(&mut self) -> ComponentKey {
|
||||
pub fn new_key(&mut self) -> ComponentKey {
|
||||
let local = self.nodes.allocate();
|
||||
ComponentKey { instance: self.id, local }
|
||||
}
|
||||
|
||||
pub fn new_node<C: Component + 'static>(&mut self, component: C, layout_key: Option<LayoutNode>, children: Vec<ComponentKey>) -> Result<ComponentKey, Error> {
|
||||
let key = self.allocate_node();
|
||||
|
||||
for child in &children {
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
key: ComponentKey,
|
||||
instance: Instance
|
||||
) -> Result<(), Error> {
|
||||
/*for child in &children {
|
||||
self.parents.get_mut(*child)?.push(key);
|
||||
}*/
|
||||
|
||||
self.components.insert(key, instance);
|
||||
self.parents.insert(key, Vec::with_capacity(1));
|
||||
self.children.insert(key, vec![]); //children);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
self.components.insert(key, Instance {
|
||||
component: Box::new(component),
|
||||
appearance: Appearance::default(),
|
||||
layout: layout_key
|
||||
});
|
||||
|
||||
self.parents.insert(key, Vec::with_capacity(1));
|
||||
self.children.insert(key, children);
|
||||
|
||||
Ok(key)
|
||||
pub fn remove(&mut self, key: ComponentKey) -> Result<Instance, Error> {
|
||||
self.parents.remove(key)?;
|
||||
self.children.remove(key)?;
|
||||
self.components.remove(key)
|
||||
}
|
||||
|
||||
pub fn add_child(&mut self, key: ComponentKey, child: ComponentKey) -> Result<(), Error> {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ impl RSX {
|
|||
RSX::VirtualNode(VirtualNode {
|
||||
tag: tag,
|
||||
create_component_fn: create_fn,
|
||||
props: Some(props),
|
||||
props: props,
|
||||
children: children
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
//! Implements a Props struct that mostly acts as expected. For arbitrary primitive values,
|
||||
//! it shadows a `serde_json::Value`.
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use alchemy_styles::StylesList;
|
||||
|
||||
use crate::rsx::RSX;
|
||||
use crate::traits::{Component};
|
||||
|
||||
/// A value stored inside the `attributes` field on a `Props` instance.
|
||||
/// It shadows `serde_json::Value`, but also allows for some other value
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ pub struct VirtualNode {
|
|||
/// ownership of a VirtualNode.
|
||||
///
|
||||
/// This aspect of functionality may be pulled in a later release if it causes too many issues.
|
||||
pub props: Option<Props>,
|
||||
pub props: Props,
|
||||
|
||||
///
|
||||
pub children: Vec<RSX>
|
||||
|
|
|
|||
Reference in a new issue