wgui: add <macro> support, propagate variables into ParserResult
This commit is contained in:
@@ -20,8 +20,6 @@ use std::{
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
type VarMap = HashMap<Rc<str>, Rc<str>>;
|
||||
|
||||
#[self_referencing]
|
||||
struct XmlDocument {
|
||||
xml: String,
|
||||
@@ -44,6 +42,8 @@ struct ParserFile {
|
||||
|
||||
pub struct ParserResult {
|
||||
pub ids: HashMap<Rc<str>, WidgetID>,
|
||||
macro_attribs: HashMap<Rc<str>, MacroAttribs>,
|
||||
var_map: HashMap<Rc<str>, Rc<str>>,
|
||||
pub templates: HashMap<Rc<str>, Rc<Template>>,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
@@ -67,7 +67,13 @@ impl ParserResult {
|
||||
anyhow::bail!("no template named \"{}\" found", template_name);
|
||||
};
|
||||
|
||||
let mut ctx = create_default_context(layout);
|
||||
let mut ctx = ParserContext {
|
||||
layout,
|
||||
ids: Default::default(),
|
||||
macro_attribs: self.macro_attribs.clone(), // FIXME: prevent copying
|
||||
var_map: self.var_map.clone(), // FIXME: prevent copying
|
||||
templates: Default::default(),
|
||||
};
|
||||
|
||||
let file = ParserFile {
|
||||
document: template.node_document.clone(),
|
||||
@@ -75,21 +81,15 @@ impl ParserResult {
|
||||
template_parameters: template_parameters.clone(), // FIXME: prevent copying
|
||||
};
|
||||
|
||||
let node = template
|
||||
.node_document
|
||||
.borrow_doc()
|
||||
.get_node(template.node)
|
||||
.unwrap();
|
||||
|
||||
parse_widget_other_internal(
|
||||
template.clone(),
|
||||
template_parameters,
|
||||
&file,
|
||||
&mut ctx,
|
||||
node,
|
||||
widget_id,
|
||||
)?;
|
||||
|
||||
// FIXME?
|
||||
ctx.ids.into_iter().for_each(|(id, key)| {
|
||||
self.ids.insert(id, key);
|
||||
});
|
||||
@@ -98,11 +98,17 @@ impl ParserResult {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct MacroAttribs {
|
||||
attribs: HashMap<Rc<str>, Rc<str>>,
|
||||
}
|
||||
|
||||
struct ParserContext<'a> {
|
||||
layout: &'a mut Layout,
|
||||
var_map: VarMap,
|
||||
templates: HashMap<Rc<str>, Rc<Template>>,
|
||||
var_map: HashMap<Rc<str>, Rc<str>>,
|
||||
macro_attribs: HashMap<Rc<str>, MacroAttribs>,
|
||||
ids: HashMap<Rc<str>, WidgetID>,
|
||||
templates: HashMap<Rc<str>, Rc<Template>>,
|
||||
}
|
||||
|
||||
// Parses a color from a HTML hex string
|
||||
@@ -207,12 +213,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_widget_other_internal<'a>(
|
||||
fn parse_widget_other_internal(
|
||||
template: Rc<Template>,
|
||||
template_parameters: HashMap<Rc<str>, Rc<str>>,
|
||||
file: &'a ParserFile,
|
||||
file: &ParserFile,
|
||||
ctx: &mut ParserContext,
|
||||
node: roxmltree::Node<'a, 'a>,
|
||||
parent_id: WidgetID,
|
||||
) -> anyhow::Result<()> {
|
||||
let template_file = ParserFile {
|
||||
@@ -245,16 +250,10 @@ fn parse_widget_other<'a>(
|
||||
return Ok(()); // not critical
|
||||
};
|
||||
|
||||
let template_parameters: HashMap<Rc<str>, Rc<str>> = iter_attribs(file, ctx, &node).collect();
|
||||
let template_parameters: HashMap<Rc<str>, Rc<str>> =
|
||||
iter_attribs(file, ctx, &node, false).collect();
|
||||
|
||||
parse_widget_other_internal(
|
||||
template.clone(),
|
||||
template_parameters,
|
||||
file,
|
||||
ctx,
|
||||
node,
|
||||
parent_id,
|
||||
)
|
||||
parse_widget_other_internal(template.clone(), template_parameters, file, ctx, parent_id)
|
||||
}
|
||||
|
||||
fn parse_tag_include<'a>(
|
||||
@@ -333,7 +332,10 @@ pub fn replace_vars(input: &str, vars: &HashMap<Rc<str>, Rc<str>>) -> Rc<str> {
|
||||
|
||||
match vars.get(input_var) {
|
||||
Some(replacement) => replacement.clone(),
|
||||
None => Rc::from(""),
|
||||
None => {
|
||||
log::warn!("failed to replace var named \"{}\" (not found)", input_var);
|
||||
Rc::from("")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -341,14 +343,12 @@ pub fn replace_vars(input: &str, vars: &HashMap<Rc<str>, Rc<str>>) -> Rc<str> {
|
||||
}
|
||||
|
||||
#[allow(clippy::manual_strip)]
|
||||
fn iter_attribs<'a>(
|
||||
fn process_attrib<'a>(
|
||||
file: &'a ParserFile,
|
||||
ctx: &'a ParserContext,
|
||||
node: &roxmltree::Node<'a, 'a>,
|
||||
) -> impl Iterator<Item = (/*key*/ Rc<str>, /*value*/ Rc<str>)> + 'a {
|
||||
node.attributes().map(|attrib| {
|
||||
let (key, value) = (attrib.name(), attrib.value());
|
||||
|
||||
key: &str,
|
||||
value: &str,
|
||||
) -> (Rc<str>, Rc<str>) {
|
||||
if value.starts_with("~") {
|
||||
let name = &value[1..];
|
||||
|
||||
@@ -365,7 +365,42 @@ fn iter_attribs<'a>(
|
||||
replace_vars(value, &file.template_parameters),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn iter_attribs<'a>(
|
||||
file: &'a ParserFile,
|
||||
ctx: &'a ParserContext,
|
||||
node: &'a roxmltree::Node<'a, 'a>,
|
||||
is_tag_macro: bool,
|
||||
) -> impl Iterator<Item = (/*key*/ Rc<str>, /*value*/ Rc<str>)> + 'a {
|
||||
let mut res = Vec::<(Rc<str>, Rc<str>)>::new();
|
||||
|
||||
if is_tag_macro {
|
||||
// return as-is, no attrib post-processing
|
||||
for attrib in node.attributes() {
|
||||
let (key, value) = (attrib.name(), attrib.value());
|
||||
res.push((Rc::from(key), Rc::from(value)));
|
||||
}
|
||||
return res.into_iter();
|
||||
}
|
||||
|
||||
for attrib in node.attributes() {
|
||||
let (key, value) = (attrib.name(), attrib.value());
|
||||
|
||||
if key == "macro" {
|
||||
if let Some(macro_attrib) = ctx.macro_attribs.get(value) {
|
||||
for (macro_key, macro_value) in macro_attrib.attribs.iter() {
|
||||
res.push(process_attrib(file, ctx, macro_key, macro_value));
|
||||
}
|
||||
} else {
|
||||
log::warn!("requested macro named \"{}\" not found!", value);
|
||||
}
|
||||
} else {
|
||||
res.push(process_attrib(file, ctx, key, value));
|
||||
}
|
||||
}
|
||||
|
||||
res.into_iter()
|
||||
}
|
||||
|
||||
fn parse_tag_theme<'a>(
|
||||
@@ -395,7 +430,7 @@ fn parse_tag_template(
|
||||
) -> anyhow::Result<()> {
|
||||
let mut template_name: Option<Rc<str>> = None;
|
||||
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node).collect();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
||||
|
||||
for (key, value) in attribs {
|
||||
match key.as_ref() {
|
||||
@@ -424,13 +459,51 @@ fn parse_tag_template(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_tag_macro(
|
||||
file: &ParserFile,
|
||||
ctx: &mut ParserContext,
|
||||
node: roxmltree::Node<'_, '_>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut macro_name: Option<Rc<str>> = None;
|
||||
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, true).collect();
|
||||
let mut macro_attribs = HashMap::<Rc<str>, Rc<str>>::new();
|
||||
|
||||
for (key, value) in attribs {
|
||||
match key.as_ref() {
|
||||
"name" => {
|
||||
macro_name = Some(value);
|
||||
}
|
||||
_ => {
|
||||
if macro_attribs.insert(key.clone(), value).is_some() {
|
||||
log::warn!("macro attrib \"{}\" already defined!", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(name) = macro_name else {
|
||||
log::error!("Template name not specified, ignoring");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
ctx.macro_attribs.insert(
|
||||
name.clone(),
|
||||
MacroAttribs {
|
||||
attribs: macro_attribs,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_universal<'a>(
|
||||
file: &'a ParserFile,
|
||||
ctx: &mut ParserContext,
|
||||
node: roxmltree::Node<'a, 'a>,
|
||||
widget_id: WidgetID,
|
||||
) -> anyhow::Result<()> {
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node).collect();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
||||
|
||||
for (key, value) in attribs {
|
||||
#[allow(clippy::single_match)]
|
||||
@@ -485,6 +558,7 @@ fn create_default_context(layout: &mut Layout) -> ParserContext<'_> {
|
||||
ids: Default::default(),
|
||||
var_map: Default::default(),
|
||||
templates: Default::default(),
|
||||
macro_attribs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,6 +578,8 @@ pub fn parse_from_assets(
|
||||
let result = ParserResult {
|
||||
ids: std::mem::take(&mut ctx.ids),
|
||||
templates: std::mem::take(&mut ctx.templates),
|
||||
macro_attribs: std::mem::take(&mut ctx.macro_attribs),
|
||||
var_map: std::mem::take(&mut ctx.var_map),
|
||||
path,
|
||||
};
|
||||
|
||||
@@ -571,6 +647,7 @@ fn parse_document_root(
|
||||
"include" => parse_tag_include(&file, ctx, child_node, parent_id)?,
|
||||
"theme" => parse_tag_theme(ctx, child_node)?,
|
||||
"template" => parse_tag_template(&file, ctx, child_node)?,
|
||||
"macro" => parse_tag_macro(&file, ctx, child_node)?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ pub fn style_from_node<'a>(
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node).collect();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
||||
|
||||
for (key, value) in attribs {
|
||||
match &*key {
|
||||
|
||||
@@ -15,7 +15,7 @@ pub fn parse_widget_label<'a>(
|
||||
parent_id: WidgetID,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut params = TextParams::default();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node).collect();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
||||
for (key, value) in attribs {
|
||||
match &*key {
|
||||
"text" => {
|
||||
|
||||
@@ -16,7 +16,7 @@ pub fn parse_widget_rectangle<'a>(
|
||||
parent_id: WidgetID,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut params = RectangleParams::default();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node).collect();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
||||
|
||||
for (key, value) in attribs {
|
||||
match &*key {
|
||||
|
||||
@@ -15,7 +15,7 @@ pub fn parse_widget_sprite<'a>(
|
||||
parent_id: WidgetID,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut params = SpriteBoxParams::default();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node).collect();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
||||
|
||||
let mut glyph = None;
|
||||
for (key, value) in attribs {
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
<layout>
|
||||
<macro name="keycap_rect"
|
||||
margin="2" width="100%" overflow="hidden" box_sizing="border_box"
|
||||
border_color="#0044CC" border="2" round="8" color="#000A1C" color2="#000002" gradient="vertical"
|
||||
align_items="center" justify_content="center" />
|
||||
|
||||
<macro name="keycap_div"
|
||||
width="${width}" height="${height}" min_width="${width}" min_height="${height}" max_width="${width}" max_height="${height}"
|
||||
/>
|
||||
|
||||
<!-- The keyboard is build from the xkb keymap. This file is for customizing the keycaps. -->
|
||||
|
||||
<!-- Key cap with a single label. -->
|
||||
<!-- Used for special keys. -->
|
||||
<template name="KeySpecial">
|
||||
<div width="${width}" height="${height}" min_width="${width}" min_height="${height}" max_width="${width}" max_height="${height}">
|
||||
<rectangle id="${id}"
|
||||
margin="2" width="100%" overflow="hidden" box_sizing="border_box"
|
||||
border_color="#0044CC" border="2" round="8" color="#000A1C" color2="#000002" gradient="vertical"
|
||||
align_items="center"
|
||||
justify_content="center">
|
||||
<div macro="keycap_div">
|
||||
<rectangle id="${id}" macro="keycap_rect">
|
||||
<sprite color="#FFFFFF" width="32" height="32" src="keyboard/${text}.svg" />
|
||||
</rectangle>
|
||||
</div>
|
||||
@@ -18,12 +23,8 @@
|
||||
<!-- Key cap with a single label. -->
|
||||
<!-- Used for letter keys on layouts without AltGr. -->
|
||||
<template name="KeyLetter">
|
||||
<div width="${width}" height="${height}" min_width="${width}" min_height="${height}" max_width="${width}" max_height="${height}">
|
||||
<rectangle id="${id}"
|
||||
margin="2" width="100%" overflow="hidden" box_sizing="border_box"
|
||||
border_color="#0044CC" border="2" round="8" color="#000A1C" color2="#000002" gradient="vertical"
|
||||
align_items="center"
|
||||
justify_content="center">
|
||||
<div macro="keycap_div">
|
||||
<rectangle id="${id}" macro="keycap_rect">
|
||||
<label color="#FFFFFF" text="${text}" size="24" />
|
||||
</rectangle>
|
||||
</div>
|
||||
@@ -32,14 +33,8 @@
|
||||
<!-- Key cap with a primary label on top and an AltGr label on bottom. -->
|
||||
<!-- Used for letter keys on layouts with AltGr. -->
|
||||
<template name="KeyLetterAltGr">
|
||||
<div width="${width}" height="${height}" min_width="${width}" min_height="${height}" max_width="${width}" max_height="${height}">
|
||||
<rectangle id="${id}"
|
||||
margin="2" width="100%" overflow="hidden" box_sizing="border_box"
|
||||
border_color="#0044CC" border="2" round="8" color="#000A1C" color2="#000002" gradient="vertical"
|
||||
gap="4"
|
||||
flex_direction="column"
|
||||
align_items="center"
|
||||
justify_content="center">
|
||||
<div macro="keycap_div">
|
||||
<rectangle id="${id}" macro="keycap_rect" gap="4">
|
||||
<label color="#FFFFFF" text="${text}" size="24" />
|
||||
<label color="#FFFFFF70" text="${text_altgr}" size="24" />
|
||||
</rectangle>
|
||||
@@ -49,15 +44,8 @@
|
||||
<!-- Key cap with a primary label on bottom and a Shift label on top. -->
|
||||
<!-- Used for number & symbol keys on layouts without AltGr. -->
|
||||
<template name="KeySymbol">
|
||||
<div width="${width}" height="${height}" min_width="${width}" min_height="${height}" max_width="${width}" max_height="${height}">
|
||||
<rectangle id="${id}"
|
||||
margin="2" width="100%" overflow="hidden" box_sizing="border_box"
|
||||
border_color="#0044CC" border="2" round="8" color="#000A1C" color2="#000002" gradient="vertical"
|
||||
gap="4"
|
||||
flex_direction="column"
|
||||
align_items="center"
|
||||
justify_content="center"
|
||||
>
|
||||
<div macro="keycap_div">
|
||||
<rectangle id="${id}" macro="keycap_rect" gap="4">
|
||||
<label color="#FFFFFF70" text="${text_shift}" size="24" />
|
||||
<label color="#FFFFFF" text="${text}" size="24" />
|
||||
</rectangle>
|
||||
@@ -67,14 +55,8 @@
|
||||
<!-- Key cap with a primary label on bottom-left, an AltGr label on bottom-right, Shift label on top-left. -->
|
||||
<!-- Used for number & symbol keys on layouts with AltGr. -->
|
||||
<template name="KeySymbolAltGr">
|
||||
<div width="${width}" height="${height}" min_width="${width}" min_height="${height}" max_width="${width}" max_height="${height}">
|
||||
<rectangle id="${id}"
|
||||
margin="2" width="100%" overflow="hidden" box_sizing="border_box"
|
||||
border_color="#0044CC" border="2" round="8" color="#000A1C" color2="#000002" gradient="vertical"
|
||||
flex_direction="row"
|
||||
flex_wrap="wrap"
|
||||
align_items="center"
|
||||
justify_content="center">
|
||||
<div macro="keycap_div">
|
||||
<rectangle id="${id}" macro="keycap_rect" flex_direction="row" flex_wrap="wrap">
|
||||
<div width="50%" height="50%" align_items="center" justify_content="center">
|
||||
<label color="#FFFFFF70" text="${text_shift}" size="24" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user