wgui: custom attribs

This commit is contained in:
Aleksander
2025-08-16 22:55:40 +02:00
parent 481db7f23c
commit d54f74ed3c
13 changed files with 158 additions and 75 deletions

View File

@@ -13,7 +13,7 @@ use crate::{
drawing::{self},
event::EventListenerCollection,
globals::WguiGlobals,
layout::{Layout, LayoutState, Widget, WidgetID},
layout::{Layout, LayoutState, Widget, WidgetID, WidgetMap},
parser::{
component_button::parse_component_button, component_checkbox::parse_component_checkbox,
component_slider::parse_component_slider, widget_div::parse_widget_div,
@@ -23,6 +23,7 @@ use crate::{
};
use ouroboros::self_referencing;
use std::{
cell::RefMut,
collections::HashMap,
path::{Path, PathBuf},
rc::Rc,
@@ -619,59 +620,96 @@ fn parse_widget_universal<'a, U1, U2>(
}
}
fn parse_children<'a, U1, U2>(
fn parse_child<'a, U1, U2>(
file: &ParserFile,
ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>,
parent_node: roxmltree::Node<'a, 'a>,
child_node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID,
) -> anyhow::Result<()> {
for child_node in node.children() {
match node.attribute("ignore_in_mode") {
Some("dev") => {
if !ctx.doc_params.extra.dev_mode {
continue;
}
match parent_node.attribute("ignore_in_mode") {
Some("dev") => {
if !ctx.doc_params.extra.dev_mode {
return Ok(()); // do not parse
}
Some("live") => {
if ctx.doc_params.extra.dev_mode {
continue;
}
}
Some(s) => print_invalid_attrib("ignore_in_mode", s),
_ => {}
}
Some("live") => {
if ctx.doc_params.extra.dev_mode {
return Ok(()); // do not parse
}
}
Some(s) => print_invalid_attrib("ignore_in_mode", s),
_ => {}
}
match child_node.tag_name().name() {
"include" => {
parse_tag_include(file, ctx, child_node, parent_id)?;
}
"div" => {
parse_widget_div(file, ctx, child_node, parent_id)?;
}
"rectangle" => {
parse_widget_rectangle(file, ctx, child_node, parent_id)?;
}
"label" => {
parse_widget_label(file, ctx, child_node, parent_id)?;
}
"sprite" => {
parse_widget_sprite(file, ctx, child_node, parent_id)?;
}
"button" => {
parse_component_button(file, ctx, child_node, parent_id)?;
}
"slider" => {
parse_component_slider(file, ctx, child_node, parent_id)?;
}
"check_box" => {
parse_component_checkbox(file, ctx, child_node, parent_id)?;
}
"" => { /* ignore */ }
other_tag_name => {
parse_widget_other(other_tag_name, file, ctx, child_node, parent_id)?;
let mut new_widget_id: Option<WidgetID> = None;
match child_node.tag_name().name() {
"include" => {
parse_tag_include(file, ctx, child_node, parent_id)?;
}
"div" => {
new_widget_id = Some(parse_widget_div(file, ctx, child_node, parent_id)?);
}
"rectangle" => {
new_widget_id = Some(parse_widget_rectangle(file, ctx, child_node, parent_id)?);
}
"label" => {
new_widget_id = Some(parse_widget_label(file, ctx, child_node, parent_id)?);
}
"sprite" => {
new_widget_id = Some(parse_widget_sprite(file, ctx, child_node, parent_id)?);
}
"button" => {
new_widget_id = Some(parse_component_button(file, ctx, child_node, parent_id)?);
}
"slider" => {
new_widget_id = Some(parse_component_slider(file, ctx, child_node, parent_id)?);
}
"check_box" => {
new_widget_id = Some(parse_component_checkbox(file, ctx, child_node, parent_id)?);
}
"" => { /* ignore */ }
other_tag_name => {
parse_widget_other(other_tag_name, file, ctx, child_node, parent_id)?;
}
}
// check for custom attributes (if the callback is set)
if let Some(widget_id) = new_widget_id {
if let Some(on_custom_attrib) = &ctx.doc_params.extra.on_custom_attrib {
for attrib in child_node.attributes() {
let attr_name = attrib.name();
if !attr_name.starts_with('_') || attr_name.is_empty() {
continue;
}
let attr_without_prefix = &attr_name[1..]; // safe
on_custom_attrib(CustomAttribInfo {
widgets: &ctx.layout.state.widgets,
parent_id,
widget_id,
attrib: attr_without_prefix,
value: attrib.value(),
});
}
}
}
Ok(())
}
fn parse_children<'a, U1, U2>(
file: &ParserFile,
ctx: &mut ParserContext<U1, U2>,
parent_node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID,
) -> anyhow::Result<()> {
for child_node in parent_node.children() {
parse_child(file, ctx, parent_node, child_node, parent_id)?;
}
Ok(())
}
@@ -693,11 +731,30 @@ fn create_default_context<'a, U1, U2>(
}
}
pub struct UnusedAttribInfo {}
pub struct CustomAttribInfo<'a> {
pub parent_id: WidgetID,
pub widget_id: WidgetID,
pub widgets: &'a WidgetMap,
pub attrib: &'a str, // without _ at the beginning
pub value: &'a str,
}
// helper functions
impl CustomAttribInfo<'_> {
pub fn get_widget(&self) -> Option<&Widget> {
self.widgets.get(self.widget_id)
}
pub fn get_widget_as<T: 'static>(&self) -> Option<RefMut<T>> {
Some(self.widgets.get(self.widget_id)?.get_as_mut::<T>())
}
}
pub type OnCustomAttribFunc = Box<dyn Fn(CustomAttribInfo)>;
#[derive(Default)]
pub struct ParseDocumentExtra {
//pub on_unused_attrib: Option<Box<dyn Fn(UnusedAttribInfo)>>,
pub on_custom_attrib: Option<OnCustomAttribFunc>, // all attributes with '_' character prepended
pub dev_mode: bool,
}