Moving Window to new RenderEngine subscription, remove old reconciler, clean up how root nodes are registered in the system to begin with since it was messy as hell
This commit is contained in:
parent
bb44f31dda
commit
73860eccdf
5 changed files with 48 additions and 426 deletions
|
|
@ -32,8 +32,6 @@ use app::App;
|
|||
pub mod components;
|
||||
pub use components::{Fragment, Text, View};
|
||||
|
||||
pub(crate) mod reconciler;
|
||||
|
||||
pub mod window;
|
||||
pub use window::Window;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,377 +0,0 @@
|
|||
//! Implements tree diffing, and attempts to cache Component instances where
|
||||
//! possible.
|
||||
|
||||
use std::error::Error;
|
||||
use std::mem::{discriminant, swap};
|
||||
|
||||
use alchemy_styles::Stretch;
|
||||
use alchemy_styles::styles::Style;
|
||||
|
||||
use alchemy_lifecycle::rsx::{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>> {
|
||||
// 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
|
||||
// different.
|
||||
let is_replace = match discriminant(&old) != discriminant(&new) {
|
||||
true => true,
|
||||
false => {
|
||||
if let (RSX::VirtualNode(old_element), RSX::VirtualNode(new_element)) = (&old, &new) {
|
||||
old_element.tag != new_element.tag
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match (old, new) {
|
||||
(RSX::VirtualNode(mut old_element), RSX::VirtualNode(mut new_element)) => {
|
||||
if is_replace {
|
||||
// Do something different in here...
|
||||
//let mut mounted = mount_component_tree(new_tree);
|
||||
// unmount_component_tree(old_tree);
|
||||
// Swap them in memory, copy any layout + etc as necessary
|
||||
// append, link layout nodes, etc
|
||||
return Ok(RSX::VirtualNode(new_element));
|
||||
}
|
||||
|
||||
// If we get here, it's an update to an existing element. This means a cached Component
|
||||
// instance might exist, and we want to keep it around and reuse it if possible. Let's check
|
||||
// and do some swapping action to handle it.
|
||||
//
|
||||
// These need to move to the new tree, since we always keep 'em. We also wanna cache a
|
||||
// reference to our content view.
|
||||
swap(&mut old_element.instance, &mut new_element.instance);
|
||||
swap(&mut old_element.layout_node, &mut new_element.layout_node);
|
||||
|
||||
// For the root tag, which is usually the content view of the Window, we don't want to
|
||||
// perform the whole render/component lifecycle routine. It's a special case element,
|
||||
// where the Window (or other root element) patches in the output of a render method
|
||||
// specific to that object. An easy way to handle this is the depth parameter - in
|
||||
// fact, it's why it exists. Depth 0 should be considered special and skip the
|
||||
// rendering phase.
|
||||
if depth > 0 {
|
||||
// diff props, set new props
|
||||
// instance.get_derived_state_from_props()
|
||||
|
||||
if let Some(instance) = &mut new_element.instance {
|
||||
// diff props, set new props
|
||||
// instance.get_derived_state_from_props()
|
||||
|
||||
//if instance.should_component_update() {
|
||||
// instance.render() { }
|
||||
// instance.get_snapshot_before_update()
|
||||
// apply changes
|
||||
//instance.component_did_update();
|
||||
//} else {
|
||||
// If should_component_update() returns false, then we want to take the
|
||||
// children from the old node, move them to the new node, and recurse into
|
||||
// that tree instead.
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// This None path should never be hit, we just need to use a rather verbose pattern
|
||||
// here. It's unsightly, I know.
|
||||
let is_native_backed = match &new_element.instance {
|
||||
Some(instance) => {
|
||||
let lock = instance.read().unwrap();
|
||||
lock.has_native_backing_node()
|
||||
},
|
||||
None => false
|
||||
};
|
||||
|
||||
// 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
|
||||
// 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());
|
||||
std::mem::swap(&mut children, &mut new_element.children);
|
||||
|
||||
old_element.children.reverse();
|
||||
for new_child_tree in children {
|
||||
match old_element.children.pop() {
|
||||
// A matching child in the old tree means we can recurse right back into the
|
||||
// update phase.
|
||||
Some(old_child_tree) => {
|
||||
let updated = diff_and_patch_tree(old_child_tree, new_child_tree, stretch, depth + 1)?;
|
||||
new_element.children.push(updated);
|
||||
},
|
||||
|
||||
// If there's no matching child in the old tree, this is a new Component and we
|
||||
// can feel free to mount/connect it.
|
||||
None => {
|
||||
if let RSX::VirtualNode(new_el) = new_child_tree {
|
||||
let mut mounted = mount_component_tree(new_el, stretch)?;
|
||||
|
||||
// Link the layout nodes, handle the appending, etc.
|
||||
// This happens inside mount_component_tree, but that only handles that
|
||||
// specific tree. Think of this step as joining two trees in the graph.
|
||||
if is_native_backed {
|
||||
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
|
||||
}
|
||||
|
||||
new_element.children.push(RSX::VirtualNode(mounted));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trim the fat - more children in the old tree than the new one means we gonna be
|
||||
// droppin'. We need to send unmount lifecycle calls to these, and break any links we
|
||||
// have (e.g, layout, backing view tree, etc).
|
||||
loop {
|
||||
match old_element.children.pop() {
|
||||
Some(child) => {
|
||||
if let RSX::VirtualNode(mut old_child) = child {
|
||||
unmount_component_tree(&mut old_child, stretch)?;
|
||||
}
|
||||
},
|
||||
|
||||
None => { break; }
|
||||
}
|
||||
}
|
||||
|
||||
Ok(RSX::VirtualNode(new_element))
|
||||
}
|
||||
|
||||
// We're comparing two text nodes. Realistically... this requires nothing from us, because
|
||||
// the <Text> tag (or any other component instance, if it desires) should handle it.
|
||||
(RSX::VirtualText(_), RSX::VirtualText(text)) => {
|
||||
Ok(RSX::VirtualText(text))
|
||||
}
|
||||
|
||||
// These are all edge cases that shouldn't get hit. In particular:
|
||||
//
|
||||
// - VirtualText being replaced by VirtualNode should be caught by the discriminant check
|
||||
// in the beginning of this function, which registers as a replace/mount.
|
||||
// - VirtualNode being replaced with VirtualText is the same scenario as above.
|
||||
// - The (RSX::None, ...) checks are to shut the compiler up; we never store the RSX::None
|
||||
// return value, as it's mostly a value in place for return signature usability. Thus,
|
||||
// these should quite literally never register.
|
||||
//
|
||||
// This goes without saying, but: never ever store RSX::None lol
|
||||
(RSX::VirtualText(_), RSX::VirtualNode(_)) | (RSX::VirtualNode(_), RSX::VirtualText(_)) |
|
||||
(RSX::None, RSX::VirtualText(_)) | (RSX::None, RSX::VirtualNode(_)) | (RSX::None, RSX::None) |
|
||||
(RSX::VirtualNode(_), RSX::None) | (RSX::VirtualText(_), RSX::None) => {
|
||||
unreachable!("Unequal variant discriminants should already have been handled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) -> Result<(), Box<Error>> {
|
||||
if let (Some(layout_node), Some(instance)) = (node.layout_node, &node.instance) {
|
||||
let component = instance.write().unwrap();
|
||||
component.apply_styles(
|
||||
layout_manager.layout(layout_node)?,
|
||||
layout_manager.style(layout_node)?
|
||||
);
|
||||
}
|
||||
|
||||
for child in &node.children {
|
||||
if let RSX::VirtualNode(child_node) = child {
|
||||
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.
|
||||
/// 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>> {
|
||||
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) {
|
||||
stretch.add_child(*parent_layout_node, *child_layout_node)?;
|
||||
|
||||
let parent_component = parent_instance.write().unwrap();
|
||||
let child_component = child_instance.read().unwrap();
|
||||
parent_component.append_child_component(&*child_component);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
for child in child_tree.children.iter_mut() {
|
||||
if let RSX::VirtualNode(child_tree) = child {
|
||||
find_and_link_layout_nodes(parent_node, child_tree, stretch)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Recursively constructs a Component tree. This entails adding it to the backing
|
||||
/// view tree, firing various lifecycle methods, and ensuring that nodes for layout
|
||||
/// passes are configured.
|
||||
///
|
||||
/// In the future, this would ideally return patch-sets for the backing layer or something.
|
||||
fn mount_component_tree(mut new_element: VirtualNode, stretch: &mut Stretch) -> Result<VirtualNode, Box<Error>> {
|
||||
let instance = (new_element.create_component_fn)();
|
||||
|
||||
let mut is_native_backed = false;
|
||||
|
||||
let rendered = {
|
||||
let component = instance.read().unwrap();
|
||||
// instance.get_derived_state_from_props(props)
|
||||
|
||||
is_native_backed = component.has_native_backing_node();
|
||||
|
||||
if is_native_backed {
|
||||
let mut style = Style::default();
|
||||
|
||||
let layout_node = stretch.new_node(style, vec![])?;
|
||||
new_element.layout_node = Some(layout_node);
|
||||
}
|
||||
|
||||
component.render(&new_element.props)
|
||||
};
|
||||
|
||||
// instance.get_snapshot_before_update()
|
||||
|
||||
new_element.instance = Some(instance);
|
||||
|
||||
let mut children = match rendered {
|
||||
Ok(opt) => match opt {
|
||||
RSX::VirtualNode(child) => {
|
||||
let mut children = vec![];
|
||||
|
||||
// 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" {
|
||||
for child_node in child.props.children {
|
||||
if let RSX::VirtualNode(node) = child_node {
|
||||
let mut mounted = mount_component_tree(node, stretch)?;
|
||||
|
||||
if is_native_backed {
|
||||
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
|
||||
}
|
||||
|
||||
children.push(RSX::VirtualNode(mounted));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut mounted = mount_component_tree(child, stretch)?;
|
||||
|
||||
if is_native_backed {
|
||||
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
|
||||
}
|
||||
|
||||
children.push(RSX::VirtualNode(mounted));
|
||||
}
|
||||
|
||||
children
|
||||
},
|
||||
|
||||
// If a Component renders nothing (or this is a Text string, which we do nothing with)
|
||||
// that's totally fine.
|
||||
_ => vec![]
|
||||
},
|
||||
|
||||
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);
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
new_element.children.append(&mut children);
|
||||
|
||||
if let Some(instance) = &mut new_element.instance {
|
||||
let mut component = instance.write().unwrap();
|
||||
component.component_did_mount(&new_element.props);
|
||||
}
|
||||
|
||||
Ok(new_element)
|
||||
}
|
||||
|
||||
/// Walk the tree and unmount Component instances. This means we fire the
|
||||
/// `component_will_unmount` hook and remove the node(s) from their respective trees.
|
||||
///
|
||||
/// This fires the hooks from a recursive inward-out pattern; that is, the deepest nodes in the tree
|
||||
/// are the first to go, ensuring that everything is properly cleaned up.
|
||||
fn unmount_component_tree(old_element: &mut VirtualNode, stretch: &mut Stretch) -> Result<(), Box<Error>> {
|
||||
// We only need to recurse on VirtualNodes. Text and so on will automagically drop
|
||||
// because we don't support freeform text, it has to be inside a <Text> at all times.
|
||||
for child in old_element.children.iter_mut() {
|
||||
if let RSX::VirtualNode(child_element) = child {
|
||||
unmount_component_tree(child_element, stretch)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire the appropriate lifecycle method and then remove the node from the underlying
|
||||
// graph. Remember that a Component can actually not necessarily have a native backing
|
||||
// node, hence our necessary check.
|
||||
if let Some(old_component) = &mut old_element.instance {
|
||||
let mut component = old_component.write().unwrap();
|
||||
component.component_will_unmount(&old_element.props);
|
||||
|
||||
/*if let Some(view) = old_component.get_native_backing_node() {
|
||||
if let Some(native_view) = replace_native_view {
|
||||
//replace_view(&view, &native_view);
|
||||
} else {
|
||||
//remove_view(&view);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
// Rather than try to keep track of parent/child stuff for removal... just obliterate it,
|
||||
// the underlying library does a good job of killing the links anyway.
|
||||
if let Some(layout_node) = &mut old_element.layout_node {
|
||||
stretch.set_children(*layout_node, vec![])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*let mut add_attributes: HashMap<&str, &str> = HashMap::new();
|
||||
let mut remove_attributes: Vec<&str> = vec![];
|
||||
|
||||
// TODO: -> split out into func
|
||||
for (new_attr_name, new_attr_val) in new_element.attrs.iter() {
|
||||
match old_element.attrs.get(new_attr_name) {
|
||||
Some(ref old_attr_val) => {
|
||||
if old_attr_val != &new_attr_val {
|
||||
add_attributes.insert(new_attr_name, new_attr_val);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
add_attributes.insert(new_attr_name, new_attr_val);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: -> split out into func
|
||||
for (old_attr_name, old_attr_val) in old_element.attrs.iter() {
|
||||
if add_attributes.get(&old_attr_name[..]).is_some() {
|
||||
continue;
|
||||
};
|
||||
|
||||
match new_element.attrs.get(old_attr_name) {
|
||||
Some(ref new_attr_val) => {
|
||||
if new_attr_val != &old_attr_val {
|
||||
remove_attributes.push(old_attr_name);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
remove_attributes.push(old_attr_name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if add_attributes.len() > 0 {
|
||||
patches.push(Patch::AddAttributes(*cur_node_idx, add_attributes));
|
||||
}
|
||||
if remove_attributes.len() > 0 {
|
||||
patches.push(Patch::RemoveAttributes(*cur_node_idx, remove_attributes));
|
||||
}*/
|
||||
|
|
@ -3,52 +3,20 @@
|
|||
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use alchemy_lifecycle::traits::{Component, WindowDelegate};
|
||||
use alchemy_lifecycle::{Uuid, RENDER_ENGINE};
|
||||
use alchemy_lifecycle::rsx::{Props, RSX};
|
||||
use alchemy_lifecycle::traits::{Component, WindowDelegate};
|
||||
|
||||
use alchemy_styles::Stretch;
|
||||
use alchemy_styles::number::Number;
|
||||
use alchemy_styles::geometry::Size;
|
||||
use alchemy_styles::styles::{Style, Dimension};
|
||||
|
||||
use crate::{App, SHARED_APP};
|
||||
use crate::components::View;
|
||||
use crate::reconciler::{diff_and_patch_tree, walk_and_apply_styles};
|
||||
|
||||
#[cfg(feature = "cocoa")]
|
||||
use alchemy_cocoa::window::{Window as PlatformWindowBridge};
|
||||
|
||||
/// Utility function for creating a root_node.
|
||||
fn create_root_node(instance: Option<Arc<RwLock<Component>>>, layout_manager: &mut Stretch) -> RSX {
|
||||
let mut props = Props::default();
|
||||
props.styles = "root".into();
|
||||
|
||||
let mut root_node = RSX::node("root", || Arc::new(RwLock::new(View::default())), props);
|
||||
|
||||
if let RSX::VirtualNode(root) = &mut root_node {
|
||||
root.layout_node = match instance.is_some() {
|
||||
true => {
|
||||
let mut style = Style::default();
|
||||
style.size = Size {
|
||||
width: Dimension::Points(600.),
|
||||
height: Dimension::Points(600.)
|
||||
};
|
||||
|
||||
match layout_manager.new_node(style, vec![]) {
|
||||
Ok(node) => Some(node),
|
||||
Err(e) => { None }
|
||||
}
|
||||
},
|
||||
|
||||
false => None
|
||||
};
|
||||
|
||||
root.instance = instance;
|
||||
}
|
||||
|
||||
root_node
|
||||
}
|
||||
|
||||
/// AppWindow contains the inner details of a Window. It's guarded by a Mutex on `Window`,
|
||||
/// and you shouldn't create this yourself, but it's documented here so you can understand what
|
||||
/// it holds.
|
||||
|
|
@ -57,8 +25,7 @@ pub struct AppWindow {
|
|||
pub title: String,
|
||||
pub bridge: PlatformWindowBridge,
|
||||
pub delegate: Box<WindowDelegate>,
|
||||
pub root_node: RSX,
|
||||
pub layout: Stretch
|
||||
pub render_key: Uuid
|
||||
}
|
||||
|
||||
impl AppWindow {
|
||||
|
|
@ -70,6 +37,7 @@ impl AppWindow {
|
|||
/// This method is called on the `show` event, and in rare cases can be useful to call
|
||||
/// directly.
|
||||
pub fn render(&mut self) {
|
||||
/*
|
||||
let mut new_root_node = create_root_node(None, &mut self.layout);
|
||||
|
||||
// For API reasons, we'll call the render for this Window, and then patch it into a new
|
||||
|
|
@ -106,6 +74,7 @@ impl AppWindow {
|
|||
};
|
||||
|
||||
self.configure_and_apply_styles();
|
||||
*/
|
||||
}
|
||||
|
||||
/// Walks the tree again, purely concerning itself with calculating layout and applying styles.
|
||||
|
|
@ -119,12 +88,12 @@ impl AppWindow {
|
|||
height: Number::Defined(600.)
|
||||
};
|
||||
|
||||
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 {
|
||||
self.layout.compute_layout(*layout_node, window_size)?;
|
||||
walk_and_apply_styles(&root_node, &mut self.layout)?;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -149,18 +118,17 @@ impl Window {
|
|||
/// Creates a new window.
|
||||
pub fn new<S: 'static + WindowDelegate>(title: &str, dimensions: (f64, f64, f64, f64), delegate: S) -> Window {
|
||||
let window_id = SHARED_APP.windows.allocate_new_window_id();
|
||||
let mut layout = Stretch::new();
|
||||
let view = View::default();
|
||||
let shared_app_ptr: *const App = &**SHARED_APP;
|
||||
let bridge = PlatformWindowBridge::new(window_id, title, dimensions, &view, shared_app_ptr);
|
||||
let key = RENDER_ENGINE.register_root_component(view);
|
||||
|
||||
Window(Arc::new(Mutex::new(AppWindow {
|
||||
id: window_id,
|
||||
title: title.into(),
|
||||
bridge: bridge,
|
||||
delegate: Box::new(delegate),
|
||||
root_node: create_root_node(Some(Arc::new(RwLock::new(view))), &mut layout),
|
||||
layout: layout
|
||||
render_key: key
|
||||
})))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
//! This crate also includes the diffing and patching system for the widget tree -
|
||||
//! it needs to live with the `Component` lifecycle to enable state updating.
|
||||
|
||||
pub use uuid::Uuid;
|
||||
|
||||
use alchemy_styles::lazy_static;
|
||||
|
||||
pub mod error;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Implements tree diffing, and attempts to cache Component instances where
|
||||
//! possible.
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::mem::{discriminant, swap};
|
||||
|
|
@ -9,9 +9,18 @@ use std::mem::{discriminant, swap};
|
|||
use uuid::Uuid;
|
||||
|
||||
use alchemy_styles::{Stretch, THEME_ENGINE};
|
||||
use alchemy_styles::styles::Style;
|
||||
use alchemy_styles::styles::{Style, Dimension};
|
||||
use alchemy_styles::number::Number;
|
||||
use alchemy_styles::geometry::Size;
|
||||
|
||||
use crate::rsx::{RSX, VirtualNode};
|
||||
use crate::traits::Component;
|
||||
use crate::rsx::{Props, RSX, VirtualNode};
|
||||
|
||||
// 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 {}
|
||||
|
||||
pub struct RenderEngine {
|
||||
pending_state_updates: Mutex<Vec<i32>>,
|
||||
|
|
@ -31,11 +40,33 @@ impl RenderEngine {
|
|||
/// they get a key back. When they want to instruct the global `RenderEngine`
|
||||
/// to re-render or update their tree, they pass that key and whatever the new tree
|
||||
/// should be.
|
||||
pub fn register(&self, root: RSX) -> Uuid {
|
||||
pub fn register_root_component<C: Component + 'static>(&self, instance: C) -> Uuid {
|
||||
let mut root_node = RSX::node("root", || {
|
||||
Arc::new(RwLock::new(StubView {}))
|
||||
}, {
|
||||
let mut props = Props::default();
|
||||
props.styles = "root".into();
|
||||
props
|
||||
});
|
||||
|
||||
let mut stretch = Stretch::new();
|
||||
if let RSX::VirtualNode(root) = &mut root_node {
|
||||
let mut style = Style::default();
|
||||
style.size = Size {
|
||||
width: Dimension::Points(600.),
|
||||
height: Dimension::Points(600.)
|
||||
};
|
||||
|
||||
root.instance = Some(Arc::new(RwLock::new(instance)));
|
||||
root.layout_node = match stretch.new_node(style, vec![]) {
|
||||
Ok(node) => Some(node),
|
||||
Err(e) => { None }
|
||||
}
|
||||
}
|
||||
|
||||
let key = Uuid::new_v4();
|
||||
let stretch = Stretch::new();
|
||||
let mut trees = self.trees.lock().unwrap();
|
||||
trees.insert(key, (root, stretch));
|
||||
trees.insert(key, (root_node, stretch));
|
||||
key
|
||||
}
|
||||
|
||||
|
|
|
|||
Reference in a new issue