diff --git a/uidev/assets/gui/various_widgets.xml b/uidev/assets/gui/various_widgets.xml index 16e0db6..981c259 100644 --- a/uidev/assets/gui/various_widgets.xml +++ b/uidev/assets/gui/various_widgets.xml @@ -2,13 +2,61 @@ + width="1000" height="500" min_width="1000" min_height="500" + gap="4" flex_direction="column" + overflow_y="scroll"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wgui/src/components/button.rs b/wgui/src/components/button.rs index 214022e..76543ae 100644 --- a/wgui/src/components/button.rs +++ b/wgui/src/components/button.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use taffy::{AlignItems, JustifyContent, prelude::length}; use crate::{ - animation::{self, Animation, AnimationEasing}, + animation::{Animation, AnimationEasing}, components::Component, drawing::{self, Color}, event::{EventListenerCollection, EventListenerKind, WidgetCallback}, @@ -98,9 +98,9 @@ fn anim_hover_out(button: Arc, widget_id: WidgetID) -> Animation { ) } -pub fn construct( +pub fn construct( layout: &mut Layout, - listeners: &mut EventListenerCollection<(), ()>, + listeners: &mut EventListenerCollection, parent: WidgetID, params: Params, ) -> anyhow::Result> { diff --git a/wgui/src/components/mod.rs b/wgui/src/components/mod.rs index 23d40bc..1f1ae6c 100644 --- a/wgui/src/components/mod.rs +++ b/wgui/src/components/mod.rs @@ -1,5 +1,6 @@ use crate::any::AnyTrait; pub mod button; +pub mod slider; pub trait Component: AnyTrait {} diff --git a/wgui/src/components/slider.rs b/wgui/src/components/slider.rs new file mode 100644 index 0000000..db9ad3d --- /dev/null +++ b/wgui/src/components/slider.rs @@ -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( + layout: &mut Layout, + listeners: &mut EventListenerCollection, + parent: WidgetID, + params: Params, +) -> anyhow::Result> { + 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) +} diff --git a/wgui/src/event.rs b/wgui/src/event.rs index 35017a1..a8aef3b 100644 --- a/wgui/src/event.rs +++ b/wgui/src/event.rs @@ -134,6 +134,7 @@ pub enum EventListenerKind { MousePress, MouseRelease, MouseEnter, + MouseMotion, MouseLeave, InternalStateChange, } diff --git a/wgui/src/parser/component_button.rs b/wgui/src/parser/component_button.rs index 2e087cb..2766e49 100644 --- a/wgui/src/parser/component_button.rs +++ b/wgui/src/parser/component_button.rs @@ -9,9 +9,9 @@ use crate::{ widget::util::WLength, }; -pub fn parse_component_button<'a>( +pub fn parse_component_button<'a, U1, U2>( file: &'a ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, ) -> anyhow::Result<()> { @@ -43,7 +43,7 @@ pub fn parse_component_button<'a>( } } - let button = button::construct( + let _button = button::construct( ctx.layout, ctx.listeners, parent_id, diff --git a/wgui/src/parser/component_slider.rs b/wgui/src/parser/component_slider.rs new file mode 100644 index 0000000..2e85906 --- /dev/null +++ b/wgui/src/parser/component_slider.rs @@ -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, + 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(()) +} diff --git a/wgui/src/parser/mod.rs b/wgui/src/parser/mod.rs index e680984..ce53e2c 100644 --- a/wgui/src/parser/mod.rs +++ b/wgui/src/parser/mod.rs @@ -1,4 +1,5 @@ mod component_button; +mod component_slider; mod style; mod widget_div; mod widget_label; @@ -11,9 +12,9 @@ use crate::{ event::EventListenerCollection, layout::{Layout, WidgetID}, parser::{ - component_button::parse_component_button, widget_div::parse_widget_div, - widget_label::parse_widget_label, widget_rectangle::parse_widget_rectangle, - widget_sprite::parse_widget_sprite, + component_button::parse_component_button, component_slider::parse_component_slider, + widget_div::parse_widget_div, widget_label::parse_widget_label, + widget_rectangle::parse_widget_rectangle, widget_sprite::parse_widget_sprite, }, }; use ouroboros::self_referencing; @@ -59,11 +60,11 @@ impl ParserResult { } } - pub fn process_template( + pub fn process_template( &mut self, template_name: &str, layout: &mut Layout, - listeners: &mut EventListenerCollection<(), ()>, + listeners: &mut EventListenerCollection, widget_id: WidgetID, template_parameters: HashMap, Rc>, ) -> anyhow::Result<()> { @@ -108,9 +109,9 @@ struct MacroAttribs { attribs: HashMap, Rc>, } -struct ParserContext<'a> { +struct ParserContext<'a, U1, U2> { layout: &'a mut Layout, - listeners: &'a mut EventListenerCollection<(), ()>, + listeners: &'a mut EventListenerCollection, var_map: HashMap, Rc>, macro_attribs: HashMap, MacroAttribs>, ids: HashMap, WidgetID>, @@ -208,6 +209,16 @@ fn parse_f32(value: &str) -> Option { value.parse::().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(value: &str) -> Option where T: taffy::prelude::FromPercent + taffy::prelude::FromLength, @@ -219,11 +230,11 @@ where } } -fn parse_widget_other_internal( +fn parse_widget_other_internal( template: Rc, template_parameters: HashMap, Rc>, file: &ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, parent_id: WidgetID, ) -> anyhow::Result<()> { let template_file = ParserFile { @@ -244,10 +255,10 @@ fn parse_widget_other_internal( Ok(()) } -fn parse_widget_other<'a>( +fn parse_widget_other<'a, U1, U2>( xml_tag_name: &str, file: &'a ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, ) -> anyhow::Result<()> { @@ -262,9 +273,9 @@ fn parse_widget_other<'a>( 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, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, ) -> anyhow::Result<()> { @@ -291,7 +302,10 @@ fn parse_tag_include<'a>( 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, + node: roxmltree::Node<'a, 'a>, +) -> anyhow::Result<()> { let mut out_key: Option<&str> = None; let mut out_value: Option<&str> = None; @@ -349,9 +363,9 @@ pub fn replace_vars(input: &str, vars: &HashMap, Rc>) -> Rc { } #[allow(clippy::manual_strip)] -fn process_attrib<'a>( +fn process_attrib<'a, U1, U2>( file: &'a ParserFile, - ctx: &'a ParserContext, + ctx: &'a ParserContext, key: &str, value: &str, ) -> (Rc, Rc) { @@ -373,9 +387,9 @@ fn process_attrib<'a>( } } -fn iter_attribs<'a>( +fn iter_attribs<'a, U1, U2>( file: &'a ParserFile, - ctx: &'a ParserContext, + ctx: &'a ParserContext, node: &'a roxmltree::Node<'a, 'a>, is_tag_macro: bool, ) -> impl Iterator, /*value*/ Rc)> + 'a { @@ -409,8 +423,8 @@ fn iter_attribs<'a>( res.into_iter() } -fn parse_tag_theme<'a>( - ctx: &mut ParserContext, +fn parse_tag_theme<'a, U1, U2>( + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, ) -> anyhow::Result<()> { for child_node in node.children() { @@ -429,9 +443,9 @@ fn parse_tag_theme<'a>( Ok(()) } -fn parse_tag_template( +fn parse_tag_template( file: &ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'_, '_>, ) -> anyhow::Result<()> { let mut template_name: Option> = None; @@ -465,9 +479,9 @@ fn parse_tag_template( Ok(()) } -fn parse_tag_macro( +fn parse_tag_macro( file: &ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'_, '_>, ) -> anyhow::Result<()> { let mut macro_name: Option> = None; @@ -503,9 +517,9 @@ fn parse_tag_macro( Ok(()) } -fn parse_universal<'a>( +fn parse_universal<'a, U1, U2>( file: &'a ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, widget_id: WidgetID, ) -> anyhow::Result<()> { @@ -526,9 +540,9 @@ fn parse_universal<'a>( Ok(()) } -fn parse_children<'a>( +fn parse_children<'a, U1, U2>( file: &ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, ) -> anyhow::Result<()> { @@ -552,6 +566,9 @@ fn parse_children<'a>( "button" => { parse_component_button(file, ctx, child_node, parent_id)?; } + "slider" => { + parse_component_slider(file, ctx, child_node, parent_id)?; + } "" => { /* ignore */ } other_tag_name => { parse_widget_other(other_tag_name, file, ctx, child_node, parent_id)?; @@ -561,10 +578,10 @@ fn parse_children<'a>( Ok(()) } -fn create_default_context<'a>( +fn create_default_context<'a, U1, U2>( layout: &'a mut Layout, - listeners: &'a mut EventListenerCollection<(), ()>, -) -> ParserContext<'a> { + listeners: &'a mut EventListenerCollection, +) -> ParserContext<'a, U1, U2> { ParserContext { layout, listeners, @@ -575,9 +592,9 @@ fn create_default_context<'a>( } } -pub fn parse_from_assets( +pub fn parse_from_assets( layout: &mut Layout, - listeners: &mut EventListenerCollection<(), ()>, + listeners: &mut EventListenerCollection, parent_id: WidgetID, path: &str, ) -> anyhow::Result { @@ -602,9 +619,9 @@ pub fn parse_from_assets( Ok(result) } -pub fn new_layout_from_assets( +pub fn new_layout_from_assets( assets: Box, - listeners: &mut EventListenerCollection<(), ()>, + listeners: &mut EventListenerCollection, path: &str, ) -> anyhow::Result<(Layout, ParserResult)> { let mut layout = Layout::new(assets)?; @@ -618,8 +635,8 @@ fn assets_path_to_xml(assets: &mut Box, path: &Path) -> anyho Ok(String::from_utf8(data)?) } -fn get_doc_from_path( - ctx: &mut ParserContext, +fn get_doc_from_path( + ctx: &mut ParserContext, path: &Path, ) -> anyhow::Result<(ParserFile, roxmltree::NodeId)> { 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())) } -fn parse_document_root( +fn parse_document_root( file: ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, parent_id: WidgetID, node_layout: roxmltree::NodeId, ) -> anyhow::Result<()> { diff --git a/wgui/src/parser/widget_div.rs b/wgui/src/parser/widget_div.rs index 7cc35aa..7293812 100644 --- a/wgui/src/parser/widget_div.rs +++ b/wgui/src/parser/widget_div.rs @@ -6,9 +6,9 @@ use crate::{ widget, }; -pub fn parse_widget_div<'a>( +pub fn parse_widget_div<'a, U1, U2>( file: &ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, ) -> anyhow::Result<()> { diff --git a/wgui/src/parser/widget_label.rs b/wgui/src/parser/widget_label.rs index 289d7cc..38a6011 100644 --- a/wgui/src/parser/widget_label.rs +++ b/wgui/src/parser/widget_label.rs @@ -7,9 +7,9 @@ use crate::{ widget::text::{TextLabel, TextParams}, }; -pub fn parse_widget_label<'a>( +pub fn parse_widget_label<'a, U1, U2>( file: &'a ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, ) -> anyhow::Result<()> { diff --git a/wgui/src/parser/widget_rectangle.rs b/wgui/src/parser/widget_rectangle.rs index ee83922..ba600c0 100644 --- a/wgui/src/parser/widget_rectangle.rs +++ b/wgui/src/parser/widget_rectangle.rs @@ -8,9 +8,9 @@ use crate::{ widget::{self, rectangle::RectangleParams}, }; -pub fn parse_widget_rectangle<'a>( +pub fn parse_widget_rectangle<'a, U1, U2>( file: &ParserFile, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, ) -> anyhow::Result<()> { diff --git a/wgui/src/parser/widget_sprite.rs b/wgui/src/parser/widget_sprite.rs index cbdf79f..370301b 100644 --- a/wgui/src/parser/widget_sprite.rs +++ b/wgui/src/parser/widget_sprite.rs @@ -9,9 +9,9 @@ use crate::{ 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, - ctx: &mut ParserContext, + ctx: &mut ParserContext, node: roxmltree::Node<'a, 'a>, parent_id: WidgetID, ) -> anyhow::Result<()> { diff --git a/wgui/src/widget/mod.rs b/wgui/src/widget/mod.rs index 71a8a86..4c580d6 100644 --- a/wgui/src/widget/mod.rs +++ b/wgui/src/widget/mod.rs @@ -390,6 +390,17 @@ impl WidgetState { ); } } + + call_event!( + self, + listeners, + widget_id, + node_id, + params, + MouseMotion, + user_data, + CallbackMetadata::None + ); } Event::MouseLeave(e) => { if self.data.set_device_hovered(e.device, false) { @@ -420,4 +431,4 @@ impl WidgetState { } EventResult::Pass } -} \ No newline at end of file +} diff --git a/wlx-overlay-s/src/gui/panel.rs b/wlx-overlay-s/src/gui/panel.rs index 0e35596..d912398 100644 --- a/wlx-overlay-s/src/gui/panel.rs +++ b/wlx-overlay-s/src/gui/panel.rs @@ -42,8 +42,13 @@ impl GuiPanel { path: &str, state: S, ) -> anyhow::Result<(Self, ParserResult)> { - let (layout, parser_result) = - wgui::parser::new_layout_from_assets(Box::new(gui::asset::GuiAsset {}), path)?; + let mut listeners = EventListenerCollection::::default(); + + 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 mut timestep = Timestep::new(); @@ -56,7 +61,7 @@ impl GuiPanel { timestep, state, timers: vec![], - listeners: EventListenerCollection::default(), + listeners, }, parser_result, )) diff --git a/wlx-overlay-s/src/overlays/keyboard/builder.rs b/wlx-overlay-s/src/overlays/keyboard/builder.rs index 7b6fda5..a7415a6 100644 --- a/wlx-overlay-s/src/overlays/keyboard/builder.rs +++ b/wlx-overlay-s/src/overlays/keyboard/builder.rs @@ -77,6 +77,7 @@ where let (_, mut gui_state_key) = wgui::parser::new_layout_from_assets( Box::new(gui::asset::GuiAsset {}), + &mut panel.listeners, "gui/keyboard.xml", )?; @@ -156,7 +157,13 @@ where } 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) { let key_state = {