Slight tweak to how gets created on demand. We use an now, for mutability/immutability needs in the reconciliation process. Not sure offhand how much the double lock of will affect things, but that's a problem for later.

This commit is contained in:
Ryan McGrath 2019-05-24 21:44:35 -07:00
parent 4bf89f5d91
commit c7ef19a943
No known key found for this signature in database
GPG key ID: 811674B62B666830
8 changed files with 53 additions and 43 deletions

View file

@ -3,7 +3,7 @@
//! hence why they're all (somewhat annoyingly, but lovingly) re-implemented
//! as bridges.
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, RwLock};
use alchemy_styles::styles::{Layout, Style};
@ -40,7 +40,7 @@ impl Component for View {
Some(bridge.borrow_native_backing_node())
}
fn append_child_component(&self, component: &Arc<Component>) {
fn append_child_component(&self, component: &Component) {
if let Some(child) = component.borrow_native_backing_node() {
let mut bridge = self.0.lock().unwrap();
bridge.append_child(child);
@ -53,7 +53,7 @@ impl Component for View {
}
fn render(&self, props: &Props) -> Result<RSX, Error> {
Ok(RSX::node("Fragment", || Box::new(Fragment::default()), Props {
Ok(RSX::node("Fragment", || Arc::new(RwLock::new(Fragment::default())), Props {
attributes: std::collections::HashMap::new(),
key: "".into(),
styles: StylesList::new(),

View file

@ -76,7 +76,10 @@ pub fn diff_and_patch_tree(old: RSX, new: RSX, stretch: &mut Stretch, depth: usi
// 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) => instance.has_native_backing_node(),
Some(instance) => {
let lock = instance.read().unwrap();
lock.has_native_backing_node()
},
None => false
};
@ -174,7 +177,8 @@ fn configure_styles(style_keys: &StylesList, style: &mut Style) {
/// 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) {
instance.apply_styles(
let component = instance.write().unwrap();
component.apply_styles(
layout_manager.layout(layout_node)?,
layout_manager.style(layout_node)?
);
@ -195,7 +199,9 @@ fn find_and_link_layout_nodes(parent_node: &mut VirtualNode, child_tree: &mut Vi
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)?;
parent_instance.append_child_component(child_instance);
if let (parent_component, child_component) = (parent_instance.write().unwrap(), child_instance.read().unwrap()) {
parent_component.append_child_component(&*child_component);
}
return Ok(());
}
}
@ -213,25 +219,30 @@ fn find_and_link_layout_nodes(parent_node: &mut VirtualNode, child_tree: &mut Vi
/// view tree, firing various lifecycle methods, and ensuring that nodes for layout
/// passes are configured.
fn mount_component_tree(mut new_element: VirtualNode, stretch: &mut Stretch) -> Result<VirtualNode, Box<Error>> {
let mut instance = (new_element.create_component_fn)();
// "compute" props, set on instance
// instance.get_derived_state_from_props(props)
let instance = (new_element.create_component_fn)();
let is_native_backed = instance.has_native_backing_node();
let mut is_native_backed = false;
if is_native_backed {
let mut style = Style::default();
configure_styles(&new_element.props.styles, &mut style);
let rendered = {
let component = instance.read().unwrap();
// instance.get_derived_state_from_props(props)
let layout_node = stretch.new_node(style, vec![])?;
new_element.layout_node = Some(layout_node);
}
is_native_backed = component.has_native_backing_node();
let x: std::sync::Arc<Component> = instance.into();
let renderer = x.clone();
new_element.instance = Some(x);
if is_native_backed {
let mut style = Style::default();
configure_styles(&new_element.props.styles, &mut style);
let mut children = match renderer.render(&new_element.props) {
let layout_node = stretch.new_node(style, vec![])?;
new_element.layout_node = Some(layout_node);
}
component.render(&new_element.props)
};
new_element.instance = Some(instance);
let mut children = match rendered {
Ok(opt) => match opt {
RSX::VirtualNode(child) => {
let mut children = vec![];
@ -241,27 +252,21 @@ fn mount_component_tree(mut new_element: VirtualNode, stretch: &mut Stretch) ->
// 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");
for child_node in child.props.children {
if let RSX::VirtualNode(node) = child_node {
let mut mounted = mount_component_tree(node, stretch)?;
println!(" > Mounted Fragment...");
if is_native_backed {
println!(" > Linking Fragment: {} {}", new_element.tag, mounted.tag);
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
}
children.push(RSX::VirtualNode(mounted));
} else {
println!(" > Mounting other type of node...");
}
}
} else {
let mut mounted = mount_component_tree(child, stretch)?;
if is_native_backed {
println!("Linking Child");
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
}
@ -288,13 +293,14 @@ fn mount_component_tree(mut new_element: VirtualNode, stretch: &mut Stretch) ->
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);
}
// instance.get_snapshot_before_update()
//renderer.component_did_mount(&new_element.props);
//let x: std::sync::Arc<Component> = instance.into();
// new_element.instance = Some(instance);
//new_element.instance = Some(x);
Ok(new_element)
}

View file

@ -1,7 +1,7 @@
//! Implements the Window API. It attempts to provide a nice, common interface across
//! per-platform Window APIs.
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, RwLock};
use alchemy_lifecycle::traits::{Component, WindowDelegate};
use alchemy_lifecycle::rsx::{Props, RSX};
@ -19,11 +19,11 @@ use crate::reconciler::{diff_and_patch_tree, walk_and_apply_styles};
use alchemy_cocoa::window::{Window as PlatformWindowBridge};
/// Utility function for creating a root_node.
fn create_root_node(instance: Option<Arc<Component>>, layout_manager: &mut Stretch) -> RSX {
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", || Box::new(View::default()), props);
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() {
@ -159,7 +159,7 @@ impl Window {
title: title.into(),
bridge: bridge,
delegate: Box::new(delegate),
root_node: create_root_node(Some(Arc::new(view)), &mut layout),
root_node: create_root_node(Some(Arc::new(RwLock::new(view))), &mut layout),
layout: layout
})))
}

View file

@ -3,7 +3,7 @@
//! uses these to build and alter UI; they're typically returned from `render()`
//! methods.
use std::sync::Arc;
use std::sync::{Arc, RwLock};
use std::fmt::{Debug, Display};
mod virtual_node;
@ -40,7 +40,7 @@ pub enum RSX {
impl RSX {
/// Shorthand method for creating a new `RSX::VirtualNode` instance. Rarely should you call
/// this yourself; the `rsx! {}` macro handles this for you.
pub fn node<F: Fn() -> Box<Component> + Send + Sync + 'static>(tag: &'static str, create_fn: F, props: Props) -> RSX {
pub fn node<F: Fn() -> Arc<RwLock<Component>> + Send + Sync + 'static>(tag: &'static str, create_fn: F, props: Props) -> RSX {
RSX::VirtualNode(VirtualNode {
tag: tag,
create_component_fn: Arc::new(create_fn),

View file

@ -1,9 +1,11 @@
//! 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 crate::traits::{Component};
use crate::rsx::{RSX, StylesList};
/// A value stored inside the `attributes` field on a `Props` instance.

View file

@ -1,7 +1,7 @@
//! Implements the `RSX::VirtualNode` struct, which is a bit of a recursive
//! structure.
use std::sync::Arc;
use std::sync::{Arc, RwLock};
use std::fmt::{Display, Debug};
use alchemy_styles::node::Node;
@ -19,11 +19,11 @@ pub struct VirtualNode {
/// `Component` instances are created on-demand, if the reconciler deems it be so. This
/// is a closure that should return an instance of the correct type.
pub create_component_fn: Arc<Fn() -> Box<Component> + Send + Sync + 'static>,
pub create_component_fn: Arc<Fn() -> Arc<RwLock<Component>> + Send + Sync + 'static>,
/// A cached component instance, which is transferred between trees. Since `Component`
/// instances are lazily created, this is an `Option`, and defaults to `None`.
pub instance: Option<Arc<Component>>,
pub instance: Option<Arc<RwLock<Component>>>,
/// A cached `Node` for computing `Layout` with `Stretch`. Some components may not have
/// a need for layout (e.g, if they don't have a backing node), and thus this is optional.

View file

@ -85,7 +85,7 @@ pub trait Component: Send + Sync {
/// If you implement a Native-backed component, you'll need to implement this. Given a
/// `component`, you need to instruct the system how to append it to the tree at your point.
fn append_child_component(&self, _component: &Arc<Component>) {}
fn append_child_component(&self, _component: &Component) {}
/// If you implement a Native-backed component, you'll need to implement this. Given a
/// `component`, you need to instruct the system how to replace it in the tree at your point.

View file

@ -220,7 +220,9 @@ impl Element {
let component_name = Literal::string(&typename.to_string());
Ok(quote!(
alchemy::RSX::node(#component_name, || Box::new(#typename::default()), alchemy::Props {
alchemy::RSX::node(#component_name, || {
std::sync::Arc::new(std::sync::RwLock::new(#typename::default()))
}, alchemy::Props {
attributes: {
let mut attributes = std::collections::HashMap::new();
#attributes