use std::sync::Mutex;
use std::error::Error;
use alchemy_styles::THEME_ENGINE;
use alchemy_styles::styles::{Appearance, Dimension, Number, Size, Style};
use alchemy_styles::stretch::node::{Node as LayoutNode, Stretch as LayoutStore};
use crate::rsx::{RSX, VirtualNode};
use crate::traits::Component;
pub mod key;
use key::ComponentKey;
pub mod storage;
use storage::ComponentStore;
pub mod error;
use error::RenderEngineError;
mod instance;
use instance::Instance;
mod generic_root_view_stub;
use generic_root_view_stub::{GenericRootView, GenericRootViewProps};
struct GenericRootProps;
pub struct RenderEngine {
queued_state_updates: Mutex<Vec<i32>>,
components: Mutex<ComponentStore>,
layouts: Mutex<LayoutStore>
}
impl RenderEngine {
pub(crate) fn new() -> RenderEngine {
RenderEngine {
queued_state_updates: Mutex::new(vec![]),
components: Mutex::new(ComponentStore::new()),
layouts: Mutex::new(LayoutStore::new())
}
}
pub fn register_root_component<C: Component + 'static>(&self, component: C) -> Result<ComponentKey, Box<Error>> {
if !component.has_native_backing_node() {
return Err(Box::new(RenderEngineError::InvalidRootComponent {}));
}
let mut component_store = self.components.lock().unwrap();
let mut layouts_store = self.layouts.lock().unwrap();
let component_key = component_store.new_key();
component_store.insert(component_key, Instance {
tag: "root",
style_keys: "root".into(),
component: Box::new(component),
appearance: Appearance::default(),
layout: Some(layouts_store.new_node(Style::default(), vec![])?)
})?;
Ok(component_key)
}
pub fn diff_and_render_root(
&self,
key: ComponentKey,
dimensions: (f64, f64),
child: RSX
) -> Result<(), Box<Error>> {
let mut component_store = self.components.lock().unwrap();
let mut layout_store = self.layouts.lock().unwrap();
let new_root_node = RSX::node("root", "root".into(), |_| {
Box::new(GenericRootView {})
}, Box::new(GenericRootViewProps {}), match child {
RSX::VirtualNode(node) => {
if node.tag == "Fragment" {
node.children
} else {
vec![RSX::VirtualNode(node)]
}
},
_ => vec![]
});
recursively_diff_tree(key, new_root_node, &mut component_store, &mut layout_store)?;
let layout_node = {
let mut root_instance = component_store.get_mut(key)?;
let layout = root_instance.layout.unwrap();
let mut style = Style::default();
THEME_ENGINE.configure_styles_for_keys(&root_instance.style_keys, &mut style, &mut root_instance.appearance);
style.size = Size {
width: Dimension::Points(dimensions.0 as f32),
height: Dimension::Points(dimensions.1 as f32)
};
layout_store.set_style(layout, style);
layout
};
layout_store.compute_layout(layout_node, Size {
width: Number::Defined(dimensions.0 as f32),
height: Number::Defined(dimensions.1 as f32)
})?;
walk_and_apply_styles(key, &mut component_store, &mut layout_store)?;
Ok(())
}
}
fn recursively_diff_tree(
key: ComponentKey,
new_tree: RSX,
component_store: &mut ComponentStore,
layout_store: &mut LayoutStore
) -> Result<(), Box<Error>> {
let is_replace = match &new_tree {
RSX::VirtualNode(new_tree) => {
let old_tree = component_store.get(key)?;
old_tree.tag != new_tree.tag
},
_ => false
};
if is_replace {
unmount_component_tree(key, component_store, layout_store)?;
return Ok(());
}
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() {
Some(old_child_key) => {
recursively_diff_tree(
old_child_key,
new_child_tree,
component_store,
layout_store
)?;
},
None => {
if let RSX::VirtualNode(tr33amimustfeelohlol) = new_child_tree {
let new_child_key = mount_component_tree(
tr33amimustfeelohlol,
component_store,
layout_store
)?;
component_store.add_child(key, new_child_key)?;
link_layout_nodess(key, new_child_key, component_store, layout_store)?;
}
}
}
}
}
if old_children.len() > 0 {
for child in old_children {
unmount_component_tree(child, component_store, layout_store)?;
}
}
Ok(())
}
fn mount_component_tree(
tree: VirtualNode,
component_store: &mut ComponentStore,
layout_store: &mut LayoutStore
) -> Result<ComponentKey, Box<Error>> {
let key = component_store.new_key();
let component = (tree.create_component_fn)(key);
let is_native_backed = component.has_native_backing_node();
let mut instance = Instance {
tag: tree.tag,
style_keys: tree.styles,
component: component,
appearance: Appearance::default(),
layout: None
};
if is_native_backed {
let mut style = Style::default();
THEME_ENGINE.configure_styles_for_keys(&instance.style_keys, &mut style, &mut instance.appearance);
instance.layout = Some(layout_store.new_node(style, vec![])?);
}
let rendered = instance.component.render(tree.children);
component_store.insert(key, instance)?;
match rendered {
Ok(child) => if let RSX::VirtualNode(child) = child {
if child.tag == "Fragment" {
for child_tree in child.children {
if let RSX::VirtualNode(child_tree) = child_tree {
let child_key = mount_component_tree(child_tree, component_store, layout_store)?;
component_store.add_child(key, child_key)?;
if is_native_backed {
link_layout_nodess(key, child_key, component_store, layout_store)?;
}
}
}
} else {
let child_key = mount_component_tree(child, component_store, layout_store)?;
component_store.add_child(key, child_key)?;
if is_native_backed {
link_layout_nodess(key, child_key, component_store, layout_store)?;
}
}
},
Err(e) => {
eprintln!("Error rendering: {}", e);
}
}
let instance_lol = component_store.get_mut(key)?;
instance_lol.component.component_did_mount();
Ok(key)
}
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();
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); }
}
}
Ok(layout_nodes)
}
fn link_layout_nodess(
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)?;
if let Some(platform_node) = child_instance.component.borrow_native_backing_node() {
parent_instance.component.append_child_node(platform_node);
}
return Ok(());
}
}
let children = components.children(child)?;
for child_key in children {
link_layout_nodess(parent, child_key, components, layouts)?;
}
Ok(())
}
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(())
}