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:
parent
4bf89f5d91
commit
c7ef19a943
8 changed files with 53 additions and 43 deletions
|
|
@ -3,7 +3,7 @@
|
||||||
//! hence why they're all (somewhat annoyingly, but lovingly) re-implemented
|
//! hence why they're all (somewhat annoyingly, but lovingly) re-implemented
|
||||||
//! as bridges.
|
//! as bridges.
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
use alchemy_styles::styles::{Layout, Style};
|
use alchemy_styles::styles::{Layout, Style};
|
||||||
|
|
||||||
|
|
@ -40,7 +40,7 @@ impl Component for View {
|
||||||
Some(bridge.borrow_native_backing_node())
|
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() {
|
if let Some(child) = component.borrow_native_backing_node() {
|
||||||
let mut bridge = self.0.lock().unwrap();
|
let mut bridge = self.0.lock().unwrap();
|
||||||
bridge.append_child(child);
|
bridge.append_child(child);
|
||||||
|
|
@ -53,7 +53,7 @@ impl Component for View {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, props: &Props) -> Result<RSX, Error> {
|
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(),
|
attributes: std::collections::HashMap::new(),
|
||||||
key: "".into(),
|
key: "".into(),
|
||||||
styles: StylesList::new(),
|
styles: StylesList::new(),
|
||||||
|
|
|
||||||
|
|
@ -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
|
// This None path should never be hit, we just need to use a rather verbose pattern
|
||||||
// here. It's unsightly, I know.
|
// 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) => {
|
||||||
|
let lock = instance.read().unwrap();
|
||||||
|
lock.has_native_backing_node()
|
||||||
|
},
|
||||||
None => false
|
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.
|
/// 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>> {
|
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) {
|
||||||
instance.apply_styles(
|
let component = instance.write().unwrap();
|
||||||
|
component.apply_styles(
|
||||||
layout_manager.layout(layout_node)?,
|
layout_manager.layout(layout_node)?,
|
||||||
layout_manager.style(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_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) {
|
||||||
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);
|
if let (parent_component, child_component) = (parent_instance.write().unwrap(), child_instance.read().unwrap()) {
|
||||||
|
parent_component.append_child_component(&*child_component);
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -213,11 +219,15 @@ 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
|
/// view tree, firing various lifecycle methods, and ensuring that nodes for layout
|
||||||
/// 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 instance = (new_element.create_component_fn)();
|
||||||
// "compute" props, set on instance
|
|
||||||
|
let mut is_native_backed = false;
|
||||||
|
|
||||||
|
let rendered = {
|
||||||
|
let component = instance.read().unwrap();
|
||||||
// instance.get_derived_state_from_props(props)
|
// instance.get_derived_state_from_props(props)
|
||||||
|
|
||||||
let is_native_backed = instance.has_native_backing_node();
|
is_native_backed = component.has_native_backing_node();
|
||||||
|
|
||||||
if is_native_backed {
|
if is_native_backed {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
|
|
@ -227,11 +237,12 @@ fn mount_component_tree(mut new_element: VirtualNode, stretch: &mut Stretch) ->
|
||||||
new_element.layout_node = Some(layout_node);
|
new_element.layout_node = Some(layout_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
let x: std::sync::Arc<Component> = instance.into();
|
component.render(&new_element.props)
|
||||||
let renderer = x.clone();
|
};
|
||||||
new_element.instance = Some(x);
|
|
||||||
|
|
||||||
let mut children = match renderer.render(&new_element.props) {
|
new_element.instance = Some(instance);
|
||||||
|
|
||||||
|
let mut children = match rendered {
|
||||||
Ok(opt) => match opt {
|
Ok(opt) => match opt {
|
||||||
RSX::VirtualNode(child) => {
|
RSX::VirtualNode(child) => {
|
||||||
let mut children = vec![];
|
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
|
// tag similar to what React does, which just hoists the children out of it and
|
||||||
// discards the rest.
|
// discards the rest.
|
||||||
if child.tag == "Fragment" {
|
if child.tag == "Fragment" {
|
||||||
println!(" > In Fragment");
|
|
||||||
for child_node in child.props.children {
|
for child_node in child.props.children {
|
||||||
if let RSX::VirtualNode(node) = child_node {
|
if let RSX::VirtualNode(node) = child_node {
|
||||||
let mut mounted = mount_component_tree(node, stretch)?;
|
let mut mounted = mount_component_tree(node, stretch)?;
|
||||||
|
|
||||||
println!(" > Mounted Fragment...");
|
|
||||||
if is_native_backed {
|
if is_native_backed {
|
||||||
println!(" > Linking Fragment: {} {}", new_element.tag, mounted.tag);
|
|
||||||
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
|
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
children.push(RSX::VirtualNode(mounted));
|
children.push(RSX::VirtualNode(mounted));
|
||||||
} else {
|
|
||||||
println!(" > Mounting other type of node...");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut mounted = mount_component_tree(child, stretch)?;
|
let mut mounted = mount_component_tree(child, stretch)?;
|
||||||
|
|
||||||
if is_native_backed {
|
if is_native_backed {
|
||||||
println!("Linking Child");
|
|
||||||
find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
|
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);
|
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()
|
// instance.get_snapshot_before_update()
|
||||||
//renderer.component_did_mount(&new_element.props);
|
//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)
|
Ok(new_element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! Implements the Window API. It attempts to provide a nice, common interface across
|
//! Implements the Window API. It attempts to provide a nice, common interface across
|
||||||
//! per-platform Window APIs.
|
//! per-platform Window APIs.
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
use alchemy_lifecycle::traits::{Component, WindowDelegate};
|
use alchemy_lifecycle::traits::{Component, WindowDelegate};
|
||||||
use alchemy_lifecycle::rsx::{Props, RSX};
|
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};
|
use alchemy_cocoa::window::{Window as PlatformWindowBridge};
|
||||||
|
|
||||||
/// Utility function for creating a root_node.
|
/// 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();
|
let mut props = Props::default();
|
||||||
props.styles = "root".into();
|
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 {
|
if let RSX::VirtualNode(root) = &mut root_node {
|
||||||
root.layout_node = match instance.is_some() {
|
root.layout_node = match instance.is_some() {
|
||||||
|
|
@ -159,7 +159,7 @@ impl Window {
|
||||||
title: title.into(),
|
title: title.into(),
|
||||||
bridge: bridge,
|
bridge: bridge,
|
||||||
delegate: Box::new(delegate),
|
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
|
layout: layout
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
//! uses these to build and alter UI; they're typically returned from `render()`
|
//! uses these to build and alter UI; they're typically returned from `render()`
|
||||||
//! methods.
|
//! methods.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, RwLock};
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
mod virtual_node;
|
mod virtual_node;
|
||||||
|
|
@ -40,7 +40,7 @@ pub enum RSX {
|
||||||
impl RSX {
|
impl RSX {
|
||||||
/// Shorthand method for creating a new `RSX::VirtualNode` instance. Rarely should you call
|
/// Shorthand method for creating a new `RSX::VirtualNode` instance. Rarely should you call
|
||||||
/// this yourself; the `rsx! {}` macro handles this for you.
|
/// 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 {
|
RSX::VirtualNode(VirtualNode {
|
||||||
tag: tag,
|
tag: tag,
|
||||||
create_component_fn: Arc::new(create_fn),
|
create_component_fn: Arc::new(create_fn),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
//! Implements a Props struct that mostly acts as expected. For arbitrary primitive values,
|
//! Implements a Props struct that mostly acts as expected. For arbitrary primitive values,
|
||||||
//! it shadows a `serde_json::Value`.
|
//! it shadows a `serde_json::Value`.
|
||||||
|
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::traits::{Component};
|
||||||
use crate::rsx::{RSX, StylesList};
|
use crate::rsx::{RSX, StylesList};
|
||||||
|
|
||||||
/// A value stored inside the `attributes` field on a `Props` instance.
|
/// A value stored inside the `attributes` field on a `Props` instance.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! Implements the `RSX::VirtualNode` struct, which is a bit of a recursive
|
//! Implements the `RSX::VirtualNode` struct, which is a bit of a recursive
|
||||||
//! structure.
|
//! structure.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, RwLock};
|
||||||
use std::fmt::{Display, Debug};
|
use std::fmt::{Display, Debug};
|
||||||
|
|
||||||
use alchemy_styles::node::Node;
|
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
|
/// `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.
|
/// 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`
|
/// A cached component instance, which is transferred between trees. Since `Component`
|
||||||
/// instances are lazily created, this is an `Option`, and defaults to `None`.
|
/// 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 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.
|
/// a need for layout (e.g, if they don't have a backing node), and thus this is optional.
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ pub trait Component: Send + Sync {
|
||||||
|
|
||||||
/// If you implement a Native-backed component, you'll need to implement this. Given a
|
/// 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.
|
/// `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
|
/// 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.
|
/// `component`, you need to instruct the system how to replace it in the tree at your point.
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,9 @@ impl Element {
|
||||||
let component_name = Literal::string(&typename.to_string());
|
let component_name = Literal::string(&typename.to_string());
|
||||||
|
|
||||||
Ok(quote!(
|
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: {
|
attributes: {
|
||||||
let mut attributes = std::collections::HashMap::new();
|
let mut attributes = std::collections::HashMap::new();
|
||||||
#attributes
|
#attributes
|
||||||
|
|
|
||||||
Reference in a new issue