From f8025b15ea59cc8a828ac9af09c301153a8662b9 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Mon, 25 May 2020 22:08:27 -0700 Subject: [PATCH] Add parsing of XML layout files into a ParsedLayoutNode tree --- Cargo.lock | 55 +++++++------ Cargo.toml | 1 + gui/header/file-menu.xml | 16 ++-- gui/header/window-buttons.xml | 4 +- gui/input/checkbox-with-dropdown.xml | 8 +- gui/input/checkbox.xml | 4 +- gui/input/dropdown.xml | 6 +- gui/viewport/panels.xml | 4 +- gui/window/main.xml | 18 ++--- src/application.rs | 4 +- src/component_layout.rs | 116 +++++++++++++++++++++++++++ src/main.rs | 2 + src/parsed_layout_node.rs | 38 +++++++++ 13 files changed, 221 insertions(+), 55 deletions(-) create mode 100644 src/component_layout.rs create mode 100644 src/parsed_layout_node.rs diff --git a/Cargo.lock b/Cargo.lock index 15f66aec..2f10e670 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -434,9 +434,9 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", "quote 1.0.6", - "syn 1.0.23", + "syn 1.0.24", "synstructure", ] @@ -538,9 +538,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", "quote 1.0.6", - "syn 1.0.23", + "syn 1.0.24", ] [[package]] @@ -788,6 +788,7 @@ dependencies = [ "rctree", "wgpu", "winit", + "xmlparser", ] [[package]] @@ -1182,9 +1183,9 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b4b5f600e60dd3a147fb57b4547033d382d1979eb087af310e91cb45a63b1f4" dependencies = [ - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", "quote 1.0.6", - "syn 1.0.23", + "syn 1.0.24", ] [[package]] @@ -1226,9 +1227,9 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb44a25c5bba983be0fc8592dfaf3e6d0935ce8be0c6b15b2a39507af34a926" dependencies = [ - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", "quote 1.0.6", - "syn 1.0.23", + "syn 1.0.24", "synstructure", "unicode-xid 0.2.0", ] @@ -1292,9 +1293,9 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40" dependencies = [ - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", "quote 1.0.6", - "syn 1.0.23", + "syn 1.0.24", ] [[package]] @@ -1329,9 +1330,9 @@ checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro-hack" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" +checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" [[package]] name = "proc-macro-nested" @@ -1350,9 +1351,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a50b9351bfa8d65a7d93ce712dc63d2fd15ddbf2c36990fc7cac344859c04f" +checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" dependencies = [ "unicode-xid 0.2.0", ] @@ -1372,7 +1373,7 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" dependencies = [ - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", ] [[package]] @@ -1736,11 +1737,11 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269" +checksum = "f87bc5b2815ebb664de0392fdf1b95b6d10e160f86d9f64ff65e5679841ca06a" dependencies = [ - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", "quote 1.0.6", "unicode-xid 0.2.0", ] @@ -1751,9 +1752,9 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", "quote 1.0.6", - "syn 1.0.23", + "syn 1.0.24", "unicode-xid 0.2.0", ] @@ -1849,9 +1850,9 @@ dependencies = [ "bumpalo", "lazy_static", "log", - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", "quote 1.0.6", - "syn 1.0.23", + "syn 1.0.24", "wasm-bindgen-shared", ] @@ -1871,9 +1872,9 @@ version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a" dependencies = [ - "proc-macro2 1.0.15", + "proc-macro2 1.0.17", "quote 1.0.6", - "syn 1.0.23", + "syn 1.0.24", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2135,3 +2136,9 @@ name = "xml-rs" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" + +[[package]] +name = "xmlparser" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb4240203dadf40be2de9369e5c6dec1bf427528115b030baca3334c18362d7" diff --git a/Cargo.toml b/Cargo.toml index e4ee7130..91d5bf99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ palette = "0.5" futures = "0.3.4" bytemuck = "1.2.0" rctree = "0.3.3" +xmlparser = "0.13.1" diff --git a/gui/header/file-menu.xml b/gui/header/file-menu.xml index 722142f2..abf0c966 100644 --- a/gui/header/file-menu.xml +++ b/gui/header/file-menu.xml @@ -1,20 +1,20 @@ - + - + - File + File - Edit + Edit - Comp + Comp - View + View - Help + Help - + diff --git a/gui/header/window-buttons.xml b/gui/header/window-buttons.xml index 50c91929..250199e3 100644 --- a/gui/header/window-buttons.xml +++ b/gui/header/window-buttons.xml @@ -1,4 +1,4 @@ - + @@ -13,4 +13,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/gui/input/checkbox-with-dropdown.xml b/gui/input/checkbox-with-dropdown.xml index 03e811bd..cc5f0ee3 100644 --- a/gui/input/checkbox-with-dropdown.xml +++ b/gui/input/checkbox-with-dropdown.xml @@ -1,12 +1,12 @@ - + - + - {{OPTION_LIST}} + {{OPTION_LIST}} - + diff --git a/gui/input/checkbox.xml b/gui/input/checkbox.xml index 5033548c..4f61fb2c 100644 --- a/gui/input/checkbox.xml +++ b/gui/input/checkbox.xml @@ -1,4 +1,4 @@ - + @@ -17,4 +17,4 @@ - + diff --git a/gui/input/dropdown.xml b/gui/input/dropdown.xml index a95913f8..96be7900 100644 --- a/gui/input/dropdown.xml +++ b/gui/input/dropdown.xml @@ -1,8 +1,8 @@ - + - {{CURRENT_TEXT}} + {{CURRENT_TEXT}} @@ -13,4 +13,4 @@ - + diff --git a/gui/viewport/panels.xml b/gui/viewport/panels.xml index 7934780b..ca08807e 100644 --- a/gui/viewport/panels.xml +++ b/gui/viewport/panels.xml @@ -1,7 +1,7 @@ - + Option A Option B Option C - \ No newline at end of file + \ No newline at end of file diff --git a/gui/window/main.xml b/gui/window/main.xml index 21e45378..c527b3ac 100644 --- a/gui/window/main.xml +++ b/gui/window/main.xml @@ -1,18 +1,18 @@ - - + + - - Document 1* - Graphite - + + Document 1* - Graphite + - + - File: 1.8 MB | Memory: 137 MB | Scratch: 0.7/12.3 GB - 🖰 Box Select Objects | [⇧G] Move Selection | [⇧R] Rotate Selection | [⇧S] Scale Selection + File: 1.8 MB | Memory: 137 MB | Scratch: 0.7/12.3 GB + 🖰 Box Select Objects | [⇧G] Move Selection | [⇧R] Rotate Selection | [⇧S] Scale Selection - + diff --git a/src/application.rs b/src/application.rs index d1a1ad70..ab886b39 100644 --- a/src/application.rs +++ b/src/application.rs @@ -3,7 +3,7 @@ use crate::window_events; use crate::pipeline::Pipeline; use crate::texture::Texture; use crate::resource_cache::ResourceCache; -use crate::draw_command::DrawCommand; +use crate::component_layout::ComponentLayout; use crate::gui_node::GuiNode; use winit::event::*; use winit::event_loop::*; @@ -75,6 +75,8 @@ impl Application { let gui_root_data = GuiNode::new(swap_chain_descriptor.width, swap_chain_descriptor.height, ColorPalette::get_color_srgb(ColorPalette::Accent)); let gui_root = rctree::Node::new(gui_root_data); + ComponentLayout::new(); + Self { surface, adapter, diff --git a/src/component_layout.rs b/src/component_layout.rs new file mode 100644 index 00000000..7d18d07a --- /dev/null +++ b/src/component_layout.rs @@ -0,0 +1,116 @@ +use std::fs; +use std::io; +use crate::parsed_layout_node::*; + +pub struct ComponentLayout { + +} + +impl ComponentLayout { + pub fn new() -> ComponentLayout { + let parsed_layout_tree = Self::parse_xml_file("gui/window/main.xml").unwrap(); + for node in parsed_layout_tree.descendants() { + println!("{:?}", node); + } + Self {} + } + + pub fn parse_xml_file(path: &str) -> io::Result> { + let source = fs::read_to_string(path)?; + let parsed = xmlparser::Tokenizer::from(&source[..]); + + let mut stack: Vec> = Vec::new(); + let mut current: Option> = None; + let mut result: Option> = None; + + for token in parsed { + match token.unwrap() { + xmlparser::Token::ElementStart { prefix, local, .. } => { + let namespace = String::from(prefix.as_str()); + let tag_name = String::from(local.as_str()); + + let new_parsed_layout_node = ParsedLayoutNode::new_tag(namespace, tag_name); + + let new_node = rctree::Node::new(new_parsed_layout_node); + current = Some(new_node); + } + xmlparser::Token::Attribute { prefix, local, value, .. } => { + let colon_prefixed = prefix.start() > 0 && (prefix.start() == prefix.end()); + let key = if colon_prefixed { + let slice = local.as_str(); + let mut string = String::with_capacity(slice.len() + 1); + string.push(':'); + string.push_str(slice); + string + } else { String::from(local.as_str()) }; + let value = String::from(value.as_str()); + let attribute = (key, value); + + match &mut current { + Some(current_node) => { + match &mut *current_node.borrow_mut() { + ParsedLayoutNode::Tag(tag) => { + // Add this attribute to the current node that has not yet reached its closing angle bracket + tag.add_attribute(attribute); + } + ParsedLayoutNode::Text(_) => { + panic!("Error adding attribute to tag when parsing XML layout in file: {}", path); + } + } + } + None => { + panic!("Error adding attribute to tag when parsing XML layout in file: {}", path); + } + } + } + xmlparser::Token::ElementEnd { end, .. } => { + match end { + // After adding any attributes, the opening tag ends + xmlparser::ElementEnd::Open => { + // After adding any attributes, we are now a layer deeper which the stack keeps track of + let node_to_push = current.take().expect(&format!("Invalid syntax when parsing XML layout in file {}", path)[..]); + stack.push(node_to_push); + } + // After adding any attributes, the self-closing tag ends + xmlparser::ElementEnd::Empty => { + let parent_node = stack.last_mut().expect(&format!("Invalid syntax when parsing XML layout in file: {}", path)[..]); + let new_child = current.take().expect(&format!("Invalid syntax when parsing XML layout in file: {}", path)[..]); + parent_node.append(new_child); + } + // The closing tag is reached + xmlparser::ElementEnd::Close(..) => { + let popped_node = stack.pop().expect(&format!("Encountered extra closing tag when parsing XML layout in file: {}", path)[..]); + match stack.last_mut() { + Some(parent_node) => { + parent_node.append(popped_node); + } + None => { + match result { + None => result = Some(popped_node), + Some(_) => panic!("Encountered multiple root-level tags when parsing XML layout in file: {}", path), + } + } + } + } + } + } + xmlparser::Token::Text { text } => { + let parent_node = stack.last_mut().expect(&format!("Encountered text outside the root tag when parsing XML layout in file: {}", path)[..]); + let text_string = String::from(text.as_str()); + + if !text_string.trim().is_empty() { + let text_node = ParsedLayoutNode::new_text(text_string); + let new_node = rctree::Node::new(text_node); + parent_node.append(new_node); + } + } + _ => {} + } + } + + match result { + None => panic!("Invalid syntax when parsing XML layout in file: {}", path), + Some(tree) => Ok(tree) + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6d692dff..b5e53000 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,8 @@ mod draw_command; mod gui_node; mod gui_attributes; mod window_events; +mod component_layout; +mod parsed_layout_node; use application::Application; use winit::event_loop::EventLoop; diff --git a/src/parsed_layout_node.rs b/src/parsed_layout_node.rs new file mode 100644 index 00000000..6110f36e --- /dev/null +++ b/src/parsed_layout_node.rs @@ -0,0 +1,38 @@ +#[derive(Debug)] +pub enum ParsedLayoutNode { + Tag(ParsedLayoutTag), + Text(String), +} + +impl ParsedLayoutNode { + pub fn new_tag(namespace: String, tag: String) -> Self { + Self::Tag(ParsedLayoutTag::new(namespace, tag)) + } + + pub fn new_text(text: String) -> Self { + Self::Text(text) + } +} + +#[derive(Debug)] +pub struct ParsedLayoutTag { + pub namespace: Option, + pub tag: String, + pub attributes: Vec<(String, String)>, +} + +impl ParsedLayoutTag { + pub fn new(namespace: String, tag: String) -> Self { + let namespace = if namespace.is_empty() { None } else { Some(namespace) }; + + Self { + namespace, + tag, + attributes: Vec::new(), + } + } + + pub fn add_attribute(&mut self, attribute: (String, String)) { + self.attributes.push(attribute); + } +} \ No newline at end of file