sliders (wip), fix wlx build

This commit is contained in:
Aleksander
2025-06-28 19:28:05 +02:00
parent 28d58fef59
commit 9d0c0f015c
15 changed files with 323 additions and 60 deletions

View File

@@ -2,13 +2,61 @@
<elements> <elements>
<rectangle <rectangle
color="#AAAAAA" color="#AAAAAA"
width="500" height="500" min_width="500" min_height="500" width="1000" height="500" min_width="1000" min_height="500"
gap="4" flex_direction="column"> gap="4" flex_direction="column"
overflow_y="scroll">
<label text="aaa" color="#FFFFFF" /> <label text="aaa" color="#FFFFFF" />
<button text="Red button" width="128" height="32" color="#FF0000" /> <button text="Red button" width="128" height="32" color="#FF0000" />
<button text="Aqua button" width="128" height="32" color="#00FFFF" /> <button text="Aqua button" width="128" height="32" color="#00FFFF" />
<button text="Yellow button" width="128" height="32" color="#FFFF00" /> <button text="Yellow button" width="128" height="32" color="#FFFF00" />
<div flex_direction="row" gap="16">
<div flex_direction="column" gap="8">
<rectangle width="128" height="2" />
<label text="height 16" />
<rectangle width="128" height="2" />
<label text="range 0-100 value 25" />
<slider width="200" height="16" min_value="0" max_value="100" value="25" />
<label text="range 10-20 value 15" />
<slider width="200" height="16" min_value="10" max_value="20" value="15" />
<label text="range -10-42 value 0" />
<slider width="200" height="16" min_value="-10" max_value="42" value="0" />
</div>
<div flex_direction="column" gap="8">
<rectangle width="128" height="2" />
<label text="height 24" />
<rectangle width="128" height="2" />
<label text="range 0-100 value 25" />
<slider width="200" height="24" min_value="0" max_value="100" value="25" />
<label text="range 10-20 value 15" />
<slider width="200" height="24" min_value="10" max_value="20" value="15" />
<label text="range -10-42 value 0" />
<slider width="200" height="24" min_value="-10" max_value="42" value="0" />
</div>
<div flex_direction="column" gap="8">
<rectangle width="128" height="2" />
<label text="height 32" />
<rectangle width="128" height="2" />
<label text="range 0-100 value 25" />
<slider width="200" height="32" min_value="0" max_value="100" value="25" />
<label text="range 10-20 value 15" />
<slider width="200" height="32" min_value="10" max_value="20" value="15" />
<label text="range -10-42 value 0" />
<slider width="200" height="32" min_value="-10" max_value="42" value="0" />
</div>
</div>
</rectangle> </rectangle>
</elements> </elements>
</layout> </layout>

View File

@@ -2,7 +2,7 @@ use std::sync::Arc;
use taffy::{AlignItems, JustifyContent, prelude::length}; use taffy::{AlignItems, JustifyContent, prelude::length};
use crate::{ use crate::{
animation::{self, Animation, AnimationEasing}, animation::{Animation, AnimationEasing},
components::Component, components::Component,
drawing::{self, Color}, drawing::{self, Color},
event::{EventListenerCollection, EventListenerKind, WidgetCallback}, event::{EventListenerCollection, EventListenerKind, WidgetCallback},
@@ -98,9 +98,9 @@ fn anim_hover_out(button: Arc<Button>, widget_id: WidgetID) -> Animation {
) )
} }
pub fn construct( pub fn construct<U1, U2>(
layout: &mut Layout, layout: &mut Layout,
listeners: &mut EventListenerCollection<(), ()>, listeners: &mut EventListenerCollection<U1, U2>,
parent: WidgetID, parent: WidgetID,
params: Params, params: Params,
) -> anyhow::Result<Arc<Button>> { ) -> anyhow::Result<Arc<Button>> {

View File

@@ -1,5 +1,6 @@
use crate::any::AnyTrait; use crate::any::AnyTrait;
pub mod button; pub mod button;
pub mod slider;
pub trait Component: AnyTrait {} pub trait Component: AnyTrait {}

View File

@@ -0,0 +1,125 @@
use std::sync::Arc;
use taffy::prelude::{length, percent};
use crate::{
components::Component,
drawing::{self},
event::{EventListenerCollection, EventListenerKind, WidgetCallback},
layout::{Layout, WidgetID},
widget::{
rectangle::{Rectangle, RectangleParams},
util::WLength,
},
};
pub struct Params {
pub style: taffy::Style,
pub initial_value: f32,
pub min_value: f32,
pub max_value: f32,
}
impl Default for Params {
fn default() -> Self {
Self {
style: Default::default(),
initial_value: 0.5,
min_value: 0.0,
max_value: 1.0,
}
}
}
pub struct Slider {
pub body: WidgetID, // Outer rectangle
pub slider_handle_id: WidgetID, // Inner rectangle
pub slider_handle_node: taffy::NodeId,
}
impl Component for Slider {}
impl Slider {
pub fn set_value<'a, C>(&self, callback_data: &mut C, _value: f32)
where
C: WidgetCallback<'a>,
{
callback_data.mark_redraw();
callback_data.mark_dirty(self.slider_handle_node);
callback_data.call_on_widget(self.slider_handle_id, |_rect: &mut Rectangle| {
// todo
});
callback_data.mark_redraw();
callback_data.mark_dirty(self.slider_handle_node);
}
}
pub fn construct<U1, U2>(
layout: &mut Layout,
listeners: &mut EventListenerCollection<U1, U2>,
parent: WidgetID,
params: Params,
) -> anyhow::Result<Arc<Slider>> {
let mut style = params.style;
style.position = taffy::Position::Relative;
style.min_size = style.size;
style.max_size = style.size;
let body_color = drawing::Color::new(0.2, 0.3, 0.4, 1.0);
let body_border_color = drawing::Color::new(0.1, 0.2, 0.3, 1.0);
let handle_color = drawing::Color::new(1.0, 1.0, 1.0, 1.0);
let (body_id, _) = layout.add_child(
parent,
Rectangle::create(RectangleParams {
color: body_color,
round: WLength::Percent(1.0),
border_color: body_border_color,
border: 2.0,
..Default::default()
})?,
style,
)?;
let mut handle_style = taffy::Style::default();
handle_style.size.width = length(32.0);
handle_style.size.height = percent(1.0);
let (slider_handle_id, slider_handle_node) = layout.add_child(
body_id,
Rectangle::create(RectangleParams {
color: handle_color,
round: WLength::Percent(1.0),
..Default::default()
})?,
handle_style,
)?;
let slider = Arc::new(Slider {
body: body_id,
slider_handle_node,
slider_handle_id,
});
//let mut widget = layout.widget_map.get(rect_id).unwrap().lock().unwrap();
listeners.add(
body_id,
EventListenerKind::MouseEnter,
Box::new(move |_data, _, _| {}),
);
listeners.add(
body_id,
EventListenerKind::MouseMotion,
Box::new(move |_data, _, _| {}),
);
listeners.add(
body_id,
EventListenerKind::MouseLeave,
Box::new(move |_data, _, _| {}),
);
Ok(slider)
}

View File

@@ -134,6 +134,7 @@ pub enum EventListenerKind {
MousePress, MousePress,
MouseRelease, MouseRelease,
MouseEnter, MouseEnter,
MouseMotion,
MouseLeave, MouseLeave,
InternalStateChange, InternalStateChange,
} }

View File

@@ -9,9 +9,9 @@ use crate::{
widget::util::WLength, widget::util::WLength,
}; };
pub fn parse_component_button<'a>( pub fn parse_component_button<'a, U1, U2>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -43,7 +43,7 @@ pub fn parse_component_button<'a>(
} }
} }
let button = button::construct( let _button = button::construct(
ctx.layout, ctx.layout,
ctx.listeners, ctx.listeners,
parent_id, parent_id,

View File

@@ -0,0 +1,48 @@
use crate::{
components::slider,
layout::WidgetID,
parser::{ParserContext, ParserFile, iter_attribs, parse_check_f32, style::parse_style},
};
pub fn parse_component_slider<'a, U1, U2>(
file: &'a ParserFile,
ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID,
) -> 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);
for (key, value) in attribs {
match key.as_ref() {
"min_value" => {
parse_check_f32(value.as_ref(), &mut min_value);
}
"max_value" => {
parse_check_f32(value.as_ref(), &mut max_value);
}
"value" => {
parse_check_f32(value.as_ref(), &mut initial_value);
}
_ => {}
}
}
let slider = slider::construct(
ctx.layout,
ctx.listeners,
parent_id,
slider::Params {
min_value,
max_value,
initial_value,
style,
},
)?;
Ok(())
}

View File

@@ -1,4 +1,5 @@
mod component_button; mod component_button;
mod component_slider;
mod style; mod style;
mod widget_div; mod widget_div;
mod widget_label; mod widget_label;
@@ -11,9 +12,9 @@ use crate::{
event::EventListenerCollection, event::EventListenerCollection,
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
parser::{ parser::{
component_button::parse_component_button, widget_div::parse_widget_div, component_button::parse_component_button, component_slider::parse_component_slider,
widget_label::parse_widget_label, widget_rectangle::parse_widget_rectangle, widget_div::parse_widget_div, widget_label::parse_widget_label,
widget_sprite::parse_widget_sprite, widget_rectangle::parse_widget_rectangle, widget_sprite::parse_widget_sprite,
}, },
}; };
use ouroboros::self_referencing; use ouroboros::self_referencing;
@@ -59,11 +60,11 @@ impl ParserResult {
} }
} }
pub fn process_template( pub fn process_template<U1, U2>(
&mut self, &mut self,
template_name: &str, template_name: &str,
layout: &mut Layout, layout: &mut Layout,
listeners: &mut EventListenerCollection<(), ()>, listeners: &mut EventListenerCollection<U1, U2>,
widget_id: WidgetID, widget_id: WidgetID,
template_parameters: HashMap<Rc<str>, Rc<str>>, template_parameters: HashMap<Rc<str>, Rc<str>>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -108,9 +109,9 @@ struct MacroAttribs {
attribs: HashMap<Rc<str>, Rc<str>>, attribs: HashMap<Rc<str>, Rc<str>>,
} }
struct ParserContext<'a> { struct ParserContext<'a, U1, U2> {
layout: &'a mut Layout, layout: &'a mut Layout,
listeners: &'a mut EventListenerCollection<(), ()>, listeners: &'a mut EventListenerCollection<U1, U2>,
var_map: HashMap<Rc<str>, Rc<str>>, var_map: HashMap<Rc<str>, Rc<str>>,
macro_attribs: HashMap<Rc<str>, MacroAttribs>, macro_attribs: HashMap<Rc<str>, MacroAttribs>,
ids: HashMap<Rc<str>, WidgetID>, ids: HashMap<Rc<str>, WidgetID>,
@@ -208,6 +209,16 @@ fn parse_f32(value: &str) -> Option<f32> {
value.parse::<f32>().ok() value.parse::<f32>().ok()
} }
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> fn parse_size_unit<T>(value: &str) -> Option<T>
where where
T: taffy::prelude::FromPercent + taffy::prelude::FromLength, T: taffy::prelude::FromPercent + taffy::prelude::FromLength,
@@ -219,11 +230,11 @@ where
} }
} }
fn parse_widget_other_internal( fn parse_widget_other_internal<U1, U2>(
template: Rc<Template>, template: Rc<Template>,
template_parameters: HashMap<Rc<str>, Rc<str>>, template_parameters: HashMap<Rc<str>, Rc<str>>,
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let template_file = ParserFile { let template_file = ParserFile {
@@ -244,10 +255,10 @@ fn parse_widget_other_internal(
Ok(()) Ok(())
} }
fn parse_widget_other<'a>( fn parse_widget_other<'a, U1, U2>(
xml_tag_name: &str, xml_tag_name: &str,
file: &'a ParserFile, file: &'a ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -262,9 +273,9 @@ fn parse_widget_other<'a>(
parse_widget_other_internal(template.clone(), template_parameters, file, ctx, parent_id) parse_widget_other_internal(template.clone(), template_parameters, file, ctx, parent_id)
} }
fn parse_tag_include<'a>( fn parse_tag_include<'a, U1, U2>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -291,7 +302,10 @@ fn parse_tag_include<'a>(
Ok(()) Ok(())
} }
fn parse_tag_var<'a>(ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>) -> anyhow::Result<()> { fn parse_tag_var<'a, U1, U2>(
ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>,
) -> anyhow::Result<()> {
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;
@@ -349,9 +363,9 @@ pub fn replace_vars(input: &str, vars: &HashMap<Rc<str>, Rc<str>>) -> Rc<str> {
} }
#[allow(clippy::manual_strip)] #[allow(clippy::manual_strip)]
fn process_attrib<'a>( fn process_attrib<'a, U1, U2>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &'a ParserContext, ctx: &'a ParserContext<U1, U2>,
key: &str, key: &str,
value: &str, value: &str,
) -> (Rc<str>, Rc<str>) { ) -> (Rc<str>, Rc<str>) {
@@ -373,9 +387,9 @@ fn process_attrib<'a>(
} }
} }
fn iter_attribs<'a>( fn iter_attribs<'a, U1, U2>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &'a ParserContext, ctx: &'a ParserContext<U1, U2>,
node: &'a roxmltree::Node<'a, 'a>, node: &'a roxmltree::Node<'a, 'a>,
is_tag_macro: bool, is_tag_macro: bool,
) -> impl Iterator<Item = (/*key*/ Rc<str>, /*value*/ Rc<str>)> + 'a { ) -> impl Iterator<Item = (/*key*/ Rc<str>, /*value*/ Rc<str>)> + 'a {
@@ -409,8 +423,8 @@ fn iter_attribs<'a>(
res.into_iter() res.into_iter()
} }
fn parse_tag_theme<'a>( fn parse_tag_theme<'a, U1, U2>(
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
for child_node in node.children() { for child_node in node.children() {
@@ -429,9 +443,9 @@ fn parse_tag_theme<'a>(
Ok(()) Ok(())
} }
fn parse_tag_template( fn parse_tag_template<U1, U2>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'_, '_>, node: roxmltree::Node<'_, '_>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut template_name: Option<Rc<str>> = None; let mut template_name: Option<Rc<str>> = None;
@@ -465,9 +479,9 @@ fn parse_tag_template(
Ok(()) Ok(())
} }
fn parse_tag_macro( fn parse_tag_macro<U1, U2>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'_, '_>, node: roxmltree::Node<'_, '_>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut macro_name: Option<Rc<str>> = None; let mut macro_name: Option<Rc<str>> = None;
@@ -503,9 +517,9 @@ fn parse_tag_macro(
Ok(()) Ok(())
} }
fn parse_universal<'a>( fn parse_universal<'a, U1, U2>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
widget_id: WidgetID, widget_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -526,9 +540,9 @@ fn parse_universal<'a>(
Ok(()) Ok(())
} }
fn parse_children<'a>( fn parse_children<'a, U1, U2>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -552,6 +566,9 @@ fn parse_children<'a>(
"button" => { "button" => {
parse_component_button(file, ctx, child_node, parent_id)?; parse_component_button(file, ctx, child_node, parent_id)?;
} }
"slider" => {
parse_component_slider(file, ctx, child_node, parent_id)?;
}
"" => { /* ignore */ } "" => { /* ignore */ }
other_tag_name => { other_tag_name => {
parse_widget_other(other_tag_name, file, ctx, child_node, parent_id)?; parse_widget_other(other_tag_name, file, ctx, child_node, parent_id)?;
@@ -561,10 +578,10 @@ fn parse_children<'a>(
Ok(()) Ok(())
} }
fn create_default_context<'a>( fn create_default_context<'a, U1, U2>(
layout: &'a mut Layout, layout: &'a mut Layout,
listeners: &'a mut EventListenerCollection<(), ()>, listeners: &'a mut EventListenerCollection<U1, U2>,
) -> ParserContext<'a> { ) -> ParserContext<'a, U1, U2> {
ParserContext { ParserContext {
layout, layout,
listeners, listeners,
@@ -575,9 +592,9 @@ fn create_default_context<'a>(
} }
} }
pub fn parse_from_assets( pub fn parse_from_assets<U1, U2>(
layout: &mut Layout, layout: &mut Layout,
listeners: &mut EventListenerCollection<(), ()>, listeners: &mut EventListenerCollection<U1, U2>,
parent_id: WidgetID, parent_id: WidgetID,
path: &str, path: &str,
) -> anyhow::Result<ParserResult> { ) -> anyhow::Result<ParserResult> {
@@ -602,9 +619,9 @@ pub fn parse_from_assets(
Ok(result) Ok(result)
} }
pub fn new_layout_from_assets( pub fn new_layout_from_assets<U1, U2>(
assets: Box<dyn AssetProvider>, assets: Box<dyn AssetProvider>,
listeners: &mut EventListenerCollection<(), ()>, listeners: &mut EventListenerCollection<U1, U2>,
path: &str, path: &str,
) -> anyhow::Result<(Layout, ParserResult)> { ) -> anyhow::Result<(Layout, ParserResult)> {
let mut layout = Layout::new(assets)?; let mut layout = Layout::new(assets)?;
@@ -618,8 +635,8 @@ fn assets_path_to_xml(assets: &mut Box<dyn AssetProvider>, path: &Path) -> anyho
Ok(String::from_utf8(data)?) Ok(String::from_utf8(data)?)
} }
fn get_doc_from_path( fn get_doc_from_path<U1, U2>(
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
path: &Path, path: &Path,
) -> anyhow::Result<(ParserFile, roxmltree::NodeId)> { ) -> anyhow::Result<(ParserFile, roxmltree::NodeId)> {
let xml = assets_path_to_xml(&mut ctx.layout.assets, path)?; let xml = assets_path_to_xml(&mut ctx.layout.assets, path)?;
@@ -643,9 +660,9 @@ fn get_doc_from_path(
Ok((file, tag_layout.id())) Ok((file, tag_layout.id()))
} }
fn parse_document_root( fn parse_document_root<U1, U2>(
file: ParserFile, file: ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
parent_id: WidgetID, parent_id: WidgetID,
node_layout: roxmltree::NodeId, node_layout: roxmltree::NodeId,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {

View File

@@ -6,9 +6,9 @@ use crate::{
widget, widget,
}; };
pub fn parse_widget_div<'a>( pub fn parse_widget_div<'a, U1, U2>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {

View File

@@ -7,9 +7,9 @@ use crate::{
widget::text::{TextLabel, TextParams}, widget::text::{TextLabel, TextParams},
}; };
pub fn parse_widget_label<'a>( pub fn parse_widget_label<'a, U1, U2>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {

View File

@@ -8,9 +8,9 @@ use crate::{
widget::{self, rectangle::RectangleParams}, widget::{self, rectangle::RectangleParams},
}; };
pub fn parse_widget_rectangle<'a>( pub fn parse_widget_rectangle<'a, U1, U2>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {

View File

@@ -9,9 +9,9 @@ use crate::{
use super::{parse_color_hex, print_invalid_attrib}; use super::{parse_color_hex, print_invalid_attrib};
pub fn parse_widget_sprite<'a>( pub fn parse_widget_sprite<'a, U1, U2>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &mut ParserContext, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {

View File

@@ -390,6 +390,17 @@ impl WidgetState {
); );
} }
} }
call_event!(
self,
listeners,
widget_id,
node_id,
params,
MouseMotion,
user_data,
CallbackMetadata::None
);
} }
Event::MouseLeave(e) => { Event::MouseLeave(e) => {
if self.data.set_device_hovered(e.device, false) { if self.data.set_device_hovered(e.device, false) {
@@ -420,4 +431,4 @@ impl WidgetState {
} }
EventResult::Pass EventResult::Pass
} }
} }

View File

@@ -42,8 +42,13 @@ impl<S> GuiPanel<S> {
path: &str, path: &str,
state: S, state: S,
) -> anyhow::Result<(Self, ParserResult)> { ) -> anyhow::Result<(Self, ParserResult)> {
let (layout, parser_result) = let mut listeners = EventListenerCollection::<AppState, S>::default();
wgui::parser::new_layout_from_assets(Box::new(gui::asset::GuiAsset {}), path)?;
let (layout, parser_result) = wgui::parser::new_layout_from_assets(
Box::new(gui::asset::GuiAsset {}),
&mut listeners,
path,
)?;
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?; let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
let mut timestep = Timestep::new(); let mut timestep = Timestep::new();
@@ -56,7 +61,7 @@ impl<S> GuiPanel<S> {
timestep, timestep,
state, state,
timers: vec![], timers: vec![],
listeners: EventListenerCollection::default(), listeners,
}, },
parser_result, parser_result,
)) ))

View File

@@ -77,6 +77,7 @@ where
let (_, mut gui_state_key) = wgui::parser::new_layout_from_assets( let (_, mut gui_state_key) = wgui::parser::new_layout_from_assets(
Box::new(gui::asset::GuiAsset {}), Box::new(gui::asset::GuiAsset {}),
&mut panel.listeners,
"gui/keyboard.xml", "gui/keyboard.xml",
)?; )?;
@@ -156,7 +157,13 @@ where
} }
let template_key = format!("Key{:?}", key.cap_type); let template_key = format!("Key{:?}", key.cap_type);
gui_state_key.process_template(&template_key, &mut panel.layout, div, params)?; gui_state_key.process_template(
&template_key,
&mut panel.layout,
&mut panel.listeners,
div,
params,
)?;
if let Some(widget_id) = gui_state_key.ids.get(&*my_id) { if let Some(widget_id) = gui_state_key.ids.get(&*my_id) {
let key_state = { let key_state = {