Implement abstract syntax tree parsing of XML layout

This commit is contained in:
Keavon Chambers 2020-05-27 04:08:52 -07:00
parent decff5681b
commit 0c7e6bc883
22 changed files with 391 additions and 62 deletions

99
Cargo.lock generated
View file

@ -15,6 +15,15 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
[[package]]
name = "aho-corasick"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
dependencies = [
"memchr",
]
[[package]]
name = "andrew"
version = "0.2.1"
@ -334,7 +343,7 @@ dependencies = [
"autocfg 1.0.0",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"lazy_static 1.4.0",
"maybe-uninit",
"memoffset",
"scopeguard",
@ -358,7 +367,16 @@ checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg 1.0.0",
"cfg-if",
"lazy_static",
"lazy_static 1.4.0",
]
[[package]]
name = "css-color-parser"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ccb6ce7ef97e6dc6e575e51b596c9889a5cc88a307b5ef177d215c61fd7581d"
dependencies = [
"lazy_static 0.1.16",
]
[[package]]
@ -436,7 +454,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2 1.0.17",
"quote 1.0.6",
"syn 1.0.24",
"syn 1.0.27",
"synstructure",
]
@ -540,7 +558,7 @@ dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.17",
"quote 1.0.6",
"syn 1.0.24",
"syn 1.0.27",
]
[[package]]
@ -681,7 +699,7 @@ dependencies = [
"foreign-types",
"gfx-auxil",
"gfx-hal",
"lazy_static",
"lazy_static 1.4.0",
"log",
"metal",
"objc",
@ -704,7 +722,7 @@ dependencies = [
"byteorder",
"core-graphics 0.19.0",
"gfx-hal",
"lazy_static",
"lazy_static 1.4.0",
"log",
"objc",
"raw-window-handle",
@ -780,12 +798,14 @@ version = "0.1.0"
dependencies = [
"bytemuck",
"cgmath",
"css-color-parser",
"failure",
"futures",
"glsl-to-spirv",
"image",
"palette",
"rctree",
"regex",
"wgpu",
"winit",
"xmlparser",
@ -879,6 +899,12 @@ dependencies = [
"winapi-build",
]
[[package]]
name = "lazy_static"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -893,9 +919,9 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
[[package]]
name = "libc"
version = "0.2.70"
version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
[[package]]
name = "libloading"
@ -1185,7 +1211,7 @@ checksum = "0b4b5f600e60dd3a147fb57b4547033d382d1979eb087af310e91cb45a63b1f4"
dependencies = [
"proc-macro2 1.0.17",
"quote 1.0.6",
"syn 1.0.24",
"syn 1.0.27",
]
[[package]]
@ -1229,7 +1255,7 @@ checksum = "6fb44a25c5bba983be0fc8592dfaf3e6d0935ce8be0c6b15b2a39507af34a926"
dependencies = [
"proc-macro2 1.0.17",
"quote 1.0.6",
"syn 1.0.24",
"syn 1.0.27",
"synstructure",
"unicode-xid 0.2.0",
]
@ -1295,7 +1321,7 @@ checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40"
dependencies = [
"proc-macro2 1.0.17",
"quote 1.0.6",
"syn 1.0.24",
"syn 1.0.27",
]
[[package]]
@ -1568,7 +1594,7 @@ dependencies = [
"crossbeam-deque",
"crossbeam-queue",
"crossbeam-utils",
"lazy_static",
"lazy_static 1.4.0",
"num_cpus",
]
@ -1593,6 +1619,24 @@ version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
[[package]]
name = "regex"
version = "1.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
[[package]]
name = "remove_dir_all"
version = "0.5.2"
@ -1688,7 +1732,7 @@ dependencies = [
"andrew",
"bitflags",
"dlib",
"lazy_static",
"lazy_static 1.4.0",
"memmap",
"nix",
"wayland-client",
@ -1737,9 +1781,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.24"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87bc5b2815ebb664de0392fdf1b95b6d10e160f86d9f64ff65e5679841ca06a"
checksum = "ef781e621ee763a2a40721a8861ec519cb76966aee03bb5d00adb6a31dc1c1de"
dependencies = [
"proc-macro2 1.0.17",
"quote 1.0.6",
@ -1754,7 +1798,7 @@ checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
dependencies = [
"proc-macro2 1.0.17",
"quote 1.0.6",
"syn 1.0.24",
"syn 1.0.27",
"unicode-xid 0.2.0",
]
@ -1772,6 +1816,15 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static 1.4.0",
]
[[package]]
name = "tiff"
version = "0.3.1"
@ -1848,11 +1901,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94"
dependencies = [
"bumpalo",
"lazy_static",
"lazy_static 1.4.0",
"log",
"proc-macro2 1.0.17",
"quote 1.0.6",
"syn 1.0.24",
"syn 1.0.27",
"wasm-bindgen-shared",
]
@ -1874,7 +1927,7 @@ checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
dependencies = [
"proc-macro2 1.0.17",
"quote 1.0.6",
"syn 1.0.24",
"syn 1.0.27",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1942,7 +1995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4"
dependencies = [
"dlib",
"lazy_static",
"lazy_static 1.4.0",
]
[[package]]
@ -1993,7 +2046,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19a5051a357d071fd69c24671e0ea6d644a83c7418e47eac3511427379007403"
dependencies = [
"arrayvec",
"lazy_static",
"lazy_static 1.4.0",
"libc",
"objc",
"parking_lot",
@ -2069,7 +2122,7 @@ dependencies = [
"core-video-sys",
"dispatch",
"instant",
"lazy_static",
"lazy_static 1.4.0",
"libc",
"log",
"mio",
@ -2119,7 +2172,7 @@ version = "2.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8"
dependencies = [
"lazy_static",
"lazy_static 1.4.0",
"libc",
"maybe-uninit",
"pkg-config",

View file

@ -18,3 +18,5 @@ futures = "0.3.4"
bytemuck = "1.2.0"
rctree = "0.3.3"
xmlparser = "0.13.1"
regex = "1.3.7"
css-color-parser = "0.1.2"

View file

@ -1,3 +1,3 @@
<box content="INNER_XML" :fill="FILL = none" :round="ROUND = 0px" :border-thickness="BORDER_THICKNESS = 0px" :border-color="BORDER_COLOR = none">
<box content="INNER_XML: (None) = none" :fill="FILL: (Color | None) = none" :round="ROUND: (AbsolutePx) = 0px" :border-thickness="BORDER_THICKNESS: (AbsolutePx) = 0px" :border-color="BORDER_COLOR: (Color | None) = none">
{{INNER_XML}}
</box>

View file

@ -1,3 +1,3 @@
<col content="INNER_XML">
<col content="INNER_XML: (None) = none">
{{INNER_XML}}
</col>

View file

@ -1,20 +1,20 @@
<header:file-menu>
<box height="100%" x-padding="10px">
<text height="100%" y-align="0.5" :color="[mildwhite]"></text>
<text height="100%" y-align="50%" :color="['mildwhite']"></text>
</box>
<box height="100%" x-padding="10px">
<text height="100%" y-align="0.5" :color="[mildwhite]">File</text>
<text height="100%" y-align="50%" :color="['mildwhite']">File</text>
</box>
<box height="100%" x-padding="10px">
<text height="100%" y-align="0.5" :color="[mildwhite]">Edit</text>
<text height="100%" y-align="50%" :color="['mildwhite']">Edit</text>
</box>
<box height="100%" x-padding="10px">
<text height="100%" y-align="0.5" :color="[mildwhite]">Comp</text>
<text height="100%" y-align="50%" :color="['mildwhite']">Comp</text>
</box>
<box height="100%" x-padding="10px">
<text height="100%" y-align="0.5" :color="[mildwhite]">View</text>
<text height="100%" y-align="50%" :color="['mildwhite']">View</text>
</box>
<box height="100%" x-padding="10px">
<text height="100%" y-align="0.5" :color="[mildwhite]">Help</text>
<text height="100%" y-align="50%" :color="['mildwhite']">Help</text>
</box>
</header:file-menu>

View file

@ -1,8 +1,8 @@
<header:window-buttons :maximized="IS_MAXIMIZED">
<box height="100%" y-align="0.5" x-padding="18px">
<header:window-buttons :maximized="IS_MAXIMIZED: (Bool) = false">
<box height="100%" y-align="50%" x-padding="18px">
<icon :src="window_minimize.svg" />
</box>
<box height="100%" y-align="0.5" x-padding="18px">
<box height="100%" y-align="50%" x-padding="18px">
<if :a="{{IS_MAXIMIZED}}">
<icon :src="window_restore_down.svg" />
</if>
@ -10,7 +10,7 @@
<icon :src="maximize.svg" />
</if>
</box>
<box height="100%" y-align="0.5" x-padding="18px">
<box height="100%" y-align="50%" x-padding="18px">
<icon :src="window_close.svg" />
</box>
</header:window-buttons>

View file

@ -1,3 +1,3 @@
<icon content="SVG_DATA" :src="SVG_SOURCE = none">
<icon content="SVG_DATA: (None) = none" :src="SVG_SOURCE: (TemplateString | None) = none">
</icon>

View file

@ -1,12 +1,12 @@
<input:checkbox-with-dropdown content="OPTION_LIST" :disabled="DISABLED = false" :checked="CHECKED = false" :default="DEFAULT = 0">
<input:checkbox-with-dropdown content="OPTION_LIST: (None) = none" :disabled="DISABLED: (Bool) = false" :checked="CHECKED: (Bool) = false" :selected-index="SELECTED_INDEX: (Integer) = 0">
<!-- Checkbox -->
<col width="height" height="100%">
<box width="100%" height="100%" x-align="0.5" y-align="0.5" :fill="[accent]">
<box width="100%" height="100%" x-align="50%" y-align="50%" :round="4px 0px 0px 4px" :fill="['accent']">
<input:checkbox :checked="{{CHECKED}}" :disabled="{{DISABLED}}" :inverted="true" />
</box>
</col>
<!-- Dropdown -->
<col width="100%">
<input:dropdown width="100%" :default="{{DEFAULT}}" :disabled="{{DISABLED}}">{{OPTION_LIST}}</input:dropdown>
<input:dropdown width="100%" :selected-index="{{SELECTED_INDEX}}" :disabled="{{DISABLED}}">{{OPTION_LIST}}</input:dropdown>
</col>
</input:checkbox-with-dropdown>

View file

@ -1,4 +1,4 @@
<input:checkbox :checked="CHECKED = false" :disabled="DISABLED = false" :inverted="INVERTED = false">
<input:checkbox :checked="CHECKED: (Bool) = false" :disabled="DISABLED: (Bool) = false" :inverted="INVERTED: (Bool) = false">
<box width="14px" height="14px" :fill="{{BOX_COLOR}}" :round="2px">
<if :a="{{CHECKED}}">
<if :a="{{INVERTED}}" :b="false">

View file

@ -1,12 +1,12 @@
<input:dropdown content="OPTION_LIST" :default="DEFAULT = 0" :disabled="DISABLED = false">
<input:dropdown content="OPTION_LIST: (TemplateString | None) = none" :selected-index="SELECTED_INDEX: (Integer) = 0" :disabled="DISABLED: (Bool) = false">
<box width="100%" :round="4px">
<!-- Current selection text -->
<col width="100%" height="24px">
<text width="100%" height="100%" x-align="0.5" y-align="0.5" :color="[mildwhite]">{{CURRENT_TEXT}}</text>
<text width="100%" height="100%" x-align="50%" y-align="50%" :color="['mildwhite']">{{CURRENT_TEXT}}</text>
</col>
<!-- Dropdown arrow icon -->
<col width="8px" height="100%">
<icon width="8px" height="100%" x-align="0.5" y-align="0.5">
<icon width="8px" height="100%" x-align="50%" y-align="50%">
<svg width="7" height="5" viewBox="0 0 7 5">
<path d="M0,.5l3.5,3L7,.5v2l-3.5,3L0,2.5Z" transform="translate(0 -0.5)" fill="#ddd" />
</svg>

View file

@ -1,3 +1,3 @@
<row content="INNER_XML">
<row content="INNER_XML: (None) = none">
{{INNER_XML}}
</row>

View file

@ -1,3 +1,3 @@
<text content="TEXT_STRING" :color="COLOR = [middlegray]" :size="SIZE = 12px">
<text content="TEXT_STRING: (None) = none" :color="COLOR: (Color | None) = ['middlegray']" :size="SIZE: (AbsolutePx) = 12px">
</text>

View file

@ -1,7 +1,7 @@
<viewport:panels>
<checkbox-with-dropdown :checked="true" :default="2">
<dropdown-option>Option A</dropdown-option>
<dropdown-option>Option B</dropdown-option>
<dropdown-option>Option C</dropdown-option>
<checkbox-with-dropdown :checked="true" :selected-index="2">
Option A
Option B
Option C
</checkbox-with-dropdown>
</viewport:panels>

View file

@ -2,17 +2,17 @@
<window:main>
<!-- Header -->
<row width="100%" height="28px">
<header:file-menu height="100%" x-align="0" />
<text height="100%" y-align="0.5" x-align="0.5" :color="[mildwhite]">Document 1* - Graphite</text>
<header:window-buttons height="100%" x-align="1" />
<header:file-menu height="100%" x-align="0%" />
<text height="100%" y-align="50%" x-align="50%" :color="['mildwhite']">Document 1* - Graphite</text>
<header:window-buttons height="100%" x-align="100%" />
</row>
<!-- Viewport -->
<row width="100%" height="100@">
<viewport:panels />
<viewport:panels width="100%" height="100%" />
</row>
<!-- Footer -->
<row width="100%" height="24px">
<text height="100%" y-align="0.5" x-align="0" x-padding="10px" :color="[mildwhite]" :size="14px">File: 1.8 MB | Memory: 137 MB | Scratch: 0.7/12.3 GB</text>
<text height="100%" y-align="0.5" x-align="1" x-padding="10px" :color="[mildwhite]" :size="14px">🖰 Box Select Objects | [⇧G] Move Selection | [⇧R] Rotate Selection | [⇧S] Scale Selection</text>
<text height="100%" y-align="50%" x-align="0%" x-padding="10px" :color="['mildwhite']" :size="14px">File: 1.8 MB | Memory: 137 MB | Scratch: 0.7/12.3 GB</text>
<text height="100%" y-align="50%" x-align="100%" x-padding="10px" :color="['mildwhite']" :size="14px">🖰 Box Select Objects | [⇧G] Move Selection | [⇧R] Rotate Selection | [⇧S] Scale Selection</text>
</row>
</window:main>

View file

@ -72,7 +72,7 @@ impl Application {
let gui_rect_pipeline = Pipeline::new(&device, swap_chain_descriptor.format, vec![], &mut shader_cache, ("shaders/shader.vert", "shaders/shader.frag"));
pipeline_cache.set("gui_rect", gui_rect_pipeline);
let gui_root_data = GuiNode::new(swap_chain_descriptor.width, swap_chain_descriptor.height, ColorPalette::get_color_srgb(ColorPalette::Accent));
let gui_root_data = GuiNode::new(swap_chain_descriptor.width, swap_chain_descriptor.height, ColorPalette::Accent.into_color_srgb());
let gui_root = rctree::Node::new(gui_root_data);
GuiLayout::new();

View file

@ -23,7 +23,7 @@ pub enum ColorPalette {
impl ColorPalette {
#[allow(dead_code)]
pub fn get_color_srgb(self) -> Color {
pub fn into_color_srgb(&self) -> Color {
let grayscale = match self {
ColorPalette::Black => 0 * 17, // #000000
ColorPalette::NearBlack => 1 * 17, // #111111
@ -58,11 +58,34 @@ impl ColorPalette {
}
#[allow(dead_code)]
pub fn get_color_linear(self) -> Color {
let standard_rgb = ColorPalette::get_color_srgb(self);
pub fn into_color_linear(&self) -> Color {
let standard_rgb = ColorPalette::into_color_srgb(self);
let linear = palette::Srgb::new(standard_rgb.r, standard_rgb.g, standard_rgb.b).into_linear();
Color::new(linear.red, linear.green, linear.blue, standard_rgb.a)
}
pub fn lookup_palette_color(name_in_palette: &str) -> ColorPalette {
match &name_in_palette.to_ascii_lowercase()[..] {
"black" => ColorPalette::Black,
"nearblack" => ColorPalette::NearBlack,
"mildblack" => ColorPalette::MildBlack,
"darkgray" => ColorPalette::DarkGray,
"dimgray" => ColorPalette::DimGray,
"dullgray" => ColorPalette::DullGray,
"lowergray" => ColorPalette::LowerGray,
"middlegray" => ColorPalette::MiddleGray,
"uppergray" => ColorPalette::UpperGray,
"palegray" => ColorPalette::PaleGray,
"softgray" => ColorPalette::SoftGray,
"lightgray" => ColorPalette::LightGray,
"brightgray" => ColorPalette::BrightGray,
"mildwhite" => ColorPalette::MildWhite,
"nearwhite" => ColorPalette::NearWhite,
"white" => ColorPalette::White,
"accent" => ColorPalette::Accent,
_ => panic!("Invalid color lookup of `{}` from the color palette", name_in_palette),
}
}
}

View file

@ -1,6 +1,7 @@
use std::fs;
use std::io;
use crate::layout_parsed_node::*;
use crate::layout_abstract_syntax::*;
pub struct GuiLayout {
@ -10,6 +11,7 @@ impl GuiLayout {
pub fn new() -> GuiLayout {
let parsed_layout_tree = Self::parse_xml_file("gui/window/main.xml").unwrap();
Self::interpret_abstract_syntax_tree(parsed_layout_tree);
Self {}
}
@ -115,6 +117,15 @@ impl GuiLayout {
pub fn interpret_abstract_syntax_tree(root: rctree::Node<LayoutParsedNode>) {
for node in root.descendants() {
println!("{:?}", node);
match & *node.borrow() {
LayoutParsedNode::Tag(tag) => {
LayoutAbstractSyntaxNode::new(tag.namespace.clone(), tag.name.clone(), &tag.attributes);
}
LayoutParsedNode::Text(_) => {}
};
println!();
}
}
}

View file

@ -0,0 +1,150 @@
use crate::layout_abstract_types::*;
use crate::color_palette::ColorPalette;
use crate::color::Color;
#[derive(Debug)]
pub enum Attribute {
VariableValue(VariableValue),
TypeValue(TypeValue),
}
pub fn parse_attribute(input: &str) -> Attribute {
// Match variables and typed values that can be in an attribute
let regex = regex::Regex::new(
r#"(?x)
^\s*(\w*)\s*(:)\s*(\()\s*(\w*\s*(?:\|\s*\w*\s*?)*)\s*(\))\s*(=)\s*(\w*)\s*$ | # Parameter ?: (? | ... | ?) = ?
^\s*(\{\{)\s*(\w*)\s*(\}\})\s*$ | # Argument {{?}}
^\s*(-?\d+)\s*$ | # Integer ?
^\s*(-?(?:(?:\d+\.\d*)|(?:\d*\.\d+)))\s*$ | # Decimal ?
^\s*(-?(?:(?:\d+(?:\.\d*)?)|(?:\d*(?:\.\d+))))([Pp][Xx])\s*$ | # AbsolutePx ?px
^\s*(-?(?:(?:\d+(?:\.\d*)?)|(?:\d*(?:\.\d+))))(%)\s*$ | # Percent ?%
^\s*(-?(?:(?:\d+(?:\.\d*)?)|(?:\d*(?:\.\d+))))(@)\s*$ | # PercentRemainder ?@
^\s*([Ii][Nn][Nn][Ee][Rr])\s*$ | # Inner inner
^\s*([Ww][Ii][Dd][Tt][Hh])\s*$ | # Width width
^\s*([Hh][Ee][Ii][Gg][Hh][Tt])\s*$ | # Height height
^\s*`(.*)`\s*$ | # TemplateString `? ... {{?}} ...`
^\s*(\[)(.*)(\])\s*$ | # Color [?]
^\s*([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])\s*$ | # Bool true/false
^\s*([Nn][Oo][Nn][Ee])\s*$ # None none
"#
).unwrap();
// Match with the regular expression
let captures = regex.captures(input).map(|captures|
captures
.iter()
.skip(1)
.flat_map(|c| c)
.map(|c| c.as_str())
.collect::<Vec<_>>()
);
// Match against the captured values as a slice
let slices = captures.as_ref().map(|c| c.as_slice());
match slices {
Some([name, ":", "(", types, ")", "=", default_value]) => {
// TODO: Extend to support a list of N types (like (AbsolutePx) (AbsolutePx) (AbsolutePx) (AbsolutePx))
let name = String::from(*name);
let split_types = types.split("|").map(|piece| piece.trim());
let valid_types = split_types.map(|type_name|
match &type_name.to_ascii_lowercase()[..] {
"xml" => TypeName::Xml,
"integer" => TypeName::Integer,
"decimal" => TypeName::Decimal,
"absolutepx" => TypeName::AbsolutePx,
"percent" => TypeName::Percent,
"percentremainder" => TypeName::PercentRemainder,
"inner" => TypeName::Inner,
"width" => TypeName::Width,
"height" => TypeName::Height,
"templatestring" => TypeName::TemplateString,
"color" => TypeName::Color,
"bool" => TypeName::Bool,
"none" => TypeName::None,
invalid => panic!("Invalid type `{}` specified in the attribute `{}` when parsing XML layout", invalid, input),
}
).collect::<Vec<_>>();
let default = match parse_attribute(default_value) {
Attribute::TypeValue(type_value) => type_value,
Attribute::VariableValue(variable_value) => panic!("Found the variable value `{:?}` in the attribute `{}` which only allows typed values, when parsing XML layout", variable_value, input),
};
Attribute::VariableValue(VariableValue::Parameter(VariableParameter {name, valid_types, default}))
}
Some(["{{", name, "}}"]) => {
let name = String::from(*name);
Attribute::VariableValue(VariableValue::Argument(name))
}
Some([value]) if regex::Regex::new(r"^\s*(-?\d+)\s*$").unwrap().is_match(value) => {
let integer = value.parse::<i64>().expect(&format!("Invalid value `{}` specified in the attribute `{}` when parsing XML layout", value, input)[..]);
Attribute::TypeValue(TypeValue::Integer(integer))
}
Some([value]) if regex::Regex::new(r"^\s*(-?(?:(?:\d+\.\d*)|(?:\d*\.\d+)))\s*$").unwrap().is_match(value) => {
let decimal = value.parse::<f64>().expect(&format!("Invalid value `{}` specified in the attribute `{}` when parsing XML layout", value, input)[..]);
Attribute::TypeValue(TypeValue::Decimal(decimal))
}
Some([value, px]) if px.eq_ignore_ascii_case("px") => {
let pixels = value.parse::<f32>().expect(&format!("Invalid value `{}` specified in the attribute `{}` when parsing XML layout", value, input)[..]);
Attribute::TypeValue(TypeValue::AbsolutePx(pixels))
}
Some([value, "%"]) => {
let percent = value.parse::<f32>().expect(&format!("Invalid value `{}` specified in the attribute `{}` when parsing XML layout", value, input)[..]);
Attribute::TypeValue(TypeValue::Percent(percent))
}
Some([value, "@"]) => {
let percent_remainder = value.parse::<f32>().expect(&format!("Invalid value `{}` specified in the attribute `{}` when parsing XML layout", value, input)[..]);
Attribute::TypeValue(TypeValue::PercentRemainder(percent_remainder))
}
Some([inner]) if inner.eq_ignore_ascii_case("inner") => {
Attribute::TypeValue(TypeValue::Inner)
}
Some([width]) if width.eq_ignore_ascii_case("width") => {
Attribute::TypeValue(TypeValue::Width)
}
Some([height]) if height.eq_ignore_ascii_case("height") => {
Attribute::TypeValue(TypeValue::Height)
}
Some(["`", string, "`"]) => {
let mut segments = Vec::<TemplateStringSegment>::new();
let mut is_template = false;
let regex = regex::Regex::new(r"\{\{|\}\}").unwrap();
for part in regex.split(string) {
let segment = match is_template {
true => TemplateStringSegment::String(String::from(part)),
false => TemplateStringSegment::Argument(VariableArgument { name: String::from(part) }),
};
segments.push(segment);
is_template = !is_template;
}
Attribute::TypeValue(TypeValue::TemplateString(segments))
}
Some(["[", color_name, "]"]) => {
let regex = regex::Regex::new(r"\s*'(.*)'\s*").unwrap();
let color = match regex.captures(color_name) {
Some(captures) => {
let palette_color = captures.get(1).expect(&format!("Invalid palette color name `{}` specified in the attribute `{}` when parsing XML layout", color_name, input)[..]).as_str();
ColorPalette::lookup_palette_color(palette_color).into_color_srgb()
}
None => {
let parsed = color_name.parse::<css_color_parser::Color>();
let css_color = parsed.expect(&format!("Invalid CSS color name `{}` specified in the attribute `{}` when parsing XML layout", color_name, input)[..]);
Color::new(css_color.r as f32 / 255.0, css_color.g as f32 / 255.0, css_color.b as f32 / 255.0, css_color.a as f32 / 255.0)
}
};
Attribute::TypeValue(TypeValue::Color(color))
}
Some([true_or_false]) if true_or_false.eq_ignore_ascii_case("true") || true_or_false.eq_ignore_ascii_case("false") => {
let boolean = true_or_false.eq_ignore_ascii_case("true");
Attribute::TypeValue(TypeValue::Bool(boolean))
}
Some([none]) if none.eq_ignore_ascii_case("none") => {
Attribute::TypeValue(TypeValue::None)
}
_ => panic!("Invalid attribute value `{}` when parsing XML layout", input),
}
}

View file

@ -0,0 +1,23 @@
use crate::layout_abstract_attributes::*;
#[derive(Debug)]
pub struct LayoutAbstractSyntaxNode {
pub namespace: Option<String>,
pub name: String,
pub attributes: Vec<Attribute>,
}
impl LayoutAbstractSyntaxNode {
pub fn new(namespace: Option<String>, tag: String, attributes: &Vec<(String, String)>) -> Self {
for attribute in attributes {
let parsed = parse_attribute(&attribute.1[..]);
println!("{} : {:?} -> {:?}", attribute.0, attribute.1, parsed);
}
Self {
namespace,
name: tag,
attributes: Vec::new(),
}
}
}

View file

@ -0,0 +1,64 @@
use crate::color::Color;
// Variable types
#[derive(Debug)]
pub enum VariableValue {
Parameter(VariableParameter),
Argument(String),
}
#[derive(Debug)]
pub struct VariableParameter {
pub name: String,
pub valid_types: Vec<TypeName>,
pub default: TypeValue,
// pub value: TypeValue,
}
#[derive(Debug)]
pub struct VariableArgument {
pub name: String,
}
// Value types
#[derive(Debug)]
pub enum TypeName {
Xml,
Integer,
Decimal,
AbsolutePx,
Percent,
PercentRemainder,
Inner,
Width,
Height,
TemplateString,
Color,
Bool,
None,
}
#[derive(Debug)]
pub enum TypeValue {
Xml(()), // TODO
Integer(i64),
Decimal(f64),
AbsolutePx(f32),
Percent(f32),
PercentRemainder(f32),
Inner,
Width,
Height,
TemplateString(Vec<TemplateStringSegment>),
Color(Color),
Bool(bool),
None,
}
#[derive(Debug)]
pub enum TemplateStringSegment {
String(String),
Argument(VariableArgument),
}

View file

@ -17,17 +17,17 @@ impl LayoutParsedNode {
#[derive(Debug)]
pub struct LayoutParsedTag {
pub namespace: Option<String>,
pub tag: String,
pub name: String,
pub attributes: Vec<(String, String)>,
}
impl LayoutParsedTag {
pub fn new(namespace: String, tag: String) -> Self {
pub fn new(namespace: String, name: String) -> Self {
let namespace = if namespace.is_empty() { None } else { Some(namespace) };
Self {
namespace,
tag,
name,
attributes: Vec::new(),
}
}

View file

@ -11,6 +11,9 @@ mod gui_attributes;
mod window_events;
mod gui_layout;
mod layout_parsed_node;
mod layout_abstract_types;
mod layout_abstract_attributes;
mod layout_abstract_syntax;
use application::Application;
use winit::event_loop::EventLoop;