more useful parser warnings + cleanups

This commit is contained in:
galister
2026-01-08 19:16:11 +09:00
parent 76f328a16e
commit cdf4ed3882
20 changed files with 399 additions and 360 deletions

View File

@@ -29,7 +29,7 @@
<div flex_direction="row" gap="8" width="100%" height="100%" padding="4" interactable="0"> <div flex_direction="row" gap="8" width="100%" height="100%" padding="4" interactable="0">
<!-- LEFT MENU --> <!-- LEFT MENU -->
<div id="menu" <div id="menu"
width="~size_size" width="~side_size"
min_width="~side_size" min_width="~side_size"
max_width="~side_size" max_width="~side_size"
height="100%" height="100%"

View File

@@ -5,7 +5,7 @@ use crate::{
i18n::Translation, i18n::Translation,
layout::WidgetID, layout::WidgetID,
parser::{ parser::{
parse_check_f32, parse_check_i32, parse_children, parse_f32, print_invalid_attrib, process_component, parse_children, parse_f32, process_component,
style::{parse_color_opt, parse_round, parse_style, parse_text_style}, style::{parse_color_opt, parse_round, parse_style, parse_text_style},
AttribPair, ParserContext, ParserFile, AttribPair, ParserContext, ParserFile,
}, },
@@ -19,6 +19,7 @@ pub fn parse_component_button<'a>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
tag_name: &str,
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut color: Option<Color> = None; let mut color: Option<Color> = None;
let mut border = 2.0; let mut border = 2.0;
@@ -34,8 +35,8 @@ pub fn parse_component_button<'a>(
let mut translation: Option<Translation> = None; let mut translation: Option<Translation> = None;
let text_style = parse_text_style(attribs); let text_style = parse_text_style(ctx, attribs, tag_name);
let style = parse_style(attribs); let style = parse_style(ctx, attribs, tag_name);
for pair in attribs { for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
@@ -51,22 +52,29 @@ pub fn parse_component_button<'a>(
} }
} }
"round" => { "round" => {
parse_round(value, &mut round, ctx.doc_params.globals.get().defaults.rounding_mult); parse_round(
ctx,
tag_name,
key,
value,
&mut round,
ctx.doc_params.globals.get().defaults.rounding_mult,
);
} }
"color" => { "color" => {
parse_color_opt(value, &mut color); parse_color_opt(ctx, tag_name, key, value, &mut color);
} }
"border" => { "border" => {
parse_check_f32(value, &mut border); ctx.parse_check_f32(tag_name, key, value, &mut border);
} }
"border_color" => { "border_color" => {
parse_color_opt(value, &mut border_color); parse_color_opt(ctx, tag_name, key, value, &mut border_color);
} }
"hover_color" => { "hover_color" => {
parse_color_opt(value, &mut hover_color); parse_color_opt(ctx, tag_name, key, value, &mut hover_color);
} }
"hover_border_color" => { "hover_border_color" => {
parse_color_opt(value, &mut hover_border_color); parse_color_opt(ctx, tag_name, key, value, &mut hover_border_color);
} }
"sprite_src" | "sprite_src_ext" | "sprite_src_builtin" | "sprite_src_internal" => { "sprite_src" | "sprite_src_ext" | "sprite_src_builtin" | "sprite_src_internal" => {
let asset_path = match key { let asset_path = match key {
@@ -90,14 +98,14 @@ pub fn parse_component_button<'a>(
"top" => Some(tooltip::TooltipSide::Top), "top" => Some(tooltip::TooltipSide::Top),
"bottom" => Some(tooltip::TooltipSide::Bottom), "bottom" => Some(tooltip::TooltipSide::Bottom),
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
None None
} }
} }
} }
"sticky" => { "sticky" => {
let mut sticky_i32 = 0; let mut sticky_i32 = 0;
sticky = parse_check_i32(value, &mut sticky_i32) && sticky_i32 == 1; sticky = ctx.parse_check_i32(tag_name, key, value, &mut sticky_i32) && sticky_i32 == 1;
} }
"long_press_time" => { "long_press_time" => {
long_press_time = parse_f32(value).unwrap_or(long_press_time); long_press_time = parse_f32(value).unwrap_or(long_press_time);

View File

@@ -2,9 +2,7 @@ use crate::{
components::{checkbox, radio_group::ComponentRadioGroup, Component}, components::{checkbox, radio_group::ComponentRadioGroup, Component},
i18n::Translation, i18n::Translation,
layout::WidgetID, layout::WidgetID,
parser::{ parser::{process_component, style::parse_style, AttribPair, Fetchable, ParserContext},
parse_check_f32, parse_check_i32, process_component, style::parse_style, AttribPair, Fetchable, ParserContext,
},
}; };
pub enum CheckboxKind { pub enum CheckboxKind {
@@ -16,6 +14,7 @@ pub fn parse_component_checkbox(
ctx: &mut ParserContext, ctx: &mut ParserContext,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
tag_name: &str,
kind: CheckboxKind, kind: CheckboxKind,
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut box_size = 24.0; let mut box_size = 24.0;
@@ -23,7 +22,7 @@ pub fn parse_component_checkbox(
let mut checked = 0; let mut checked = 0;
let mut component_value = None; let mut component_value = None;
let style = parse_style(attribs); let style = parse_style(ctx, attribs, tag_name);
for pair in attribs { for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
@@ -42,10 +41,10 @@ pub fn parse_component_checkbox(
component_value = Some(value.into()); component_value = Some(value.into());
} }
"box_size" => { "box_size" => {
parse_check_f32(value, &mut box_size); ctx.parse_check_f32(tag_name, key, value, &mut box_size);
} }
"checked" => { "checked" => {
parse_check_i32(value, &mut checked); ctx.parse_check_i32(tag_name, key, value, &mut checked);
} }
_ => {} _ => {}
} }

View File

@@ -1,7 +1,7 @@
use crate::{ use crate::{
components::{Component, radio_group}, components::{radio_group, Component},
layout::WidgetID, layout::WidgetID,
parser::{AttribPair, ParserContext, ParserFile, parse_children, process_component, style::parse_style}, parser::{parse_children, process_component, style::parse_style, AttribPair, ParserContext, ParserFile},
}; };
pub fn parse_component_radio_group<'a>( pub fn parse_component_radio_group<'a>(
@@ -10,8 +10,9 @@ pub fn parse_component_radio_group<'a>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
tag_name: &str,
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let style = parse_style(attribs); let style = parse_style(ctx, attribs, tag_name);
let (widget, component) = radio_group::construct(&mut ctx.get_construct_essentials(parent_id), style)?; let (widget, component) = radio_group::construct(&mut ctx.get_construct_essentials(parent_id), style)?;

View File

@@ -1,7 +1,7 @@
use crate::{ use crate::{
components::{Component, slider}, components::{slider, Component},
layout::WidgetID, layout::WidgetID,
parser::{AttribPair, ParserContext, parse_check_f32, parse_check_i32, process_component, style::parse_style}, parser::{process_component, style::parse_style, AttribPair, ParserContext},
widget::ConstructEssentials, widget::ConstructEssentials,
}; };
@@ -9,6 +9,7 @@ pub fn parse_component_slider(
ctx: &mut ParserContext, ctx: &mut ParserContext,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
tag_name: &str,
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut min_value = 0.0; let mut min_value = 0.0;
let mut max_value = 1.0; let mut max_value = 1.0;
@@ -16,25 +17,25 @@ pub fn parse_component_slider(
let mut step = 1.0; let mut step = 1.0;
let mut show_value = 1; let mut show_value = 1;
let style = parse_style(attribs); let style = parse_style(ctx, attribs, tag_name);
for pair in attribs { for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
match key { match key {
"min_value" => { "min_value" => {
parse_check_f32(value, &mut min_value); ctx.parse_check_f32(tag_name, key, value, &mut min_value);
} }
"max_value" => { "max_value" => {
parse_check_f32(value, &mut max_value); ctx.parse_check_f32(tag_name, key, value, &mut max_value);
} }
"value" => { "value" => {
parse_check_f32(value, &mut initial_value); ctx.parse_check_f32(tag_name, key, value, &mut initial_value);
} }
"step" => { "step" => {
parse_check_f32(value, &mut step); ctx.parse_check_f32(tag_name, key, value, &mut step);
} }
"show_value" => { "show_value" => {
parse_check_i32(value, &mut show_value); ctx.parse_check_i32(tag_name, key, value, &mut show_value);
} }
_ => {} _ => {}
} }

View File

@@ -10,16 +10,9 @@ mod widget_rectangle;
mod widget_sprite; mod widget_sprite;
use crate::{ use crate::{
assets::{AssetPath, AssetPathOwned, normalize_path}, assets::{normalize_path, AssetPath, AssetPathOwned}, components::{Component, ComponentWeak}, drawing::{self}, globals::WguiGlobals, i18n::Translation, layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap, WidgetPair}, log::LogErr, parser::{
components::{Component, ComponentWeak},
drawing::{self},
globals::WguiGlobals,
i18n::Translation,
layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap, WidgetPair},
log::LogErr,
parser::{
component_button::parse_component_button, component_button::parse_component_button,
component_checkbox::{CheckboxKind, parse_component_checkbox}, component_checkbox::{parse_component_checkbox, CheckboxKind},
component_radio_group::parse_component_radio_group, component_radio_group::parse_component_radio_group,
component_slider::parse_component_slider, component_slider::parse_component_slider,
widget_div::parse_widget_div, widget_div::parse_widget_div,
@@ -27,9 +20,7 @@ use crate::{
widget_label::parse_widget_label, widget_label::parse_widget_label,
widget_rectangle::parse_widget_rectangle, widget_rectangle::parse_widget_rectangle,
widget_sprite::parse_widget_sprite, widget_sprite::parse_widget_sprite,
}, }, widget::ConstructEssentials, windowing::context_menu
widget::ConstructEssentials,
windowing::context_menu,
}; };
use anyhow::Context; use anyhow::Context;
use ouroboros::self_referencing; use ouroboros::self_referencing;
@@ -224,7 +215,7 @@ impl ParserState {
template_parameters: HashMap<Rc<str>, Rc<str>>, template_parameters: HashMap<Rc<str>, Rc<str>>,
) -> anyhow::Result<ParserData> { ) -> anyhow::Result<ParserData> {
let Some(template) = self.data.templates.get(template_name) else { let Some(template) = self.data.templates.get(template_name) else {
anyhow::bail!("no template named \"{template_name}\" found"); anyhow::bail!("{:?}: no template named \"{template_name}\" found", self.path.get_path_buf());
}; };
let mut ctx = ParserContext { let mut ctx = ParserContext {
@@ -314,7 +305,7 @@ impl ParserState {
}); });
} }
other => { other => {
anyhow::bail!("unexpected <{other}> tag"); anyhow::bail!("{:?}: unexpected <{other}> tag", self.path.get_path_buf());
} }
} }
} }
@@ -443,7 +434,7 @@ impl ParserContext<'_> {
.insert(id.clone(), component.weak()) .insert(id.clone(), component.weak())
.is_some() .is_some()
{ {
log::warn!("duplicate component ID \"{id}\" in the same layout file!"); log::warn!("{}: duplicate component ID \"{id}\"", self.doc_params.path.get_str());
} }
self.data_local.components.push(component); self.data_local.components.push(component);
@@ -451,7 +442,7 @@ impl ParserContext<'_> {
fn insert_id(&mut self, id: &Rc<str>, widget_id: WidgetID) { fn insert_id(&mut self, id: &Rc<str>, widget_id: WidgetID) {
if self.data_local.ids.insert(id.clone(), widget_id).is_some() { if self.data_local.ids.insert(id.clone(), widget_id).is_some() {
log::warn!("duplicate widget ID \"{id}\" in the same layout file!"); log::warn!("{}: duplicate widget ID \"{id}\"", self.doc_params.path.get_str());
} }
} }
@@ -479,6 +470,77 @@ impl ParserContext<'_> {
insert_color_vars!(self, "faded", def.faded_color, def.translucent_alpha); insert_color_vars!(self, "faded", def.faded_color, def.translucent_alpha);
insert_color_vars!(self, "bg", def.bg_color, def.translucent_alpha); insert_color_vars!(self, "bg", def.bg_color, def.translucent_alpha);
} }
fn print_invalid_attrib(&self, tag_name: &str, key: &str, value: &str) {
log::warn!("{}: <{tag_name}> value for \"{key}\" is invalid: \"{value}\"", self.doc_params.path.get_str());
}
fn print_missing_attrib(&self, tag_name: &str, attr: &str) {
log::warn!("{}: <{tag_name}> is missing \"{attr}\".", self.doc_params.path.get_str());
}
fn parse_val(&self, tag_name: &str, key: &str, value: &str) -> Option<f32> {
let Ok(val) = value.parse::<f32>() else {
self.print_invalid_attrib(tag_name, key, value);
return None;
};
Some(val)
}
fn parse_percent(&self, tag_name: &str, key: &str, value: &str) -> Option<f32> {
let Some(val_str) = value.split('%').next() else {
self.print_invalid_attrib(tag_name, key, value);
return None;
};
let Ok(val) = val_str.parse::<f32>() else {
self.print_invalid_attrib(tag_name, key, value);
return None;
};
Some(val / 100.0)
}
fn parse_size_unit<T>(&self, tag_name: &str, key: &str, value: &str) -> Option<T>
where
T: taffy::prelude::FromPercent + taffy::prelude::FromLength,
{
if is_percent(value) {
Some(taffy::prelude::percent(self.parse_percent(tag_name, key, value)?))
} else {
Some(taffy::prelude::length(parse_f32(value)?))
}
}
fn parse_check_i32(&self, tag_name: &str, key: &str, value: &str, num: &mut i32) -> bool {
if let Some(value) = parse_i32(value) {
*num = value;
true
} else {
self.print_invalid_attrib(tag_name, key, value);
false
}
}
fn parse_check_f32(&self, tag_name: &str, key: &str, value: &str, num: &mut f32) -> bool {
if let Some(value) = parse_f32(value) {
*num = value;
true
} else {
self.print_invalid_attrib(tag_name, key, value);
false
}
}
}
fn parse_i32(value: &str) -> Option<i32> {
value.parse::<i32>().ok()
}
fn parse_f32(value: &str) -> Option<f32> {
value.parse::<f32>().ok()
}
fn is_percent(value: &str) -> bool {
value.ends_with('%')
} }
// Parses a color from a HTML hex string // Parses a color from a HTML hex string
@@ -510,7 +572,6 @@ pub fn parse_color_hex(html_hex: &str) -> Option<drawing::Color> {
f32::from(a) / 255., f32::from(a) / 255.,
)); ));
} }
log::warn!("failed to parse color \"{html_hex}\"");
None None
} }
@@ -522,81 +583,6 @@ fn require_tag_by_name<'a>(node: &roxmltree::Node<'a, 'a>, name: &str) -> anyhow
get_tag_by_name(node, name).ok_or_else(|| anyhow::anyhow!("Tag \"{name}\" not found")) get_tag_by_name(node, name).ok_or_else(|| anyhow::anyhow!("Tag \"{name}\" not found"))
} }
fn print_invalid_attrib(key: &str, value: &str) {
log::warn!("Invalid value \"{value}\" in attribute \"{key}\"");
}
fn print_missing_attrib(tag_name: &str, attr: &str) {
log::warn!("Missing attribute {attr} in tag <{tag_name}>");
}
fn print_invalid_value(value: &str) {
log::warn!("Invalid value \"{value}\"");
}
fn parse_val(value: &str) -> Option<f32> {
let Ok(val) = value.parse::<f32>() else {
print_invalid_value(value);
return None;
};
Some(val)
}
fn is_percent(value: &str) -> bool {
value.ends_with('%')
}
fn parse_percent(value: &str) -> Option<f32> {
let Some(val_str) = value.split('%').next() else {
print_invalid_value(value);
return None;
};
let Ok(val) = val_str.parse::<f32>() else {
print_invalid_value(value);
return None;
};
Some(val / 100.0)
}
fn parse_i32(value: &str) -> Option<i32> {
value.parse::<i32>().ok()
}
fn parse_f32(value: &str) -> Option<f32> {
value.parse::<f32>().ok()
}
fn parse_check_i32(value: &str, num: &mut i32) -> bool {
if let Some(value) = parse_i32(value) {
*num = value;
true
} else {
print_invalid_value(value);
false
}
}
fn parse_check_f32(value: &str, num: &mut f32) -> bool {
if let Some(value) = parse_f32(value) {
*num = value;
true
} else {
print_invalid_value(value);
false
}
}
fn parse_size_unit<T>(value: &str) -> Option<T>
where
T: taffy::prelude::FromPercent + taffy::prelude::FromLength,
{
if is_percent(value) {
Some(taffy::prelude::percent(parse_percent(value)?))
} else {
Some(taffy::prelude::length(parse_f32(value)?))
}
}
fn parse_widget_other_internal( fn parse_widget_other_internal(
template: &Rc<Template>, template: &Rc<Template>,
@@ -631,7 +617,7 @@ fn parse_widget_other(
attribs: &[AttribPair], attribs: &[AttribPair],
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let Some(template) = ctx.get_template(xml_tag_name) else { let Some(template) = ctx.get_template(xml_tag_name) else {
log::error!("Undefined tag named \"{xml_tag_name}\""); log::error!("{}: Undefined tag named \"{xml_tag_name}\"", ctx.doc_params.path.get_str());
return Ok(()); // not critical return Ok(()); // not critical
}; };
@@ -647,6 +633,8 @@ fn parse_tag_include(
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
const TAG_NAME: &str = "include";
let mut path = None; let mut path = None;
let mut optional = false; let mut optional = false;
@@ -678,16 +666,16 @@ fn parse_tag_include(
} }
"optional" => { "optional" => {
let mut optional_i32 = 0; let mut optional_i32 = 0;
optional = parse_check_i32(&pair.value, &mut optional_i32) && optional_i32 == 1; optional = ctx.parse_check_i32(TAG_NAME, &pair.attrib, &pair.value, &mut optional_i32) && optional_i32 == 1;
} }
_ => { _ => {
print_invalid_attrib(pair.attrib.as_ref(), pair.value.as_ref()); ctx.print_invalid_attrib(TAG_NAME, pair.attrib.as_ref(), pair.value.as_ref());
} }
} }
} }
let Some(path) = path else { let Some(path) = path else {
log::warn!("include tag with no source! specify either: src, src_builtin, src_internal"); ctx.print_missing_attrib("include", "src");
return Ok(()); return Ok(());
}; };
let path_ref = path.as_ref(); let path_ref = path.as_ref();
@@ -703,7 +691,7 @@ fn parse_tag_include(
Ok(()) Ok(())
} }
fn parse_tag_var<'a>(ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>) { fn parse_tag_var<'a>(ctx: &mut ParserContext, tag_name: &str, node: roxmltree::Node<'a, 'a>) {
let mut out_key: Option<&str> = None; let mut out_key: Option<&str> = None;
let mut out_value: Option<&str> = None; let mut out_value: Option<&str> = None;
@@ -718,18 +706,18 @@ fn parse_tag_var<'a>(ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>) {
out_value = Some(value); out_value = Some(value);
} }
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
} }
let Some(key) = out_key else { let Some(key) = out_key else {
print_missing_attrib("var", "key"); ctx.print_missing_attrib(tag_name, "key");
return; return;
}; };
let Some(value) = out_value else { let Some(value) = out_value else {
print_missing_attrib("var", "value"); ctx.print_missing_attrib(tag_name, "value");
return; return;
}; };
@@ -769,7 +757,10 @@ fn process_attrib(
match ctx.get_var(name) { match ctx.get_var(name) {
Some(name) => AttribPair::new(key, name), Some(name) => AttribPair::new(key, name),
None => AttribPair::new(key, "undefined"), None => {
log::warn!("{}: undefined variable \"{value}\"", ctx.doc_params.path.get_str());
AttribPair::new(key, "undefined")
},
} }
} else { } else {
AttribPair::new(key, replace_vars(value, template_parameters)) AttribPair::new(key, replace_vars(value, template_parameters))
@@ -806,7 +797,7 @@ fn process_attribs<'a>(
res.push(process_attrib(&file.template_parameters, ctx, macro_key, macro_value)); res.push(process_attrib(&file.template_parameters, ctx, macro_key, macro_value));
} }
} else { } else {
log::warn!("requested macro named \"{value}\" not found!"); log::warn!("{}: requested macro named \"{value}\" not found!", ctx.doc_params.path.get_str());
} }
} else { } else {
res.push(process_attrib(&file.template_parameters, ctx, key, value)); res.push(process_attrib(&file.template_parameters, ctx, key, value));
@@ -821,11 +812,11 @@ fn parse_tag_theme<'a>(ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>) {
let child_name = child_node.tag_name().name(); let child_name = child_node.tag_name().name();
match child_name { match child_name {
"var" => { "var" => {
parse_tag_var(ctx, child_node); parse_tag_var(ctx, child_name, child_node);
} }
"" => { /* ignore */ } "" => { /* ignore */ }
_ => { _ => {
print_invalid_value(child_name); log::warn!("{}: <{child_name}> is not a valid child to <theme>.", ctx.doc_params.path.get_str());
} }
} }
} }
@@ -842,13 +833,13 @@ fn parse_tag_template(file: &ParserFile, ctx: &mut ParserContext, node: roxmltre
template_name = Some(pair.value); template_name = Some(pair.value);
} }
_ => { _ => {
print_invalid_attrib(pair.value.as_ref(), pair.value.as_ref()); ctx.print_invalid_attrib("template", &pair.attrib, pair.value.as_ref());
} }
} }
} }
let Some(name) = template_name else { let Some(name) = template_name else {
log::error!("Template name not specified, ignoring"); ctx.print_missing_attrib("template", "name");
return; return;
}; };
@@ -874,14 +865,14 @@ fn parse_tag_macro(file: &ParserFile, ctx: &mut ParserContext, node: roxmltree::
} }
_ => { _ => {
if macro_attribs.insert(pair.attrib.clone(), pair.value).is_some() { if macro_attribs.insert(pair.attrib.clone(), pair.value).is_some() {
log::warn!("macro attrib \"{}\" already defined!", pair.attrib); log::warn!("{}: macro attrib \"{}\" already defined!", ctx.doc_params.path.get_str(), pair.attrib);
} }
} }
} }
} }
let Some(name) = macro_name else { let Some(name) = macro_name else {
log::error!("Macro name not specified, ignoring"); ctx.print_missing_attrib("macro", "name");
return; return;
}; };
@@ -904,7 +895,7 @@ fn process_component(ctx: &mut ParserContext, component: Component, widget_id: W
ctx.insert_component(widget_id, component, component_id); ctx.insert_component(widget_id, component, component_id);
} }
fn parse_widget_universal(ctx: &mut ParserContext, widget: &WidgetPair, attribs: &[AttribPair]) { fn parse_widget_universal(ctx: &mut ParserContext, widget: &WidgetPair, attribs: &[AttribPair], tag_name: &str) {
for pair in attribs { for pair in attribs {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match pair.attrib.as_ref() { match pair.attrib.as_ref() {
@@ -916,21 +907,21 @@ fn parse_widget_universal(ctx: &mut ParserContext, widget: &WidgetPair, attribs:
if let Some(num) = parse_i32(&pair.value) { if let Some(num) = parse_i32(&pair.value) {
widget.widget.state().flags.new_pass = num != 0; widget.widget.state().flags.new_pass = num != 0;
} else { } else {
print_invalid_attrib(&pair.attrib, &pair.value); ctx.print_invalid_attrib(tag_name, &pair.attrib, &pair.value);
} }
} }
"interactable" => { "interactable" => {
if let Some(num) = parse_i32(&pair.value) { if let Some(num) = parse_i32(&pair.value) {
widget.widget.state().flags.interactable = num != 0; widget.widget.state().flags.interactable = num != 0;
} else { } else {
print_invalid_attrib(&pair.attrib, &pair.value); ctx.print_invalid_attrib(tag_name, &pair.attrib, &pair.value);
} }
} }
"consume_mouse_events" => { "consume_mouse_events" => {
if let Some(num) = parse_i32(&pair.value) { if let Some(num) = parse_i32(&pair.value) {
widget.widget.state().flags.consume_mouse_events = num != 0; widget.widget.state().flags.consume_mouse_events = num != 0;
} else { } else {
print_invalid_attrib(&pair.attrib, &pair.value); ctx.print_invalid_attrib(tag_name, &pair.attrib, &pair.value);
} }
} }
_ => {} _ => {}
@@ -945,6 +936,7 @@ fn parse_child<'a>(
child_node: roxmltree::Node<'a, 'a>, child_node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let tag_name = child_node.tag_name().name();
match parent_node.attribute("ignore_in_mode") { match parent_node.attribute("ignore_in_mode") {
Some("dev") => { Some("dev") => {
if !ctx.doc_params.extra.dev_mode { if !ctx.doc_params.extra.dev_mode {
@@ -956,44 +948,44 @@ fn parse_child<'a>(
return Ok(()); // do not parse return Ok(()); // do not parse
} }
} }
Some(s) => print_invalid_attrib("ignore_in_mode", s), Some(s) => ctx.print_invalid_attrib(tag_name, "ignore_in_mode", s),
_ => {} _ => {}
} }
let attribs = process_attribs(file, ctx, &child_node, false); let attribs = process_attribs(file, ctx, &child_node, false);
let mut new_widget_id: Option<WidgetID> = None; let mut new_widget_id: Option<WidgetID> = None;
match child_node.tag_name().name() { match tag_name {
"include" => { "include" => {
parse_tag_include(file, ctx, parent_id, &attribs)?; parse_tag_include(file, ctx, parent_id, &attribs)?;
} }
"div" => { "div" => {
new_widget_id = Some(parse_widget_div(file, ctx, child_node, parent_id, &attribs)?); new_widget_id = Some(parse_widget_div(file, ctx, child_node, parent_id, &attribs, tag_name)?);
} }
"rectangle" => { "rectangle" => {
new_widget_id = Some(parse_widget_rectangle(file, ctx, child_node, parent_id, &attribs)?); new_widget_id = Some(parse_widget_rectangle(file, ctx, child_node, parent_id, &attribs, tag_name)?);
} }
"label" => { "label" => {
new_widget_id = Some(parse_widget_label(file, ctx, child_node, parent_id, &attribs)?); new_widget_id = Some(parse_widget_label(file, ctx, child_node, parent_id, &attribs, tag_name)?);
} }
"sprite" => { "sprite" => {
new_widget_id = Some(parse_widget_sprite(file, ctx, child_node, parent_id, &attribs)?); new_widget_id = Some(parse_widget_sprite(file, ctx, child_node, parent_id, &attribs, tag_name)?);
} }
"image" => { "image" => {
new_widget_id = Some(parse_widget_image(file, ctx, child_node, parent_id, &attribs)?); new_widget_id = Some(parse_widget_image(file, ctx, child_node, parent_id, &attribs, tag_name)?);
} }
"Button" => { "Button" => {
new_widget_id = Some(parse_component_button(file, ctx, child_node, parent_id, &attribs)?); new_widget_id = Some(parse_component_button(file, ctx, child_node, parent_id, &attribs, tag_name)?);
} }
"Slider" => { "Slider" => {
new_widget_id = Some(parse_component_slider(ctx, parent_id, &attribs)?); new_widget_id = Some(parse_component_slider(ctx, parent_id, &attribs, tag_name)?);
} }
"CheckBox" => { "CheckBox" => {
new_widget_id = Some(parse_component_checkbox( new_widget_id = Some(parse_component_checkbox(
ctx, ctx,
parent_id, parent_id,
&attribs, &attribs,
tag_name,
CheckboxKind::CheckBox, CheckboxKind::CheckBox,
)?); )?);
} }
@@ -1002,11 +994,12 @@ fn parse_child<'a>(
ctx, ctx,
parent_id, parent_id,
&attribs, &attribs,
tag_name,
CheckboxKind::RadioBox, CheckboxKind::RadioBox,
)?); )?);
} }
"RadioGroup" => { "RadioGroup" => {
new_widget_id = Some(parse_component_radio_group(file, ctx, child_node, parent_id, &attribs)?); new_widget_id = Some(parse_component_radio_group(file, ctx, child_node, parent_id, &attribs, tag_name)?);
} }
"" => { /* ignore */ } "" => { /* ignore */ }
other_tag_name => { other_tag_name => {

View File

@@ -5,45 +5,55 @@ use taffy::{
use crate::{ use crate::{
drawing, drawing,
parser::{ parser::{is_percent, parse_color_hex, parse_f32, AttribPair, ParserContext},
AttribPair, is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val,
print_invalid_attrib, print_invalid_value,
},
renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle}, renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle},
widget::util::WLength, widget::util::WLength,
}; };
pub fn parse_round(value: &str, round: &mut WLength, multiplier: f32) { pub fn parse_round(
ctx: &ParserContext<'_>,
tag_name: &str,
key: &str,
value: &str,
round: &mut WLength,
multiplier: f32,
) {
if is_percent(value) { if is_percent(value) {
if let Some(val) = parse_percent(value) { if let Some(val) = ctx.parse_percent(tag_name, key, value) {
*round = WLength::Percent(val); *round = WLength::Percent(val);
} else { } else {
print_invalid_value(value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} else if let Some(val) = parse_f32(value) { } else if let Some(val) = parse_f32(value) {
*round = WLength::Units((val * multiplier).max(0.)); *round = WLength::Units((val * multiplier).max(0.));
} else { } else {
print_invalid_value(value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
pub fn parse_color(value: &str, color: &mut drawing::Color) { pub fn parse_color(ctx: &ParserContext<'_>, tag_name: &str, key: &str, value: &str, color: &mut drawing::Color) {
if let Some(res_color) = parse_color_hex(value) { if let Some(res_color) = parse_color_hex(value) {
*color = res_color; *color = res_color;
} else { } else {
print_invalid_value(value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
pub fn parse_color_opt(value: &str, color: &mut Option<drawing::Color>) { pub fn parse_color_opt(
ctx: &ParserContext<'_>,
tag_name: &str,
key: &str,
value: &str,
color: &mut Option<drawing::Color>,
) {
if let Some(res_color) = parse_color_hex(value) { if let Some(res_color) = parse_color_hex(value) {
*color = Some(res_color); *color = Some(res_color);
} else { } else {
print_invalid_value(value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle { pub fn parse_text_style(ctx: &ParserContext<'_>, attribs: &[AttribPair], tag_name: &str) -> TextStyle {
let mut style = TextStyle::default(); let mut style = TextStyle::default();
for pair in attribs { for pair in attribs {
@@ -61,7 +71,7 @@ pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle {
"justified" => style.align = Some(HorizontalAlign::Justified), "justified" => style.align = Some(HorizontalAlign::Justified),
"end" => style.align = Some(HorizontalAlign::End), "end" => style.align = Some(HorizontalAlign::End),
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"weight" => match value { "weight" => match value {
@@ -69,14 +79,14 @@ pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle {
"normal" => style.weight = Some(FontWeight::Normal), "normal" => style.weight = Some(FontWeight::Normal),
"bold" => style.weight = Some(FontWeight::Bold), "bold" => style.weight = Some(FontWeight::Bold),
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"size" => { "size" => {
if let Ok(size) = value.parse::<f32>() { if let Ok(size) = value.parse::<f32>() {
style.size = Some(size); style.size = Some(size);
} else { } else {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
"shadow" => { "shadow" => {
@@ -88,21 +98,21 @@ pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle {
if let Ok(x) = value.parse::<f32>() { if let Ok(x) = value.parse::<f32>() {
style.shadow.get_or_insert_default().x = x; style.shadow.get_or_insert_default().x = x;
} else { } else {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
"shadow_y" => { "shadow_y" => {
if let Ok(y) = value.parse::<f32>() { if let Ok(y) = value.parse::<f32>() {
style.shadow.get_or_insert_default().y = y; style.shadow.get_or_insert_default().y = y;
} else { } else {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
"wrap" => { "wrap" => {
if let Ok(y) = value.parse::<i32>() { if let Ok(y) = value.parse::<i32>() {
style.wrap = y == 1; style.wrap = y == 1;
} else { } else {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
_ => {} _ => {}
@@ -114,7 +124,7 @@ pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style { pub fn parse_style(ctx: &ParserContext<'_>, attribs: &[AttribPair], tag_name: &str) -> taffy::Style {
let mut style = taffy::Style::default(); let mut style = taffy::Style::default();
for pair in attribs { for pair in attribs {
@@ -126,51 +136,51 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"grid" => style.display = Display::Grid, "grid" => style.display = Display::Grid,
"none" => style.display = Display::None, "none" => style.display = Display::None,
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"margin_left" => { "margin_left" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.margin.left = dim; style.margin.left = dim;
} }
} }
"margin_right" => { "margin_right" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.margin.right = dim; style.margin.right = dim;
} }
} }
"margin_top" => { "margin_top" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.margin.top = dim; style.margin.top = dim;
} }
} }
"margin_bottom" => { "margin_bottom" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.margin.bottom = dim; style.margin.bottom = dim;
} }
} }
"padding_left" => { "padding_left" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.padding.left = dim; style.padding.left = dim;
} }
} }
"padding_right" => { "padding_right" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.padding.right = dim; style.padding.right = dim;
} }
} }
"padding_top" => { "padding_top" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.padding.top = dim; style.padding.top = dim;
} }
} }
"padding_bottom" => { "padding_bottom" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.padding.bottom = dim; style.padding.bottom = dim;
} }
} }
"margin" => { "margin" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.margin.left = dim; style.margin.left = dim;
style.margin.right = dim; style.margin.right = dim;
style.margin.top = dim; style.margin.top = dim;
@@ -178,7 +188,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
} }
} }
"padding" => { "padding" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.padding.left = dim; style.padding.left = dim;
style.padding.right = dim; style.padding.right = dim;
style.padding.top = dim; style.padding.top = dim;
@@ -203,7 +213,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
style.overflow.y = Overflow::Scroll; style.overflow.y = Overflow::Scroll;
} }
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"overflow_x" => match value { "overflow_x" => match value {
@@ -212,7 +222,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"clip" => style.overflow.x = Overflow::Clip, "clip" => style.overflow.x = Overflow::Clip,
"scroll" => style.overflow.x = Overflow::Scroll, "scroll" => style.overflow.x = Overflow::Scroll,
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"overflow_y" => match value { "overflow_y" => match value {
@@ -221,56 +231,56 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"clip" => style.overflow.y = Overflow::Clip, "clip" => style.overflow.y = Overflow::Clip,
"scroll" => style.overflow.y = Overflow::Scroll, "scroll" => style.overflow.y = Overflow::Scroll,
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"min_width" => { "min_width" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.min_size.width = dim; style.min_size.width = dim;
} }
} }
"min_height" => { "min_height" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.min_size.height = dim; style.min_size.height = dim;
} }
} }
"max_width" => { "max_width" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.max_size.width = dim; style.max_size.width = dim;
} }
} }
"max_height" => { "max_height" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.max_size.height = dim; style.max_size.height = dim;
} }
} }
"width" => { "width" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.size.width = dim; style.size.width = dim;
} }
} }
"height" => { "height" => {
if let Some(dim) = parse_size_unit(value) { if let Some(dim) = ctx.parse_size_unit(tag_name, key, value) {
style.size.height = dim; style.size.height = dim;
} }
} }
"gap" => { "gap" => {
if let Some(val) = parse_size_unit(value) { if let Some(val) = ctx.parse_size_unit(tag_name, key, value) {
style.gap = val; style.gap = val;
} }
} }
"flex_basis" => { "flex_basis" => {
if let Some(val) = parse_size_unit(value) { if let Some(val) = ctx.parse_size_unit(tag_name, key, value) {
style.flex_basis = val; style.flex_basis = val;
} }
} }
"flex_grow" => { "flex_grow" => {
if let Some(val) = parse_val(value) { if let Some(val) = ctx.parse_val(tag_name, key, value) {
style.flex_grow = val; style.flex_grow = val;
} }
} }
"flex_shrink" => { "flex_shrink" => {
if let Some(val) = parse_val(value) { if let Some(val) = ctx.parse_val(tag_name, key, value) {
style.flex_shrink = val; style.flex_shrink = val;
} }
} }
@@ -278,14 +288,14 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"absolute" => style.position = taffy::Position::Absolute, "absolute" => style.position = taffy::Position::Absolute,
"relative" => style.position = taffy::Position::Relative, "relative" => style.position = taffy::Position::Relative,
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"box_sizing" => match value { "box_sizing" => match value {
"border_box" => style.box_sizing = BoxSizing::BorderBox, "border_box" => style.box_sizing = BoxSizing::BorderBox,
"content_box" => style.box_sizing = BoxSizing::ContentBox, "content_box" => style.box_sizing = BoxSizing::ContentBox,
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"align_self" => match value { "align_self" => match value {
@@ -297,7 +307,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"start" => style.align_self = Some(AlignSelf::Start), "start" => style.align_self = Some(AlignSelf::Start),
"stretch" => style.align_self = Some(AlignSelf::Stretch), "stretch" => style.align_self = Some(AlignSelf::Stretch),
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"justify_self" => match value { "justify_self" => match value {
@@ -308,7 +318,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"start" => style.justify_self = Some(JustifySelf::Start), "start" => style.justify_self = Some(JustifySelf::Start),
"stretch" => style.justify_self = Some(JustifySelf::Stretch), "stretch" => style.justify_self = Some(JustifySelf::Stretch),
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"align_items" => match value { "align_items" => match value {
@@ -320,7 +330,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"start" => style.align_items = Some(AlignItems::Start), "start" => style.align_items = Some(AlignItems::Start),
"stretch" => style.align_items = Some(AlignItems::Stretch), "stretch" => style.align_items = Some(AlignItems::Stretch),
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"align_content" => match value { "align_content" => match value {
@@ -334,7 +344,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"start" => style.align_content = Some(AlignContent::Start), "start" => style.align_content = Some(AlignContent::Start),
"stretch" => style.align_content = Some(AlignContent::Stretch), "stretch" => style.align_content = Some(AlignContent::Stretch),
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"justify_content" => match value { "justify_content" => match value {
@@ -348,7 +358,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"start" => style.justify_content = Some(JustifyContent::Start), "start" => style.justify_content = Some(JustifyContent::Start),
"stretch" => style.justify_content = Some(JustifyContent::Stretch), "stretch" => style.justify_content = Some(JustifyContent::Stretch),
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
"flex_wrap" => match value { "flex_wrap" => match value {
@@ -363,7 +373,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
"row_reverse" => style.flex_direction = FlexDirection::RowReverse, "row_reverse" => style.flex_direction = FlexDirection::RowReverse,
"row" => style.flex_direction = FlexDirection::Row, "row" => style.flex_direction = FlexDirection::Row,
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
}, },
_ => {} _ => {}

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
layout::WidgetID, layout::WidgetID,
parser::{AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, style::parse_style}, parser::{parse_children, parse_widget_universal, style::parse_style, AttribPair, ParserContext, ParserFile},
widget::div::WidgetDiv, widget::div::WidgetDiv,
}; };
@@ -10,12 +10,13 @@ pub fn parse_widget_div<'a>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
tag_name: &str,
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let style = parse_style(attribs); let style = parse_style(ctx, attribs, tag_name);
let (widget, _) = ctx.layout.add_child(parent_id, WidgetDiv::create(), style)?; let (widget, _) = ctx.layout.add_child(parent_id, WidgetDiv::create(), style)?;
parse_widget_universal(ctx, &widget, attribs); parse_widget_universal(ctx, &widget, attribs, tag_name);
parse_children(file, ctx, node, widget.id)?; parse_children(file, ctx, node, widget.id)?;
Ok(widget.id) Ok(widget.id)

View File

@@ -2,8 +2,9 @@ use crate::{
assets::AssetPath, assets::AssetPath,
layout::WidgetID, layout::WidgetID,
parser::{ parser::{
AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, print_invalid_attrib, parse_children, parse_widget_universal,
style::{parse_color, parse_round, parse_style}, style::{parse_color, parse_round, parse_style},
AttribPair, ParserContext, ParserFile,
}, },
renderer_vk::text::custom_glyph::CustomGlyphData, renderer_vk::text::custom_glyph::CustomGlyphData,
widget::image::{WidgetImage, WidgetImageParams}, widget::image::{WidgetImage, WidgetImageParams},
@@ -15,9 +16,10 @@ pub fn parse_widget_image<'a>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
tag_name: &str,
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut params = WidgetImageParams::default(); let mut params = WidgetImageParams::default();
let style = parse_style(attribs); let style = parse_style(ctx, attribs, tag_name);
let mut glyph = None; let mut glyph = None;
for pair in attribs { for pair in attribs {
@@ -44,6 +46,9 @@ pub fn parse_widget_image<'a>(
} }
"round" => { "round" => {
parse_round( parse_round(
ctx,
tag_name,
key,
value, value,
&mut params.round, &mut params.round,
ctx.doc_params.globals.get().defaults.rounding_mult, ctx.doc_params.globals.get().defaults.rounding_mult,
@@ -51,12 +56,12 @@ pub fn parse_widget_image<'a>(
} }
"border" => { "border" => {
params.border = value.parse().unwrap_or_else(|_| { params.border = value.parse().unwrap_or_else(|_| {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
0.0 0.0
}); });
} }
"border_color" => { "border_color" => {
parse_color(value, &mut params.border_color); parse_color(ctx, tag_name, key, value, &mut params.border_color);
} }
_ => {} _ => {}
} }
@@ -65,12 +70,12 @@ pub fn parse_widget_image<'a>(
if let Some(glyph) = glyph { if let Some(glyph) = glyph {
params.glyph_data = Some(glyph); params.glyph_data = Some(glyph);
} else { } else {
log::warn!("No source for image node!"); ctx.print_missing_attrib(tag_name, "src");
} }
let (widget, _) = ctx.layout.add_child(parent_id, WidgetImage::create(params), style)?; let (widget, _) = ctx.layout.add_child(parent_id, WidgetImage::create(params), style)?;
parse_widget_universal(ctx, &widget, attribs); parse_widget_universal(ctx, &widget, attribs, tag_name);
parse_children(file, ctx, node, widget.id)?; parse_children(file, ctx, node, widget.id)?;
Ok(widget.id) Ok(widget.id)

View File

@@ -2,8 +2,9 @@ use crate::{
i18n::Translation, i18n::Translation,
layout::WidgetID, layout::WidgetID,
parser::{ parser::{
AttribPair, ParserContext, ParserFile, parse_children, parse_i32, parse_widget_universal, print_invalid_attrib, parse_children, parse_i32, parse_widget_universal,
style::{parse_style, parse_text_style}, style::{parse_style, parse_text_style},
AttribPair, ParserContext, ParserFile,
}, },
widget::label::{WidgetLabel, WidgetLabelParams}, widget::label::{WidgetLabel, WidgetLabelParams},
}; };
@@ -14,11 +15,12 @@ pub fn parse_widget_label<'a>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
tag_name: &str,
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut params = WidgetLabelParams::default(); let mut params = WidgetLabelParams::default();
let style = parse_style(attribs); let style = parse_style(ctx, attribs, tag_name);
params.style = parse_text_style(attribs); params.style = parse_text_style(ctx, attribs, tag_name);
for pair in attribs { for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
@@ -27,7 +29,7 @@ pub fn parse_widget_label<'a>(
if let Some(num) = parse_i32(value) { if let Some(num) = parse_i32(value) {
params.style.wrap = num == 1; params.style.wrap = num == 1;
} else { } else {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
"text" => { "text" => {
@@ -50,7 +52,7 @@ pub fn parse_widget_label<'a>(
.layout .layout
.add_child(parent_id, WidgetLabel::create(&mut globals.get(), params), style)?; .add_child(parent_id, WidgetLabel::create(&mut globals.get(), params), style)?;
parse_widget_universal(ctx, &widget, attribs); parse_widget_universal(ctx, &widget, attribs, tag_name);
parse_children(file, ctx, node, widget.id)?; parse_children(file, ctx, node, widget.id)?;
Ok(widget.id) Ok(widget.id)

View File

@@ -2,8 +2,9 @@ use crate::{
drawing::GradientMode, drawing::GradientMode,
layout::WidgetID, layout::WidgetID,
parser::{ parser::{
AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, print_invalid_attrib, parse_children, parse_widget_universal,
style::{parse_color, parse_round, parse_style}, style::{parse_color, parse_round, parse_style},
AttribPair, ParserContext, ParserFile,
}, },
widget::rectangle::{WidgetRectangle, WidgetRectangleParams}, widget::rectangle::{WidgetRectangle, WidgetRectangleParams},
}; };
@@ -14,18 +15,19 @@ pub fn parse_widget_rectangle<'a>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
tag_name: &str,
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut params = WidgetRectangleParams::default(); let mut params = WidgetRectangleParams::default();
let style = parse_style(attribs); let style = parse_style(ctx, attribs, tag_name);
for pair in attribs { for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
match key { match key {
"color" => { "color" => {
parse_color(value, &mut params.color); parse_color(ctx, tag_name, key, value, &mut params.color);
} }
"color2" => { "color2" => {
parse_color(value, &mut params.color2); parse_color(ctx, tag_name, key, value, &mut params.color2);
} }
"gradient" => { "gradient" => {
params.gradient = match value { params.gradient = match value {
@@ -34,13 +36,16 @@ pub fn parse_widget_rectangle<'a>(
"radial" => GradientMode::Radial, "radial" => GradientMode::Radial,
"none" => GradientMode::None, "none" => GradientMode::None,
_ => { _ => {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
GradientMode::None GradientMode::None
} }
} }
} }
"round" => { "round" => {
parse_round( parse_round(
ctx,
tag_name,
key,
value, value,
&mut params.round, &mut params.round,
ctx.doc_params.globals.get().defaults.rounding_mult, ctx.doc_params.globals.get().defaults.rounding_mult,
@@ -48,12 +53,12 @@ pub fn parse_widget_rectangle<'a>(
} }
"border" => { "border" => {
params.border = value.parse().unwrap_or_else(|_| { params.border = value.parse().unwrap_or_else(|_| {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
0.0 0.0
}); });
} }
"border_color" => { "border_color" => {
parse_color(value, &mut params.border_color); parse_color(ctx, tag_name, key, value, &mut params.border_color);
} }
_ => {} _ => {}
} }
@@ -63,7 +68,7 @@ pub fn parse_widget_rectangle<'a>(
.layout .layout
.add_child(parent_id, WidgetRectangle::create(params), style)?; .add_child(parent_id, WidgetRectangle::create(params), style)?;
parse_widget_universal(ctx, &widget, attribs); parse_widget_universal(ctx, &widget, attribs, tag_name);
parse_children(file, ctx, node, widget.id)?; parse_children(file, ctx, node, widget.id)?;
Ok(widget.id) Ok(widget.id)

View File

@@ -1,12 +1,12 @@
use crate::{ use crate::{
assets::AssetPath, assets::AssetPath,
layout::WidgetID, layout::WidgetID,
parser::{AttribPair, ParserContext, ParserFile, 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::CustomGlyphData, renderer_vk::text::custom_glyph::CustomGlyphData,
widget::sprite::{WidgetSprite, WidgetSpriteParams}, widget::sprite::{WidgetSprite, WidgetSpriteParams},
}; };
use super::{parse_color_hex, print_invalid_attrib}; use super::parse_color_hex;
pub fn parse_widget_sprite<'a>( pub fn parse_widget_sprite<'a>(
file: &'a ParserFile, file: &'a ParserFile,
@@ -14,9 +14,10 @@ pub fn parse_widget_sprite<'a>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
tag_name: &str,
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut params = WidgetSpriteParams::default(); let mut params = WidgetSpriteParams::default();
let style = parse_style(attribs); let style = parse_style(ctx, attribs, tag_name);
let mut glyph = None; let mut glyph = None;
for pair in attribs { for pair in attribs {
@@ -45,7 +46,7 @@ pub fn parse_widget_sprite<'a>(
if let Some(color) = parse_color_hex(value) { if let Some(color) = parse_color_hex(value) {
params.color = Some(color); params.color = Some(color);
} else { } else {
print_invalid_attrib(key, value); ctx.print_invalid_attrib(tag_name, key, value);
} }
} }
_ => {} _ => {}
@@ -55,12 +56,12 @@ pub fn parse_widget_sprite<'a>(
if let Some(glyph) = glyph { if let Some(glyph) = glyph {
params.glyph_data = Some(glyph); params.glyph_data = Some(glyph);
} else { } else {
log::warn!("No source for sprite node!"); ctx.print_missing_attrib(tag_name, "src");
} }
let (widget, _) = ctx.layout.add_child(parent_id, WidgetSprite::create(params), style)?; let (widget, _) = ctx.layout.add_child(parent_id, WidgetSprite::create(params), style)?;
parse_widget_universal(ctx, &widget, attribs); parse_widget_universal(ctx, &widget, attribs, tag_name);
parse_children(file, ctx, node, widget.id)?; parse_children(file, ctx, node, widget.id)?;
Ok(widget.id) Ok(widget.id)

View File

@@ -13,12 +13,7 @@ use crate::{
i18n::Translation, i18n::Translation,
layout::{Layout, LayoutTask, LayoutTasks, WidgetPair}, layout::{Layout, LayoutTask, LayoutTasks, WidgetPair},
parser::{self, Fetchable, ParserState}, parser::{self, Fetchable, ParserState},
widget::{ widget::{div::WidgetDiv, label::WidgetLabel, rectangle::WidgetRectangle, EventResult},
div::WidgetDiv,
label::WidgetLabel,
rectangle::{WidgetRectangle, WidgetRectangleParams},
EventResult,
},
}; };
struct OpenedWindow { struct OpenedWindow {

View File

@@ -108,7 +108,7 @@
<!-- An app with a single icon. --> <!-- An app with a single icon. -->
<template name="App"> <template name="App">
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _context_name="${name}" _press="::ContextMenuOpen menu_app"> <Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _context_name="${name}" _press="::ContextMenuOpen menu_app">
<sprite width="38" height="38" color="~text_color" src_ext="${icon}" /> <sprite width="38" height="38" color="~color_text" src_ext="${icon}" />
</Button> </Button>
</template> </template>
@@ -123,7 +123,7 @@
<!-- A screen with a shortened connector name, e.g. "H1" for HDMI-A-1 or "D2" for DP-2 --> <!-- A screen with a shortened connector name, e.g. "H1" for HDMI-A-1 or "D2" for DP-2 -->
<template name="Screen"> <template name="Screen">
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _context_name="${name}" _press="::ContextMenuOpen menu_screen"> <Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _context_name="${name}" _press="::ContextMenuOpen menu_screen">
<sprite width="38" height="38" color="~text_color" src_builtin="edit/screen.svg" /> <sprite width="38" height="38" color="~color_text" src_builtin="edit/screen.svg" />
<div position="absolute" margin_top="-7" margin_left="-1"> <div position="absolute" margin_top="-7" margin_left="-1">
<label text="${display}" size="18" color="~color_faded_20" weight="bold" /> <label text="${display}" size="18" color="~color_faded_20" weight="bold" />
</div> </div>
@@ -141,7 +141,7 @@
<template name="Panel"> <template name="Panel">
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _context_name="${name}" _press="::ContextMenuOpen menu_panel"> <Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _context_name="${name}" _press="::ContextMenuOpen menu_panel">
<sprite width="38" height="38" color="~text_color" src_builtin="edit/panel.svg" /> <sprite width="38" height="38" color="~color_text" src_builtin="edit/panel.svg" />
</Button> </Button>
</template> </template>
@@ -156,7 +156,7 @@
<template name="Mirror"> <template name="Mirror">
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _context_name="${name}" _press="::ContextMenuOpen menu_mirror"> <Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _context_name="${name}" _press="::ContextMenuOpen menu_mirror">
<sprite width="38" height="38" color="~text_color" src_builtin="edit/mirror.svg" /> <sprite width="38" height="38" color="~color_text" src_builtin="edit/mirror.svg" />
<div position="absolute" margin_top="5" margin_left="13"> <div position="absolute" margin_top="5" margin_left="13">
<label text="${display}" size="20" color="~color_faded_20" weight="bold" /> <label text="${display}" size="20" color="~color_faded_20" weight="bold" />
</div> </div>
@@ -174,7 +174,7 @@
<template name="Set"> <template name="Set">
<Button macro="button_style" id="set_${idx}" _press="::SetSwitch ${idx}" tooltip="WATCH.SWITCH_TO_SET" tooltip_side="bottom"> <Button macro="button_style" id="set_${idx}" _press="::SetSwitch ${idx}" tooltip="WATCH.SWITCH_TO_SET" tooltip_side="bottom">
<sprite width="38" height="38" color="~text_color" src_builtin="watch/set2.svg" /> <sprite width="38" height="38" color="~color_text" src_builtin="watch/set2.svg" />
<div position="absolute" margin_top="10" margin_left="-7"> <div position="absolute" margin_top="10" margin_left="-7">
<label text="${display}" size="20" color="~color_faded_20" weight="bold" /> <label text="${display}" size="20" color="~color_faded_20" weight="bold" />
</div> </div>
@@ -186,32 +186,23 @@
<rectangle macro="bg_rect" padding="10" align_items="center" justify_content="space_between"> <rectangle macro="bg_rect" padding="10" align_items="center" justify_content="space_between">
<div gap="10"> <div gap="10">
<Button macro="button_style" id="btn_dashboard" _press="::DashToggle"> <Button macro="button_style" id="btn_dashboard" _press="::DashToggle">
<sprite width="38" height="38" color="~text_color" src="watch/wayvr_dashboard_mono.svg" /> <sprite width="38" height="38" color="~color_text" src="watch/wayvr_dashboard_mono.svg" />
</Button> </Button>
<VerticalSeparator /> <VerticalSeparator />
<div id="panels_root" gap="6"> <div id="panels_root" gap="6">
<Screen idx="0" display="H1" name="HDMI-A-1" />
<Screen idx="1" display="D2" name="Screen: DP-2" />
<Mirror idx="1" display="1" name="M1" />
<Panel idx="1" display="Test" name="Test" />
</div> </div>
<VerticalSeparator /> <VerticalSeparator />
<div id="apps_root" gap="6"> <div id="apps_root" gap="6">
<App id="test1" name="Blender" icon="/usr/share/icons/hicolor/scalable/apps/blender-5.0.svg" />
<App id="test2" name="Inkscape" icon="/usr/share/icons/hicolor/scalable/apps/org.inkscape.Inkscape.svg" />
<App id="test3" name="GIMP" icon="/usr/share/icons/hicolor/scalable/apps/gimp.svg" />
</div> </div>
</div> </div>
<div id="tray_root" flex_direction="row" gap="10"> <div id="tray_root" flex_direction="row" gap="10">
<Button macro="button_style" _press="::ContextMenuOpen menu_burger"> <Button macro="button_style" _press="::ContextMenuOpen menu_burger">
<sprite width="38" height="38" color="~text_color" src_builtin="keyboard/burger.svg" /> <sprite width="38" height="38" color="~color_text" src_builtin="keyboard/burger.svg" />
</Button> </Button>
<VerticalSeparator /> <VerticalSeparator />
<div id="sets_root" flex_direction="row" gap="6"> <div id="sets_root" flex_direction="row" gap="6">
<Set idx="0" display="1" />
<Set idx="1" display="2" />
</div> </div>
<VerticalSeparator /> <VerticalSeparator />

View File

@@ -40,7 +40,7 @@
<template name="Screen"> <template name="Screen">
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::OverlayToggle ${name}"> <Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::OverlayToggle ${name}">
<sprite width="38" height="38" color="~text_color" src_builtin="edit/screen.svg" /> <sprite width="38" height="38" color="~color_text" src_builtin="edit/screen.svg" />
<div position="absolute" margin_top="-7" margin_left="-1"> <div position="absolute" margin_top="-7" margin_left="-1">
<label text="${display}" size="18" color="~color_faded_20" weight="bold" /> <label text="${display}" size="18" color="~color_faded_20" weight="bold" />
</div> </div>
@@ -48,12 +48,12 @@
</template> </template>
<template name="Panel"> <template name="Panel">
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::OverlayToggle ${name}"> <Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::OverlayToggle ${name}">
<sprite width="38" height="38" color="~text_color" src_builtin="edit/panel.svg" /> <sprite width="38" height="38" color="~color_text" src_builtin="edit/panel.svg" />
</Button> </Button>
</template> </template>
<template name="Mirror"> <template name="Mirror">
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::OverlayToggle ${name}"> <Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::OverlayToggle ${name}">
<sprite width="38" height="38" color="~text_color" src_builtin="edit/mirror.svg" /> <sprite width="38" height="38" color="~color_text" src_builtin="edit/mirror.svg" />
<div position="absolute" margin_top="5" margin_left="13"> <div position="absolute" margin_top="5" margin_left="13">
<label text="${display}" size="20" color="~color_faded_20" weight="bold" /> <label text="${display}" size="20" color="~color_faded_20" weight="bold" />
</div> </div>
@@ -61,13 +61,13 @@
</template> </template>
<template name="App"> <template name="App">
<Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::OverlayToggle ${name}"> <Button macro="button_style" id="overlay_${idx}" tooltip_str="${name}" _press="::OverlayToggle ${name}">
<sprite width="38" height="38" color="~text_color" src_ext="${icon}" /> <sprite width="38" height="38" color="~color_text" src_ext="${icon}" />
</Button> </Button>
</template> </template>
<template name="Set"> <template name="Set">
<Button macro="button_style" id="set_${idx}" _press="::SetToggle ${idx}" tooltip="WATCH.SWITCH_TO_SET" tooltip_side="top"> <Button macro="button_style" id="set_${idx}" _press="::SetToggle ${idx}" tooltip="WATCH.SWITCH_TO_SET" tooltip_side="top">
<sprite width="40" height="40" color="~text_color" src_builtin="watch/set2.svg" /> <sprite width="40" height="40" color="~color_text" src_builtin="watch/set2.svg" />
<div position="absolute" margin_top="9"> <div position="absolute" margin_top="9">
<label text="${display}" size="24" color="#00050F" weight="bold" /> <label text="${display}" size="24" color="#00050F" weight="bold" />
</div> </div>
@@ -77,10 +77,10 @@
<template name="Clock"> <template name="Clock">
<div gap="12" flex_direction="column" padding="4"> <div gap="12" flex_direction="column" padding="4">
<div flex_direction="column" > <div flex_direction="column" >
<label text="11:22 PM" _source="clock" _display="time" color="~text_color" size="~clock0_size" weight="bold" align="center" /> <label text="11:22 PM" _source="clock" _display="time" color="~color_text" size="~clock0_size" weight="bold" align="center" />
<div padding_left="2" gap="16" flex_direction="row" justify_content="center"> <div padding_left="2" gap="16" flex_direction="row" justify_content="center">
<label text="Tue" _source="clock" _display="dow_short" color="~clock0_color" size="~clock0_dow_size" weight="bold" /> <label text="Tue" _source="clock" _display="dow_short" color="~color_text" size="~clock0_dow_size" weight="bold" />
<label text="22/2/2022" _source="clock" _display="date" color="~clock0_color" size="~clock0_date_size" weight="bold" /> <label text="22/2/2022" _source="clock" _display="date" color="~color_text" size="~clock0_date_size" weight="bold" />
</div> </div>
</div> </div>
<div flex_direction="row" gap="8" justify_content="space_around"> <div flex_direction="row" gap="8" justify_content="space_around">
@@ -131,15 +131,15 @@
<sprite src_builtin="watch/keyboard.svg" width="40" height="40" /> <sprite src_builtin="watch/keyboard.svg" width="40" height="40" />
</Button> </Button>
<Button id="btn_edit_mode" macro="button_style" _press="::EditToggle" tooltip="WATCH.EDIT_MODE" tooltip_side="left"> <Button id="btn_edit_mode" macro="button_style" _press="::EditToggle" tooltip="WATCH.EDIT_MODE" tooltip_side="left">
<sprite color="~text_color" width="40" height="40" src="watch/edit.svg" /> <sprite color="~color_text" width="40" height="40" src="watch/edit.svg" />
</Button> </Button>
</div> </div>
<div gap="8"> <div gap="8">
<Button macro="button_style" _press="::PlayspaceRecenter" tooltip="WATCH.RECENTER" tooltip_side="left"> <Button macro="button_style" _press="::PlayspaceRecenter" tooltip="WATCH.RECENTER" tooltip_side="left">
<sprite width="40" height="40" color="~text_color" src="watch/recenter.svg" /> <sprite width="40" height="40" color="~color_text" src="watch/recenter.svg" />
</Button> </Button>
<Button macro="button_style" _press="::PlayspaceFixFloor" tooltip="WATCH.FIX_FLOOR" tooltip_side="left"> <Button macro="button_style" _press="::PlayspaceFixFloor" tooltip="WATCH.FIX_FLOOR" tooltip_side="left">
<sprite width="40" height="40" color="~text_color" src="watch/fix-floor.svg" /> <sprite width="40" height="40" color="~color_text" src="watch/fix-floor.svg" />
</Button> </Button>
</div> </div>
</div> </div>
@@ -149,7 +149,7 @@
<div flex_direction="row" gap="8"> <div flex_direction="row" gap="8">
<div gap="4"> <div gap="4">
<Button id="btn_dashboard" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top"> <Button id="btn_dashboard" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top">
<sprite color="~text_color" width="40" height="40" src="watch/wayvr_dashboard_mono.svg" /> <sprite color="~color_text" width="40" height="40" src="watch/wayvr_dashboard_mono.svg" />
</Button> </Button>
</div> </div>
<VerticalSeparator /> <VerticalSeparator />
@@ -162,8 +162,8 @@
<div id="panels_root" gap="4" display="none"> <div id="panels_root" gap="4" display="none">
<!-- Will populate tags at runtime --> <!-- Will populate tags at runtime -->
<!-- These are examples for uidev --> <!-- These are examples for uidev -->
<Screen idx="0" display="H1" /> <Screen idx="0" display="H1" name="HDMI-A-1" />
<Screen idx="1" display="D2" /> <Screen idx="1" display="D2" name="DP-2"/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -30,6 +30,7 @@ use crate::{
task::{OverlayTask, PlayspaceTask, TaskType, ToggleMode}, task::{OverlayTask, PlayspaceTask, TaskType, ToggleMode},
wayvr::process::KillSignal, wayvr::process::KillSignal,
}, },
gui::panel::{log_cmd_invalid_arg, log_cmd_missing_arg},
overlays::{custom::create_custom, toast::Toast, wayvr::WvrCommand}, overlays::{custom::create_custom, toast::Toast, wayvr::WvrCommand},
state::AppState, state::AppState,
subsystem::hid::VirtualKey, subsystem::hid::VirtualKey,
@@ -192,6 +193,8 @@ pub(super) fn setup_custom_button<S: 'static>(
on_custom_attribs: &parser::OnCustomAttribsFunc, on_custom_attribs: &parser::OnCustomAttribsFunc,
button: Rc<ComponentButton>, button: Rc<ComponentButton>,
) { ) {
const TAG: &str = "Button";
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS { for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
let Some(action) = attribs.get_value(name) else { let Some(action) = attribs.get_value(name) else {
continue; continue;
@@ -207,10 +210,7 @@ pub(super) fn setup_custom_button<S: 'static>(
let callback: EventCallback<AppState, S> = match command { let callback: EventCallback<AppState, S> = match command {
"::ContextMenuOpen" => { "::ContextMenuOpen" => {
let Some(template_name) = args.next() else { let Some(template_name) = args.next() else {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has invalid arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
@@ -252,18 +252,13 @@ pub(super) fn setup_custom_button<S: 'static>(
} }
"::ElementSetDisplay" => { "::ElementSetDisplay" => {
let (Some(id), Some(value)) = (args.next(), args.next()) else { let (Some(id), Some(value)) = (args.next(), args.next()) else {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has invalid arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
let Ok(widget_id) = parser_state.data.get_widget_id(id) else { let Ok(widget_id) = parser_state.data.get_widget_id(id) else {
log::warn!( let msg = format!("no element with ID \"{id}\"");
"{:?}: {command}: no element exists with ID '{id}'", log_cmd_invalid_arg(parser_state, TAG, name, command, &msg);
parser_state.path.get_path_buf()
);
return; return;
}; };
@@ -273,7 +268,8 @@ pub(super) fn setup_custom_button<S: 'static>(
"block" => taffy::Display::Block, "block" => taffy::Display::Block,
"grid" => taffy::Display::Grid, "grid" => taffy::Display::Grid,
_ => { _ => {
log::warn!("{command} has invalid display argument: '{value}'"); let msg = format!("unexpected \"{value}\"");
log_cmd_invalid_arg(parser_state, TAG, name, command, &msg);
return; return;
} }
}; };
@@ -297,10 +293,8 @@ pub(super) fn setup_custom_button<S: 'static>(
"::SetToggle" => { "::SetToggle" => {
let arg = args.next().unwrap_or_default(); let arg = args.next().unwrap_or_default();
let Ok(set_idx) = arg.parse() else { let Ok(set_idx) = arg.parse() else {
log::error!( let msg = format!("expected integer, found \"{arg}\"");
"{:?}: {command} has invalid argument: \"{arg}\"", log_cmd_invalid_arg(parser_state, TAG, name, command, &msg);
parser_state.path.get_path_buf()
);
return; return;
}; };
Box::new(move |_common, data, app, _| { Box::new(move |_common, data, app, _| {
@@ -316,10 +310,8 @@ pub(super) fn setup_custom_button<S: 'static>(
"::SetSwitch" => { "::SetSwitch" => {
let arg = args.next().unwrap_or_default(); let arg = args.next().unwrap_or_default();
let Ok(set_idx) = arg.parse::<i32>() else { let Ok(set_idx) = arg.parse::<i32>() else {
log::error!( let msg = format!("expected integer, found \"{arg}\"");
"{:?}: {command} has invalid argument: \"{arg}\"", log_cmd_invalid_arg(parser_state, TAG, name, command, &msg);
parser_state.path.get_path_buf()
);
return; return;
}; };
let maybe_set = if set_idx < 0 { let maybe_set = if set_idx < 0 {
@@ -340,10 +332,7 @@ pub(super) fn setup_custom_button<S: 'static>(
"::OverlayReset" => { "::OverlayReset" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into(); let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 { if arg.len() < 1 {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
@@ -362,10 +351,7 @@ pub(super) fn setup_custom_button<S: 'static>(
"::OverlayToggle" => { "::OverlayToggle" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into(); let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 { if arg.len() < 1 {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
@@ -385,10 +371,7 @@ pub(super) fn setup_custom_button<S: 'static>(
"::OverlayDrop" => { "::OverlayDrop" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into(); let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 { if arg.len() < 1 {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
@@ -424,10 +407,7 @@ pub(super) fn setup_custom_button<S: 'static>(
"::CustomOverlayReload" => { "::CustomOverlayReload" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into(); let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 { if arg.len() < 1 {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
@@ -465,10 +445,7 @@ pub(super) fn setup_custom_button<S: 'static>(
"::WvrOverlayCloseWindow" => { "::WvrOverlayCloseWindow" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into(); let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 { if arg.len() < 1 {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
Box::new(move |_common, data, app, _| { Box::new(move |_common, data, app, _| {
@@ -491,10 +468,7 @@ pub(super) fn setup_custom_button<S: 'static>(
"::WvrOverlayKillProcess" | "::WvrOverlayTermProcess" => { "::WvrOverlayKillProcess" | "::WvrOverlayTermProcess" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into(); let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 { if arg.len() < 1 {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
@@ -615,24 +589,28 @@ pub(super) fn setup_custom_button<S: 'static>(
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}), }),
"::SendKey" => { "::SendKey" => {
let Some(key) = args.next().and_then(|s| VirtualKey::from_str(s).ok()) else { let Some(arg) = args.next() else {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has bad/missing arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
let Some(down) = args.next().and_then(|s| match s.to_lowercase().as_str() { let Ok(key) = VirtualKey::from_str(arg) else {
"down" => Some(true), let msg = format!("expected VirtualKey, found \"{arg}\"");
"up" => Some(false), log_cmd_invalid_arg(parser_state, TAG, name, command, &msg);
_ => None,
}) else {
log::error!(
"{:?}: {command} has bad/missing arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
let Some(arg) = args.next() else {
log_cmd_missing_arg(parser_state, TAG, name, command);
return;
};
let down = match arg.to_lowercase().as_str() {
"down" => true,
"up" => false,
_ => {
let msg = format!("expected \"down\" or \"up\", found \"{arg}\"");
log_cmd_invalid_arg(parser_state, TAG, name, command, &msg);
return;
}
};
Box::new(move |_common, data, app, _| { Box::new(move |_common, data, app, _| {
if !test_button(data) || !test_duration(&button, app) { if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass); return Ok(EventResult::Pass);
@@ -677,18 +655,17 @@ pub(super) fn setup_custom_button<S: 'static>(
use crate::subsystem::osc::parse_osc_value; use crate::subsystem::osc::parse_osc_value;
let Some(address) = args.next().map(std::string::ToString::to_string) else { let Some(address) = args.next().map(std::string::ToString::to_string) else {
log::error!( log_cmd_missing_arg(parser_state, TAG, name, command);
"{:?}: {command} has bad/missing arguments",
parser_state.path.get_path_buf()
);
return; return;
}; };
let mut osc_args = vec![]; let mut osc_args = vec![];
for arg in args { for arg in args {
let Ok(osc_arg) = parse_osc_value(arg) let Ok(osc_arg) = parse_osc_value(arg)
.inspect_err(|e| log::error!("Could not parse OSC value '{arg}': {e:?}")) .inspect_err(|e| log::warn!("Could not parse OSC value '{arg}': {e:?}"))
else { else {
let msg = format!("expected OscValue, found \"{arg}\"");
log_cmd_invalid_arg(parser_state, TAG, name, command, &msg);
return; return;
}; };
osc_args.push(osc_arg); osc_args.push(osc_arg);

View File

@@ -46,8 +46,6 @@ impl DeviceList {
let template = device.role.as_ref(); let template = device.role.as_ref();
log::warn!("creating {template} tag for {i}");
params.insert("idx".into(), i.to_string().into()); params.insert("idx".into(), i.to_string().into());
parser_state.instantiate_template( parser_state.instantiate_template(
&doc_params, &doc_params,

View File

@@ -7,30 +7,38 @@ use wgui::{
event::{self, EventCallback}, event::{self, EventCallback},
i18n::Translation, i18n::Translation,
layout::Layout, layout::Layout,
parser::{CustomAttribsInfoOwned, parse_color_hex}, parser::{CustomAttribsInfoOwned, ParserState, parse_color_hex},
widget::{EventResult, label::WidgetLabel}, widget::{EventResult, label::WidgetLabel},
}; };
use crate::state::AppState; use crate::{
gui::panel::{log_invalid_attrib, log_missing_attrib},
state::AppState,
};
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub(super) fn setup_custom_label<S: 'static>( pub(super) fn setup_custom_label<S: 'static>(
layout: &mut Layout, layout: &mut Layout,
parser_state: &ParserState,
attribs: &CustomAttribsInfoOwned, attribs: &CustomAttribsInfoOwned,
app: &AppState, app: &AppState,
) { ) {
const TAG: &str = "label";
let Some(source) = attribs.get_value("_source") else { let Some(source) = attribs.get_value("_source") else {
log::warn!("custom label with no source!"); log_missing_attrib(parser_state, TAG, "_source");
return; return;
}; };
let callback: EventCallback<AppState, S> = match source { let callback: EventCallback<AppState, S> = match source {
"battery" => { "battery" => {
let Some(device) = attribs let Some(device) = attribs.get_value("_device") else {
.get_value("_device") log_missing_attrib(parser_state, TAG, "_device");
.and_then(|s| s.parse::<usize>().ok()) return;
else { };
log::warn!("label with battery source but no device attribute!"); let Ok(device) = device.parse::<usize>() else {
let msg = format!("expected integer, found \"{device}\"");
log_invalid_attrib(parser_state, TAG, "_device", &msg);
return; return;
}; };
@@ -60,17 +68,31 @@ pub(super) fn setup_custom_label<S: 'static>(
} }
"clock" => { "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!"); log_missing_attrib(parser_state, TAG, "_display");
return; return;
}; };
let tz_str = attribs
.get_value("_timezone")
.and_then(|tz| {
tz.parse::<usize>()
.inspect_err(|_| {
let msg = format!("expected integer, found \"{tz}\"");
log_invalid_attrib(parser_state, TAG, "_timezone", &msg);
})
.ok()
})
.and_then(|tz_idx| {
app.session.config.timezones.get(tz_idx).or_else(|| {
let msg = format!("timezone index \"{tz_idx}\" is out of range");
log_invalid_attrib(parser_state, TAG, "_timezone", &msg);
None
})
});
let format = match display { let format = match display {
"name" => { "name" => {
let maybe_pretty_tz = attribs let maybe_pretty_tz = tz_str.and_then(|tz_name| {
.get_value("_timezone")
.and_then(|tz| tz.parse::<usize>().ok())
.and_then(|tz_idx| app.session.config.timezones.get(tz_idx))
.and_then(|tz_name| {
tz_name.split('/').next_back().map(|x| x.replace('_', " ")) tz_name.split('/').next_back().map(|x| x.replace('_', " "))
}); });
@@ -102,11 +124,6 @@ pub(super) fn setup_custom_label<S: 'static>(
} }
}; };
let tz_str = attribs
.get_value("_timezone")
.and_then(|tz| tz.parse::<usize>().ok())
.and_then(|tz_idx| app.session.config.timezones.get(tz_idx));
let state = ClockLabelState { let state = ClockLabelState {
timezone: tz_str.and_then(|tz| { timezone: tz_str.and_then(|tz| {
tz.parse() tz.parse()
@@ -126,7 +143,7 @@ pub(super) fn setup_custom_label<S: 'static>(
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
unk => { unk => {
log::warn!("Unknown source value for label: {unk}"); log_invalid_attrib(parser_state, TAG, "_source", unk);
return; return;
} }
}; };

View File

@@ -1,4 +1,4 @@
use std::{any, cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use button::setup_custom_button; use button::setup_custom_button;
use glam::{Affine2, Vec2, vec2}; use glam::{Affine2, Vec2, vec2};
@@ -192,7 +192,7 @@ impl<S: 'static> GuiPanel<S> {
.get_as::<WidgetLabel>(elem.widget_id) .get_as::<WidgetLabel>(elem.widget_id)
.is_some() .is_some()
{ {
setup_custom_label::<S>(&mut self.layout, elem, app); setup_custom_label::<S>(&mut self.layout, &self.parser_state, elem, app);
} else if let Ok(button) = self } else if let Ok(button) = self
.parser_state .parser_state
.fetch_component_from_widget_id_as::<ComponentButton>(elem.widget_id) .fetch_component_from_widget_id_as::<ComponentButton>(elem.widget_id)
@@ -440,3 +440,37 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
false false
} }
} }
fn log_missing_attrib(parser_state: &ParserState, tag_name: &str, attrib: &str) {
log::warn!(
"{:?}: <{tag_name}> is missing \"{attrib}\"",
parser_state.path.get_path_buf()
)
}
fn log_invalid_attrib(parser_state: &ParserState, tag_name: &str, attrib: &str, value: &str) {
log::warn!(
"{:?}: <{tag_name}> value for \"{attrib}\" is invalid: {value}",
parser_state.path.get_path_buf()
)
}
fn log_cmd_missing_arg(parser_state: &ParserState, tag_name: &str, attrib: &str, command: &str) {
log::warn!(
"{:?}: <{tag_name}> \"{attrib}\": \"{command}\" has missing arguments",
parser_state.path.get_path_buf()
)
}
fn log_cmd_invalid_arg(
parser_state: &ParserState,
tag_name: &str,
attrib: &str,
command: &str,
arg: &str,
) {
log::warn!(
"{:?}: <{tag_name}> \"{attrib}\": \"{command}\" has invalid argument: {arg}",
parser_state.path.get_path_buf()
)
}

View File

@@ -283,6 +283,7 @@ fn logging_init(args: &mut Args) {
.from_env_lossy() .from_env_lossy()
.add_directive("symphonia_core::probe=warn".parse().unwrap()) .add_directive("symphonia_core::probe=warn".parse().unwrap())
.add_directive("zbus=warn".parse().unwrap()) .add_directive("zbus=warn".parse().unwrap())
.add_directive("usvg=error".parse().unwrap())
.add_directive("cosmic_text=warn".parse().unwrap()) .add_directive("cosmic_text=warn".parse().unwrap())
.add_directive("wlx_capture::wayland=info".parse().unwrap()) .add_directive("wlx_capture::wayland=info".parse().unwrap())
.add_directive("smithay=debug".parse().unwrap()), /* GLES render spam */ .add_directive("smithay=debug".parse().unwrap()), /* GLES render spam */