diff --git a/dom_pseudocode.xml b/dom_pseudocode.xml new file mode 100644 index 000000000..f07b20fad --- /dev/null +++ b/dom_pseudocode.xml @@ -0,0 +1,12 @@ + + + + + + +Start with the given root component +For each child_component: + Get the component of the associated child_component tag + Create a DomNode for this component + Give this DomNode its width and height and other layout attributes + Recursively load its children \ No newline at end of file diff --git a/src/layout_abstract_syntax.rs b/src/layout_abstract_syntax.rs index 66fd7231c..f825fd3ac 100644 --- a/src/layout_abstract_syntax.rs +++ b/src/layout_abstract_syntax.rs @@ -45,7 +45,7 @@ pub type NodeOrDefTree = rctree::Node; // ==================================================================================================== -/// Representation of an XML node with either another XML tag (`LayoutComponentTag`) or a text node (just a `String`) +/// Representation of an XML node with either another XML tag (`LayoutComponentTag`) or a text node (a vector of alternating `TemplateStringSegment::String`s and `TemplateStringSegment::Argument`s) #[derive(Debug, Clone, PartialEq)] pub enum LayoutComponentNode { Tag(LayoutComponentTag), @@ -81,19 +81,19 @@ pub struct LayoutComponentDefinition { /// Name of the component in "namespace:name" format pub name: (String, String), /// User-defined attribute parameters, which are prefixed with ':' - pub user_attributes: Vec, + pub parameters: Vec, } impl LayoutComponentDefinition { /// Construct a definition for a layout component given its name in "namespace:name" format with an empty set of parameters pub fn new(name: (String, String)) -> Self { - let user_attributes = vec![]; - Self { name, user_attributes } + let parameters = vec![]; + Self { name, parameters } } /// Add a parameter definition (with its name, types, and default value) to this component definition pub fn add_parameter(&mut self, parameter: VariableParameter) { - self.user_attributes.push(parameter); + self.parameters.push(parameter); } } diff --git a/src/layout_system.rs b/src/layout_system.rs index 825e27c5d..92feb84c2 100644 --- a/src/layout_system.rs +++ b/src/layout_system.rs @@ -7,15 +7,15 @@ use std::collections::HashSet; use std::fs; use std::io; -pub struct LayoutSystem { - windows: Vec, +pub struct LayoutSystem<'a> { + windows: Vec>, loaded_components: ResourceCache, attribute_parser: AttributeParser, } -impl LayoutSystem { +impl<'a> LayoutSystem<'a> { /// Construct the `LayoutSystem` with zero windows, an empty cache of component XML layouts, and an `AttributeParser` with its regex parsers - pub fn new() -> LayoutSystem { + pub fn new() -> Self { Self { windows: vec![], loaded_components: ResourceCache::new(), @@ -24,17 +24,18 @@ impl LayoutSystem { } /// Load and construct a new window from a layout component - pub fn add_window(&mut self, name: (&str, &str)) { + pub fn add_window(&'a mut self, name: (&str, &str)) { // Preload the component and its dependencies self.preload_component(name) .expect(&format!("Failure loading layout component '{}'", Self::component_name(name))[..]); // Get the now-loaded component let window_root_component_name = Self::component_name(name); - let window_root_component = self.loaded_components.get(&window_root_component_name[..]).unwrap(); + // let window_root_component = self.loaded_components.get(&window_root_component_name[..]).unwrap(); + // println!("FC: {:#?}", window_root_component); // Construct the window and save it - let new_window = WindowDom::new(window_root_component); + let new_window = WindowDom::new(&window_root_component_name[..], (1920, 1080), &self.loaded_components); self.windows.push(new_window); } @@ -67,7 +68,7 @@ impl LayoutSystem { } // Go through each parameter attribute and preload any default values of layouts - for definition in &component.own_info.user_attributes { + for definition in &component.own_info.parameters { for default in definition.type_sequence_default.iter() { if let TypeValue::Layout(layouts) = default { for layout in layouts { @@ -175,7 +176,7 @@ impl LayoutSystem { }).collect::>(); cloned_tag.set_content(children); - // Return this LayoutComponentTag within the component's root definition tag + // Return this `LayoutComponentTag` within the component's root definition tag Some(cloned_tag) }) .collect::>(); @@ -338,7 +339,7 @@ impl LayoutSystem { } /// Get a string in `namespace:name` format (or just `name` for primitives) given a namespace and component name - fn component_name(name: (&str, &str)) -> String { + pub fn component_name(name: (&str, &str)) -> String { let (namespace, file) = name; if namespace.len() > 0 { format!("{}:{}", namespace, file) diff --git a/src/window_dom.rs b/src/window_dom.rs index 9a9de4ccf..e4ec35b14 100644 --- a/src/window_dom.rs +++ b/src/window_dom.rs @@ -1,9 +1,105 @@ use crate::layout_abstract_syntax::*; +use crate::layout_abstract_types::*; +use crate::{layout_system::*, resource_cache::ResourceCache}; +use std::collections::HashMap; -pub struct WindowDom {} +pub struct WindowDom<'a> { + pub dom: rctree::Node, + loaded_components: &'a ResourceCache, +} -impl WindowDom { - pub fn new(root_component: &FlatComponent) -> WindowDom { - Self {} +impl<'a> WindowDom<'a> { + pub fn new(root_component: &str, window_size: (u32, u32), loaded_components: &'a ResourceCache) -> WindowDom<'a> { + let mut layout_attributes = LayoutAttributes::default(); + layout_attributes.width = Dimension::AbsolutePx(window_size.0 as f64); + layout_attributes.height = Dimension::AbsolutePx(window_size.1 as f64); + + let dom = Self::build_dom_from_component(root_component, &layout_attributes, &vec![], loaded_components); + Self { dom, loaded_components } + } + + fn build_dom_from_component( + root_component: &str, + layout_attributes: &LayoutAttributes, + parameters: &Vec, + loaded_components: &'a ResourceCache, + ) -> rctree::Node { + // Instantiate the DOM node and put it in a tree node + let component = loaded_components.get(root_component).unwrap(); + let dom_node = DomNode::from_component(component, layout_attributes, parameters); + let mut tree = rctree::Node::new(dom_node); + + // Recursively build the child `DomNode` tree node instances + let child_nodes = component + .child_components + .iter() + .map(|child| { + // Get the child name used as the component cache key + let (namespace, name) = &child.name; + let component_name = LayoutSystem::component_name((namespace, name)); + + // Recursively build the child `DomNode` component instance + Self::build_dom_from_component(&component_name[..], &child.layout_arguments, &child.user_arguments, loaded_components) + }) + .collect::>(); + + // Append each child `DomNode` tree node + for child in child_nodes { + tree.append(child); + } + + // Return the tree that has been recursively built with sibling and child components + tree + } +} + +pub struct DomNode { + pub cache_name: String, + pub layout_attributes: LayoutAttributes, + pub variable_bindings: HashMap>, +} + +impl DomNode { + pub fn new(cache_name: String, layout_attributes: LayoutAttributes, variable_bindings: HashMap>) -> Self { + Self { + cache_name, + layout_attributes, + variable_bindings, + } + } + + pub fn from_component(component: &FlatComponent, layout_attributes: &LayoutAttributes, parameters: &Vec) -> Self { + // Cached name of the loaded component + let (namespace, name) = &component.own_info.name; + let cache_name = LayoutSystem::component_name((&namespace[..], &name[..])); + + // Every VARIABLE_NAME binding defined as a parameter on this component + let mut variable_bindings = component + .own_info + .parameters + .iter() + .map(|parameter| { + ( + // HashMap key is the parameter name + parameter.name.clone(), + // HashMap value is the parameter's defined default value + parameter + .type_sequence_default + .iter() + .map(|value| TypeValueOrArgument::TypeValue(value.clone())) + .collect::>(), + ) + }) + .collect::>(); + // Overwrite the defaults for given parameters + for parameter in parameters { + if !variable_bindings.contains_key(¶meter.name[..]) { + panic!("Invalid argument {} given to the {} component", parameter.name, cache_name); + } + + variable_bindings.insert(parameter.name.clone(), parameter.value.clone()); + } + + Self::new(cache_name, layout_attributes.clone(), variable_bindings) } }