diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index d0c4b1b..0b73ee4 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -7,9 +7,9 @@ use crate::{ use glam::Vec2; use wgui::{ components::{ - Component, button::{ButtonClickCallback, ComponentButton}, checkbox::ComponentCheckbox, + Component, }, drawing::Color, event::EventListenerCollection, @@ -66,17 +66,17 @@ impl TestbedGeneric { let extra = ParseDocumentExtra { on_custom_attribs: Some(Box::new(move |par| { - let Some(my_custom_value) = par.get_value("my_custom") else { + let Some(my_custom_value) = par.get_value("_my_custom") else { return; }; - let Some(mult_value) = par.get_value("mult") else { + let Some(mult_value) = par.get_value("_mult") else { return; }; let mult_f32 = mult_value.parse::().unwrap(); - let mut color = match my_custom_value { + let mut color = match my_custom_value.as_ref() { "red" => Color::new(1.0, 0.0, 0.0, 1.0), "green" => Color::new(0.0, 1.0, 0.0, 1.0), "blue" => Color::new(0.0, 0.0, 1.0, 1.0), diff --git a/wgui/src/parser/component_button.rs b/wgui/src/parser/component_button.rs index b40f1f7..cffc13c 100644 --- a/wgui/src/parser/component_button.rs +++ b/wgui/src/parser/component_button.rs @@ -1,11 +1,12 @@ use crate::{ - components::{Component, button}, + components::{button, Component}, drawing::Color, i18n::Translation, layout::WidgetID, parser::{ - ParserContext, ParserFile, iter_attribs, parse_children, process_component, + parse_children, process_component, style::{parse_color_opt, parse_round, parse_style, parse_text_style}, + AttribPair, ParserContext, ParserFile, }, widget::util::WLength, }; @@ -15,6 +16,7 @@ pub fn parse_component_button<'a, U1, U2>( ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, + attribs: &[AttribPair], ) -> anyhow::Result { let mut color: Option = None; let mut border_color: Option = None; @@ -23,11 +25,11 @@ pub fn parse_component_button<'a, U1, U2>( let mut round = WLength::Units(4.0); let mut translation: Option = None; - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); - let text_style = parse_text_style(&attribs); - let style = parse_style(&attribs); + let text_style = parse_text_style(attribs); + let style = parse_style(attribs); - for (key, value) in attribs { + for pair in attribs { + let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); match key.as_ref() { "text" => { translation = Some(Translation::from_raw_text(&value)); @@ -73,7 +75,7 @@ pub fn parse_component_button<'a, U1, U2>( }, )?; - process_component(file, ctx, node, Component(component), new_id); + process_component(ctx, Component(component), new_id, attribs); parse_children(file, ctx, node, new_id)?; Ok(new_id) diff --git a/wgui/src/parser/component_checkbox.rs b/wgui/src/parser/component_checkbox.rs index fbb1ea2..95d8128 100644 --- a/wgui/src/parser/component_checkbox.rs +++ b/wgui/src/parser/component_checkbox.rs @@ -1,38 +1,35 @@ use crate::{ - components::{Component, checkbox}, + components::{checkbox, Component}, i18n::Translation, layout::WidgetID, - parser::{ - ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_check_i32, process_component, style::parse_style, - }, + parser::{parse_check_f32, parse_check_i32, process_component, style::parse_style, AttribPair, ParserContext}, }; pub fn parse_component_checkbox<'a, U1, U2>( - file: &'a ParserFile, ctx: &mut ParserContext, - node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, + attribs: &[AttribPair], ) -> anyhow::Result { let mut box_size = 24.0; let mut translation = Translation::default(); let mut checked = 0; - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); - let style = parse_style(&attribs); + let style = parse_style(attribs); - for (key, value) in attribs { - match key.as_ref() { + for pair in attribs { + let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); + match key { "text" => { - translation = Translation::from_raw_text(&value); + translation = Translation::from_raw_text(value); } "translation" => { - translation = Translation::from_translation_key(&value); + translation = Translation::from_translation_key(value); } "box_size" => { - parse_check_f32(value.as_ref(), &mut box_size); + parse_check_f32(value, &mut box_size); } "checked" => { - parse_check_i32(value.as_ref(), &mut checked); + parse_check_i32(value, &mut checked); } _ => {} } @@ -50,7 +47,7 @@ pub fn parse_component_checkbox<'a, U1, U2>( }, )?; - process_component(file, ctx, node, Component(component), new_id); + process_component(ctx, Component(component), new_id, attribs); Ok(new_id) } diff --git a/wgui/src/parser/component_slider.rs b/wgui/src/parser/component_slider.rs index 29138d1..b6ff768 100644 --- a/wgui/src/parser/component_slider.rs +++ b/wgui/src/parser/component_slider.rs @@ -1,32 +1,31 @@ use crate::{ - components::{Component, slider}, + components::{slider, Component}, layout::WidgetID, - parser::{ParserContext, ParserFile, iter_attribs, parse_check_f32, process_component, style::parse_style}, + parser::{parse_check_f32, process_component, style::parse_style, AttribPair, ParserContext}, }; pub fn parse_component_slider<'a, U1, U2>( - file: &'a ParserFile, ctx: &mut ParserContext, - node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, + attribs: &[AttribPair], ) -> anyhow::Result { let mut min_value = 0.0; let mut max_value = 1.0; let mut initial_value = 0.5; - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); - let style = parse_style(&attribs); + let style = parse_style(attribs); - for (key, value) in attribs { - match key.as_ref() { + for pair in attribs { + let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); + match key { "min_value" => { - parse_check_f32(value.as_ref(), &mut min_value); + parse_check_f32(value, &mut min_value); } "max_value" => { - parse_check_f32(value.as_ref(), &mut max_value); + parse_check_f32(value, &mut max_value); } "value" => { - parse_check_f32(value.as_ref(), &mut initial_value); + parse_check_f32(value, &mut initial_value); } _ => {} } @@ -46,7 +45,7 @@ pub fn parse_component_slider<'a, U1, U2>( }, )?; - process_component(file, ctx, node, Component(component), new_id); + process_component(ctx, Component(component), new_id, attribs); Ok(new_id) } diff --git a/wgui/src/parser/mod.rs b/wgui/src/parser/mod.rs index af7c48c..68f027c 100644 --- a/wgui/src/parser/mod.rs +++ b/wgui/src/parser/mod.rs @@ -441,7 +441,7 @@ fn print_invalid_value(value: &str) { log::warn!("Invalid value \"{value}\""); } -fn parse_val(value: &Rc) -> Option { +fn parse_val(value: &str) -> Option { let Ok(val) = value.parse::() else { print_invalid_value(value); return None; @@ -534,15 +534,16 @@ fn parse_widget_other<'a, U1, U2>( xml_tag_name: &str, file: &'a ParserFile, ctx: &mut ParserContext, - node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, + attribs: &[AttribPair], ) -> anyhow::Result<()> { let Some(template) = ctx.get_template(xml_tag_name) else { log::error!("Undefined tag named \"{xml_tag_name}\""); return Ok(()); // not critical }; - let template_parameters: HashMap, Rc> = iter_attribs(file, ctx, &node, false).collect(); + let template_parameters: HashMap, Rc> = + attribs.iter().map(|a| (a.attrib.clone(), a.value.clone())).collect(); parse_widget_other_internal(&template, template_parameters, file, ctx, parent_id) } @@ -550,17 +551,15 @@ fn parse_widget_other<'a, U1, U2>( fn parse_tag_include<'a, U1, U2>( file: &ParserFile, ctx: &mut ParserContext, - node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, + attribs: &[AttribPair], ) -> anyhow::Result<()> { - for attrib in node.attributes() { - let (key, value) = (attrib.name(), attrib.value()); - + for pair in attribs { #[allow(clippy::single_match)] - match key { + match pair.attrib.as_ref() { "src" => { let mut new_path = file.path.parent().unwrap_or_else(|| Path::new("/")).to_path_buf(); - new_path.push(value); + new_path.push(pair.value.as_ref()); let new_path = assets::normalize_path(&new_path); let (new_file, node_layout) = get_doc_from_path(ctx, &new_path)?; @@ -569,7 +568,7 @@ fn parse_tag_include<'a, U1, U2>( return Ok(()); } _ => { - print_invalid_attrib(key, value); + print_invalid_attrib(pair.attrib.as_ref(), pair.value.as_ref()); } } } @@ -637,38 +636,39 @@ fn process_attrib<'a, U1, U2>( ctx: &'a ParserContext, key: &str, value: &str, -) -> (Rc, Rc) { +) -> AttribPair { if value.starts_with('~') { let name = &value[1..]; - ( - Rc::from(key), - match ctx.get_var(name) { - Some(name) => name, - None => Rc::from("undefined"), - }, - ) + match ctx.get_var(name) { + Some(name) => AttribPair::new(key, name.clone()), + None => AttribPair::new(key, "undefined"), + } } else { - (Rc::from(key), replace_vars(value, &file.template_parameters)) + AttribPair::new(key, replace_vars(value, &file.template_parameters)) } } -fn iter_attribs<'a, U1, U2>( +fn raw_attribs<'a>(node: &'a roxmltree::Node<'a, 'a>) -> Vec { + let mut res = vec![]; + for attrib in node.attributes() { + let (key, value) = (attrib.name(), attrib.value()); + res.push(AttribPair::new(key, value)); + } + return res; +} + +fn process_attribs<'a, U1, U2>( file: &'a ParserFile, ctx: &'a ParserContext, node: &'a roxmltree::Node<'a, 'a>, is_tag_macro: bool, -) -> impl Iterator, /*value*/ Rc)> + 'a { - let mut res = Vec::<(Rc, Rc)>::new(); - +) -> Vec { 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(); + return raw_attribs(node); } + let mut res = vec![]; for attrib in node.attributes() { let (key, value) = (attrib.name(), attrib.value()); @@ -686,7 +686,7 @@ fn iter_attribs<'a, U1, U2>( } } - res.into_iter() + res } fn parse_tag_theme<'a, U1, U2>(ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>) { @@ -707,15 +707,15 @@ fn parse_tag_theme<'a, U1, U2>(ctx: &mut ParserContext, node: roxmltree: fn parse_tag_template(file: &ParserFile, ctx: &mut ParserContext, node: roxmltree::Node<'_, '_>) { let mut template_name: Option> = None; - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); + let attribs = process_attribs(file, ctx, &node, false); - for (key, value) in attribs { - match key.as_ref() { + for pair in attribs { + match pair.attrib.as_ref() { "name" => { - template_name = Some(value); + template_name = Some(pair.value); } _ => { - print_invalid_attrib(&key, &value); + print_invalid_attrib(pair.value.as_ref(), pair.value.as_ref()); } } } @@ -737,17 +737,17 @@ fn parse_tag_template(file: &ParserFile, ctx: &mut ParserContext fn parse_tag_macro(file: &ParserFile, ctx: &mut ParserContext, node: roxmltree::Node<'_, '_>) { let mut macro_name: Option> = None; - let attribs: Vec<_> = iter_attribs(file, ctx, &node, true).collect(); + let attribs = process_attribs(file, ctx, &node, true); let mut macro_attribs = HashMap::, Rc>::new(); - for (key, value) in attribs { - match key.as_ref() { + for pair in attribs { + match pair.attrib.as_ref() { "name" => { - macro_name = Some(value); + macro_name = Some(pair.value); } _ => { - if macro_attribs.insert(key.clone(), value).is_some() { - log::warn!("macro attrib \"{key}\" already defined!"); + if macro_attribs.insert(pair.attrib.clone(), pair.value).is_some() { + log::warn!("macro attrib \"{}\" already defined!", pair.attrib); } } } @@ -762,21 +762,18 @@ fn parse_tag_macro(file: &ParserFile, ctx: &mut ParserContext, n } fn process_component<'a, U1, U2>( - file: &'a ParserFile, ctx: &mut ParserContext, - node: roxmltree::Node<'a, 'a>, component: Component, widget_id: WidgetID, + attribs: &[AttribPair], ) { - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); - let mut component_id: Option> = None; - for (key, value) in attribs { + for pair in attribs { #[allow(clippy::single_match)] - match key.as_ref() { + match pair.attrib.as_ref() { "id" => { - component_id = Some(value); + component_id = Some(pair.value.clone()); } _ => {} } @@ -785,20 +782,13 @@ fn process_component<'a, U1, U2>( ctx.insert_component(widget_id, component, component_id); } -fn parse_widget_universal<'a, U1, U2>( - file: &'a ParserFile, - ctx: &mut ParserContext, - node: roxmltree::Node<'a, 'a>, - widget_id: WidgetID, -) { - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); - - for (key, value) in attribs { +fn parse_widget_universal<'a, U1, U2>(ctx: &mut ParserContext, widget_id: WidgetID, attribs: &[AttribPair]) { + for pair in attribs { #[allow(clippy::single_match)] - match key.as_ref() { + match pair.attrib.as_ref() { "id" => { // Attach a specific widget to name-ID map (just like getElementById) - ctx.insert_id(&value, widget_id); + ctx.insert_id(&pair.value, widget_id); } _ => {} } @@ -827,36 +817,38 @@ fn parse_child<'a, U1, U2>( _ => {} } + let attribs = process_attribs(file, ctx, &child_node, false); + let mut new_widget_id: Option = None; match child_node.tag_name().name() { "include" => { - parse_tag_include(file, ctx, child_node, parent_id)?; + parse_tag_include(file, ctx, parent_id, &attribs)?; } "div" => { - new_widget_id = Some(parse_widget_div(file, ctx, child_node, parent_id)?); + new_widget_id = Some(parse_widget_div(file, ctx, child_node, parent_id, &attribs)?); } "rectangle" => { - new_widget_id = Some(parse_widget_rectangle(file, ctx, child_node, parent_id)?); + new_widget_id = Some(parse_widget_rectangle(file, ctx, child_node, parent_id, &attribs)?); } "label" => { - new_widget_id = Some(parse_widget_label(file, ctx, child_node, parent_id)?); + new_widget_id = Some(parse_widget_label(file, ctx, child_node, parent_id, &attribs)?); } "sprite" => { - new_widget_id = Some(parse_widget_sprite(file, ctx, child_node, parent_id)?); + new_widget_id = Some(parse_widget_sprite(file, ctx, child_node, parent_id, &attribs)?); } "Button" => { - new_widget_id = Some(parse_component_button(file, ctx, child_node, parent_id)?); + new_widget_id = Some(parse_component_button(file, ctx, child_node, parent_id, &attribs)?); } "Slider" => { - new_widget_id = Some(parse_component_slider(file, ctx, child_node, parent_id)?); + new_widget_id = Some(parse_component_slider(ctx, parent_id, &attribs)?); } "CheckBox" => { - new_widget_id = Some(parse_component_checkbox(file, ctx, child_node, parent_id)?); + new_widget_id = Some(parse_component_checkbox(ctx, parent_id, &attribs)?); } "" => { /* ignore */ } other_tag_name => { - parse_widget_other(other_tag_name, file, ctx, child_node, parent_id)?; + parse_widget_other(other_tag_name, file, ctx, parent_id, &attribs)?; } } @@ -864,20 +856,13 @@ fn parse_child<'a, U1, U2>( if let Some(widget_id) = new_widget_id && let Some(on_custom_attribs) = &ctx.doc_params.extra.on_custom_attribs { - let mut pairs = SmallVec::<[CustomAttribPair; 4]>::new(); + let mut pairs = SmallVec::<[AttribPair; 4]>::new(); - for attrib in child_node.attributes() { - let attr_name = attrib.name(); - if !attr_name.starts_with('_') || attr_name.is_empty() { + for pair in attribs { + if !pair.attrib.starts_with('_') || pair.attrib.is_empty() { continue; } - - let attr_without_prefix = &attr_name[1..]; // safe - - pairs.push(CustomAttribPair { - attrib: attr_without_prefix, - value: attrib.value(), - }); + pairs.push(pair.clone()); } if !pairs.is_empty() { @@ -921,16 +906,30 @@ fn create_default_context<'a, U1, U2>( } } -pub struct CustomAttribPair<'a> { - pub attrib: &'a str, // without _ at the beginning - pub value: &'a str, +#[derive(Clone)] +pub struct AttribPair { + pub attrib: Rc, + pub value: Rc, +} + +impl AttribPair { + fn new(attrib: A, value: V) -> Self + where + A: Into>, + V: Into>, + { + Self { + attrib: attrib.into(), + value: value.into(), + } + } } pub struct CustomAttribsInfo<'a> { pub parent_id: WidgetID, pub widget_id: WidgetID, pub widgets: &'a WidgetMap, - pub pairs: &'a [CustomAttribPair<'a>], + pub pairs: &'a [AttribPair], } // helper functions @@ -943,11 +942,11 @@ impl CustomAttribsInfo<'_> { self.widgets.get(self.widget_id)?.get_as_mut::() } - pub fn get_value(&self, attrib_name: &str) -> Option<&str> { + pub fn get_value(&self, attrib_name: &str) -> Option> { // O(n) search, these pairs won't be problematically big anyways for pair in self.pairs { - if pair.attrib == attrib_name { - return Some(pair.value); + if *pair.attrib == *attrib_name { + return Some(pair.value.clone()); } } @@ -958,35 +957,23 @@ impl CustomAttribsInfo<'_> { CustomAttribsInfoOwned { parent_id: self.parent_id, widget_id: self.widget_id, - pairs: self - .pairs - .iter() - .map(|p| CustomAttribPairOwned { - attrib: p.attrib.to_string(), - value: p.value.to_string(), - }) - .collect(), + pairs: self.pairs.iter().cloned().collect(), } } } -pub struct CustomAttribPairOwned { - pub attrib: String, // without _ at the beginning - pub value: String, -} - pub struct CustomAttribsInfoOwned { pub parent_id: WidgetID, pub widget_id: WidgetID, - pub pairs: Vec, + pub pairs: Vec, } impl CustomAttribsInfoOwned { pub fn get_value(&self, attrib_name: &str) -> Option<&str> { // O(n) search, these pairs won't be problematically big anyways for pair in &self.pairs { - if pair.attrib == attrib_name { - return Some(pair.value.as_str()); + if pair.attrib.as_ref() == attrib_name { + return Some(pair.value.as_ref()); } } @@ -1091,7 +1078,7 @@ fn parse_document_root( #[allow(clippy::single_match)] match child_node.tag_name().name() { /* topmost include directly in */ - "include" => parse_tag_include(file, ctx, child_node, parent_id)?, + "include" => parse_tag_include(file, ctx, parent_id, &raw_attribs(&child_node))?, "theme" => parse_tag_theme(ctx, child_node), "template" => parse_tag_template(file, ctx, child_node), "macro" => parse_tag_macro(file, ctx, child_node), diff --git a/wgui/src/parser/style.rs b/wgui/src/parser/style.rs index 0e52711..234190e 100644 --- a/wgui/src/parser/style.rs +++ b/wgui/src/parser/style.rs @@ -1,15 +1,13 @@ -use std::rc::Rc; - use taffy::{ - AlignContent, AlignItems, AlignSelf, BoxSizing, Display, FlexDirection, FlexWrap, JustifyContent, - JustifySelf, Overflow, + AlignContent, AlignItems, AlignSelf, BoxSizing, Display, FlexDirection, FlexWrap, JustifyContent, JustifySelf, + Overflow, }; use crate::{ drawing, parser::{ - is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val, - print_invalid_attrib, print_invalid_value, + is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val, print_invalid_attrib, + print_invalid_value, AttribPair, }, renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle}, widget::util::WLength, @@ -45,11 +43,12 @@ pub fn parse_color_opt(value: &str, color: &mut Option) { } } -pub fn parse_text_style(attribs: &[(Rc, Rc)]) -> TextStyle { +pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle { let mut style = TextStyle::default(); - for (key, value) in attribs { - match key.as_ref() { + for pair in attribs { + let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); + match key { "color" => { if let Some(color) = parse_color_hex(value) { style.color = Some(color); @@ -88,10 +87,11 @@ pub fn parse_text_style(attribs: &[(Rc, Rc)]) -> TextStyle { #[allow(clippy::too_many_lines)] #[allow(clippy::cognitive_complexity)] -pub fn parse_style(attribs: &[(Rc, Rc)]) -> taffy::Style { +pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style { let mut style = taffy::Style::default(); - for (key, value) in attribs { + for pair in attribs { + let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); match key.as_ref() { "display" => match value.as_ref() { "flex" => style.display = Display::Flex, diff --git a/wgui/src/parser/widget_div.rs b/wgui/src/parser/widget_div.rs index 3939820..cf1cbf3 100644 --- a/wgui/src/parser/widget_div.rs +++ b/wgui/src/parser/widget_div.rs @@ -1,9 +1,6 @@ use crate::{ layout::WidgetID, - parser::{ - ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal, - style::parse_style, - }, + parser::{parse_children, parse_widget_universal, style::parse_style, AttribPair, ParserContext, ParserFile}, widget::div::WidgetDiv, }; @@ -12,15 +9,13 @@ pub fn parse_widget_div<'a, U1, U2>( ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, + attribs: &[AttribPair], ) -> anyhow::Result { - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); - let style = parse_style(&attribs); + let style = parse_style(attribs); - let (new_id, _) = ctx - .layout - .add_child(parent_id, WidgetDiv::create(), style)?; + let (new_id, _) = ctx.layout.add_child(parent_id, WidgetDiv::create(), style)?; - parse_widget_universal(file, ctx, node, new_id); + parse_widget_universal(ctx, new_id, attribs); parse_children(file, ctx, node, new_id)?; Ok(new_id) diff --git a/wgui/src/parser/widget_label.rs b/wgui/src/parser/widget_label.rs index b554e01..31b1726 100644 --- a/wgui/src/parser/widget_label.rs +++ b/wgui/src/parser/widget_label.rs @@ -2,8 +2,9 @@ use crate::{ i18n::Translation, layout::WidgetID, parser::{ - ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal, + parse_children, parse_widget_universal, style::{parse_style, parse_text_style}, + AttribPair, ParserContext, ParserFile, }, widget::label::{WidgetLabel, WidgetLabelParams}, }; @@ -13,23 +14,24 @@ pub fn parse_widget_label<'a, U1, U2>( ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, + attribs: &[AttribPair], ) -> anyhow::Result { let mut params = WidgetLabelParams::default(); - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); - let style = parse_style(&attribs); - params.style = parse_text_style(&attribs); + let style = parse_style(attribs); + params.style = parse_text_style(attribs); - for (key, value) in attribs { - match &*key { + for pair in attribs { + let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); + match key { "text" => { if !value.is_empty() { - params.content = Translation::from_raw_text(&value); + params.content = Translation::from_raw_text(value); } } "translation" => { if !value.is_empty() { - params.content = Translation::from_translation_key(&value); + params.content = Translation::from_translation_key(value); } } _ => {} @@ -42,7 +44,7 @@ pub fn parse_widget_label<'a, U1, U2>( .layout .add_child(parent_id, WidgetLabel::create(&mut globals.get(), params), style)?; - parse_widget_universal(file, ctx, node, new_id); + parse_widget_universal(ctx, new_id, attribs); parse_children(file, ctx, node, new_id)?; Ok(new_id) diff --git a/wgui/src/parser/widget_rectangle.rs b/wgui/src/parser/widget_rectangle.rs index 51f5bdb..33a9030 100644 --- a/wgui/src/parser/widget_rectangle.rs +++ b/wgui/src/parser/widget_rectangle.rs @@ -2,9 +2,9 @@ use crate::{ drawing::GradientMode, layout::WidgetID, parser::{ - ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal, - print_invalid_attrib, + parse_children, parse_widget_universal, print_invalid_attrib, style::{parse_color, parse_round, parse_style}, + AttribPair, ParserContext, ParserFile, }, widget::rectangle::{WidgetRectangle, WidgetRectangleParams}, }; @@ -14,42 +14,43 @@ pub fn parse_widget_rectangle<'a, U1, U2>( ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, + attribs: &[AttribPair], ) -> anyhow::Result { let mut params = WidgetRectangleParams::default(); - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); let style = parse_style(&attribs); - for (key, value) in attribs { - match &*key { + for pair in attribs { + let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); + match key { "color" => { - parse_color(&value, &mut params.color); + parse_color(value, &mut params.color); } "color2" => { - parse_color(&value, &mut params.color2); + parse_color(value, &mut params.color2); } "gradient" => { - params.gradient = match &*value { + params.gradient = match value { "horizontal" => GradientMode::Horizontal, "vertical" => GradientMode::Vertical, "radial" => GradientMode::Radial, "none" => GradientMode::None, _ => { - print_invalid_attrib(&key, &value); + print_invalid_attrib(key, value); GradientMode::None } } } "round" => { - parse_round(&value, &mut params.round); + parse_round(value, &mut params.round); } "border" => { params.border = value.parse().unwrap_or_else(|_| { - print_invalid_attrib(&key, &value); + print_invalid_attrib(key, value); 0.0 }); } "border_color" => { - parse_color(&value, &mut params.border_color); + parse_color(value, &mut params.border_color); } _ => {} } @@ -59,7 +60,7 @@ pub fn parse_widget_rectangle<'a, U1, U2>( .layout .add_child(parent_id, WidgetRectangle::create(params), style)?; - parse_widget_universal(file, ctx, node, new_id); + parse_widget_universal(ctx, new_id, attribs); parse_children(file, ctx, node, new_id)?; Ok(new_id) diff --git a/wgui/src/parser/widget_sprite.rs b/wgui/src/parser/widget_sprite.rs index 72902f7..cf11218 100644 --- a/wgui/src/parser/widget_sprite.rs +++ b/wgui/src/parser/widget_sprite.rs @@ -1,6 +1,6 @@ use crate::{ layout::WidgetID, - parser::{ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal, style::parse_style}, + parser::{parse_children, parse_widget_universal, style::parse_style, AttribPair, ParserContext, ParserFile}, renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData}, widget::sprite::{WidgetSprite, WidgetSpriteParams}, }; @@ -12,17 +12,18 @@ pub fn parse_widget_sprite<'a, U1, U2>( ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, + attribs: &[AttribPair], ) -> anyhow::Result { let mut params = WidgetSpriteParams::default(); - let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); let style = parse_style(&attribs); let mut glyph = None; - for (key, value) in attribs { - match key.as_ref() { + for pair in attribs { + let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); + match key { "src" => { if !value.is_empty() { - glyph = match CustomGlyphContent::from_assets(&mut ctx.layout.state.globals.assets(), &value) { + glyph = match CustomGlyphContent::from_assets(&mut ctx.layout.state.globals.assets(), value) { Ok(glyph) => Some(glyph), Err(e) => { log::warn!("failed to load {value}: {e}"); @@ -32,15 +33,15 @@ pub fn parse_widget_sprite<'a, U1, U2>( } } "src_ext" => { - if !value.is_empty() && std::fs::exists(value.as_ref()).unwrap_or(false) { - glyph = CustomGlyphContent::from_file(&value).ok(); + if !value.is_empty() && std::fs::exists(value).unwrap_or(false) { + glyph = CustomGlyphContent::from_file(value).ok(); } } "color" => { - if let Some(color) = parse_color_hex(&value) { + if let Some(color) = parse_color_hex(value) { params.color = Some(color); } else { - print_invalid_attrib(&key, &value); + print_invalid_attrib(key, value); } } _ => {} @@ -55,7 +56,7 @@ pub fn parse_widget_sprite<'a, U1, U2>( let (new_id, _) = ctx.layout.add_child(parent_id, WidgetSprite::create(params), style)?; - parse_widget_universal(file, ctx, node, new_id); + parse_widget_universal(ctx, new_id, attribs); parse_children(file, ctx, node, new_id)?; Ok(new_id) diff --git a/wlx-overlay-s/src/gui/panel/button.rs b/wlx-overlay-s/src/gui/panel/button.rs index 6e9a2ae..d11b970 100644 --- a/wlx-overlay-s/src/gui/panel/button.rs +++ b/wlx-overlay-s/src/gui/panel/button.rs @@ -11,7 +11,7 @@ use wgui::{ use crate::{ backend::{common::OverlaySelector, overlay::OverlayID, task::TaskType, wayvr::WayVRAction}, - config::{AStrSetExt, save_layout}, + config::{save_layout, AStrSetExt}, state::AppState, }; @@ -24,8 +24,8 @@ pub(super) fn setup_custom_button( _app: &AppState, ) { const EVENTS: [(&str, EventListenerKind); 2] = [ - ("press", EventListenerKind::MousePress), - ("release", EventListenerKind::MouseRelease), + ("_press", EventListenerKind::MousePress), + ("_release", EventListenerKind::MouseRelease), ]; for (name, kind) in &EVENTS { diff --git a/wlx-overlay-s/src/gui/panel/label.rs b/wlx-overlay-s/src/gui/panel/label.rs index 846b555..caf72ec 100644 --- a/wlx-overlay-s/src/gui/panel/label.rs +++ b/wlx-overlay-s/src/gui/panel/label.rs @@ -15,7 +15,7 @@ use wgui::{ event::{self, EventCallback, EventListenerCollection, ListenerHandleVec}, i18n::Translation, layout::Layout, - parser::{CustomAttribsInfoOwned, parse_color_hex}, + parser::{parse_color_hex, CustomAttribsInfoOwned}, widget::label::WidgetLabel, }; @@ -31,14 +31,14 @@ pub(super) fn setup_custom_label( listener_handles: &mut ListenerHandleVec, app: &AppState, ) { - let Some(source) = attribs.get_value("source") else { + let Some(source) = attribs.get_value("_source") else { log::warn!("custom label with no source!"); return; }; let callback: EventCallback = match source { "shell" => { - let Some(exec) = attribs.get_value("exec") else { + let Some(exec) = attribs.get_value("_exec") else { log::warn!("label with shell source but no exec attribute!"); return; }; @@ -57,7 +57,7 @@ pub(super) fn setup_custom_label( }) } "fifo" => { - let Some(path) = attribs.get_value("path") else { + let Some(path) = attribs.get_value("_path") else { log::warn!("label with fifo source but no path attribute!"); return; }; @@ -76,7 +76,7 @@ pub(super) fn setup_custom_label( } "battery" => { let Some(device) = attribs - .get_value("device") + .get_value("_device") .and_then(|s| s.parse::().ok()) else { log::warn!("label with battery source but no device attribute!"); @@ -85,19 +85,19 @@ pub(super) fn setup_custom_label( let state = BatteryLabelState { low_color: attribs - .get_value("low_color") + .get_value("_low_color") .and_then(parse_color_hex) .unwrap_or(BAT_LOW), normal_color: attribs - .get_value("normal_color") + .get_value("_normal_color") .and_then(parse_color_hex) .unwrap_or(BAT_NORMAL), charging_color: attribs - .get_value("charging_color") + .get_value("_charging_color") .and_then(parse_color_hex) .unwrap_or(BAT_CHARGING), low_threshold: attribs - .get_value("low_threshold") + .get_value("_low_threshold") .and_then(|s| s.parse().ok()) .unwrap_or(BAT_LOW_THRESHOLD), device, @@ -108,7 +108,7 @@ pub(super) fn setup_custom_label( }) } "clock" => { - let Some(display) = attribs.get_value("display") else { + let Some(display) = attribs.get_value("_display") else { log::warn!("label with clock source but no display attribute!"); return; }; @@ -116,7 +116,7 @@ pub(super) fn setup_custom_label( let format = match display { "name" => { let maybe_pretty_tz = attribs - .get_value("timezone") + .get_value("_timezone") .and_then(|tz| tz.parse::().ok()) .and_then(|tz_idx| app.session.config.timezones.get(tz_idx)) .and_then(|tz_name| { @@ -152,7 +152,7 @@ pub(super) fn setup_custom_label( }; let tz_str = attribs - .get_value("timezone") + .get_value("_timezone") .and_then(|tz| tz.parse::().ok()) .and_then(|tz_idx| app.session.config.timezones.get(tz_idx));