From 5ce745d2b9c1551726d0622c9c937df4502f9fc3 Mon Sep 17 00:00:00 2001 From: Aleksander Date: Sat, 15 Nov 2025 00:00:41 +0100 Subject: [PATCH] various_widgets: add visibility test, minor refactoring --- uidev/assets/gui/various_widgets.xml | 96 ++++++++++++++------------ uidev/src/testbed/testbed_generic.rs | 19 +++++ wgui/doc/widgets.md | 4 ++ wgui/src/components/button.rs | 27 ++++++-- wgui/src/components/checkbox.rs | 17 +++-- wgui/src/components/mod.rs | 15 +++- wgui/src/components/slider.rs | 36 ++++++---- wgui/src/components/tooltip.rs | 12 ++-- wgui/src/event.rs | 6 +- wgui/src/layout.rs | 41 ++++++++--- wlx-overlay-s/src/overlays/edit/tab.rs | 3 +- 11 files changed, 182 insertions(+), 94 deletions(-) diff --git a/uidev/assets/gui/various_widgets.xml b/uidev/assets/gui/various_widgets.xml index e6614a5..65cefb4 100644 --- a/uidev/assets/gui/various_widgets.xml +++ b/uidev/assets/gui/various_widgets.xml @@ -1,31 +1,40 @@ - - - -
+ + + + + + + + + +
- -
+ +
- -
- -
- -
-
- + +
\ No newline at end of file diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index 6e790fb..0262abf 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -18,6 +18,7 @@ use wgui::{ i18n::Translation, layout::{Layout, LayoutParams, RcLayout, Widget}, parser::{Fetchable, ParseDocumentExtra, ParseDocumentParams, ParserState}, + taffy, widget::{label::WidgetLabel, rectangle::WidgetRectangle}, windowing::{WguiWindow, WguiWindowParams}, }; @@ -118,6 +119,24 @@ impl TestbedGeneric { }, )?; + let cb_visible = state.fetch_component_as::("cb_visible")?; + let div_visibility = state.fetch_widget(&layout.state, "div_visibility")?; + + cb_visible.on_toggle(Box::new(move |common, evt| { + let mut style = common + .state + .get_widget_style(div_visibility.id) + .unwrap() + .clone(); + style.display = if evt.checked { + taffy::Display::Flex + } else { + taffy::Display::None + }; + common.alterables.set_style(div_visibility.id, style); + Ok(()) + })); + let label_cur_option = state.fetch_widget(&layout.state, "label_current_option")?; let button_click_me = state.fetch_component_as::("button_click_me")?; diff --git a/wgui/doc/widgets.md b/wgui/doc/widgets.md index 047583b..d6f24a9 100644 --- a/wgui/doc/widgets.md +++ b/wgui/doc/widgets.md @@ -196,6 +196,10 @@ _Tooltip text on hover, translated by key_ `tooltip_side`: "top" | "bottom" | "left" | "right" (default: top) +`sticky`: "1" | "0" (default: "0") + +_make button act as a toggle (visual only)_ + #### Info Child widgets are supported and can be added directly in XML. diff --git a/wgui/src/components/button.rs b/wgui/src/components/button.rs index fb7acf0..cad2229 100644 --- a/wgui/src/components/button.rs +++ b/wgui/src/components/button.rs @@ -1,6 +1,6 @@ use crate::{ animation::{Animation, AnimationEasing}, - components::{self, tooltip::ComponentTooltip, Component, ComponentBase, ComponentTrait, InitData}, + components::{self, Component, ComponentBase, ComponentTrait, RefreshData, tooltip::ComponentTooltip}, drawing::{self, Boundary, Color}, event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind}, i18n::Translation, @@ -10,15 +10,15 @@ use crate::{ util::centered_matrix, }, widget::{ + ConstructEssentials, EventResult, WidgetData, label::{WidgetLabel, WidgetLabelParams}, rectangle::{WidgetRectangle, WidgetRectangleParams}, util::WLength, - ConstructEssentials, EventResult, WidgetData, }, }; use glam::{Mat4, Vec3}; use std::{cell::RefCell, rc::Rc}; -use taffy::{prelude::length, AlignItems, JustifyContent}; +use taffy::{AlignItems, JustifyContent, prelude::length}; pub struct Params { pub text: Option, // if unset, label will not be populated @@ -83,11 +83,15 @@ pub struct ComponentButton { } impl ComponentTrait for ComponentButton { - fn base(&mut self) -> &mut ComponentBase { + fn base(&self) -> &ComponentBase { + &self.base + } + + fn base_mut(&mut self) -> &mut ComponentBase { &mut self.base } - fn init(&self, _data: &mut InitData) {} + fn refresh(&self, _data: &mut RefreshData) {} } impl ComponentButton { @@ -339,6 +343,7 @@ fn register_event_mouse_release( ) } +#[allow(clippy::too_many_lines)] pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc)> { let globals = ess.layout.state.globals.clone(); let mut style = params.style; @@ -390,7 +395,14 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul let id_rect = root.id; - let light_text = (color.r + color.g + color.b) < 1.5; + let light_text = { + let mult = if globals.get().defaults.dark_mode { + color.a + } else { + 1.0 - color.a + }; + (color.r + color.g + color.b) * mult < 1.5 + }; let id_label = if let Some(content) = params.text { let (label, _node_label) = ess.layout.add_child( @@ -436,6 +448,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul })); let base = ComponentBase { + id: root.id, lhandles: { let mut widget = ess.layout.state.widgets.get(id_rect).unwrap().state(); vec![ @@ -449,6 +462,6 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul let button = Rc::new(ComponentButton { base, data, state }); - ess.layout.defer_component_init(Component(button.clone())); + ess.layout.defer_component_refresh(Component(button.clone())); Ok((root, button)) } diff --git a/wgui/src/components/checkbox.rs b/wgui/src/components/checkbox.rs index 3ef411d..472eb69 100644 --- a/wgui/src/components/checkbox.rs +++ b/wgui/src/components/checkbox.rs @@ -1,22 +1,22 @@ use std::{cell::RefCell, rc::Rc}; use taffy::{ - prelude::{length, percent}, AlignItems, JustifyContent, + prelude::{length, percent}, }; use crate::{ animation::{Animation, AnimationEasing}, - components::{Component, ComponentBase, ComponentTrait, InitData}, + components::{Component, ComponentBase, ComponentTrait, RefreshData}, drawing::Color, event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind}, i18n::Translation, layout::{self, WidgetID, WidgetPair}, renderer_vk::text::{FontWeight, TextStyle}, widget::{ + ConstructEssentials, EventResult, label::{WidgetLabel, WidgetLabelParams}, rectangle::{WidgetRectangle, WidgetRectangleParams}, util::WLength, - ConstructEssentials, EventResult, }, }; @@ -68,11 +68,15 @@ pub struct ComponentCheckbox { } impl ComponentTrait for ComponentCheckbox { - fn base(&mut self) -> &mut ComponentBase { + fn base(&self) -> &ComponentBase { + &self.base + } + + fn base_mut(&mut self) -> &mut ComponentBase { &mut self.base } - fn init(&self, _data: &mut InitData) {} + fn refresh(&self, _data: &mut RefreshData) {} } const COLOR_CHECKED: Color = Color::new(0.1, 0.5, 1.0, 1.0); @@ -333,6 +337,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul })); let base = ComponentBase { + id: root.id, lhandles: { let mut widget = ess.layout.state.widgets.get(id_container).unwrap().state(); vec![ @@ -346,6 +351,6 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul let checkbox = Rc::new(ComponentCheckbox { base, data, state }); - ess.layout.defer_component_init(Component(checkbox.clone())); + ess.layout.defer_component_refresh(Component(checkbox.clone())); Ok((root, checkbox)) } diff --git a/wgui/src/components/mod.rs b/wgui/src/components/mod.rs index c659579..9f85887 100644 --- a/wgui/src/components/mod.rs +++ b/wgui/src/components/mod.rs @@ -3,6 +3,7 @@ use std::rc::Rc; use crate::{ any::AnyTrait, event::{CallbackDataCommon, EventListenerID}, + layout::WidgetID, }; pub mod button; @@ -10,7 +11,7 @@ pub mod checkbox; pub mod slider; pub mod tooltip; -pub struct InitData<'a> { +pub struct RefreshData<'a> { pub common: &'a mut CallbackDataCommon<'a>, } @@ -19,11 +20,19 @@ pub struct InitData<'a> { pub struct ComponentBase { #[allow(dead_code)] lhandles: Vec, + id: WidgetID, +} + +impl ComponentBase { + pub const fn get_id(&self) -> WidgetID { + self.id + } } pub trait ComponentTrait: AnyTrait { - fn base(&mut self) -> &mut ComponentBase; - fn init(&self, data: &mut InitData); + fn base(&self) -> &ComponentBase; + fn base_mut(&mut self) -> &mut ComponentBase; + fn refresh(&self, data: &mut RefreshData); } #[derive(Clone)] diff --git a/wgui/src/components/slider.rs b/wgui/src/components/slider.rs index 2f6438b..d4f4487 100644 --- a/wgui/src/components/slider.rs +++ b/wgui/src/components/slider.rs @@ -5,7 +5,7 @@ use taffy::prelude::{length, percent}; use crate::{ animation::{Animation, AnimationEasing}, - components::{Component, ComponentBase, ComponentTrait, InitData}, + components::{Component, ComponentBase, ComponentTrait, RefreshData}, drawing::{self}, event::{self, CallbackDataCommon, EventListenerCollection, EventListenerKind}, i18n::Translation, @@ -15,11 +15,11 @@ use crate::{ util, }, widget::{ + ConstructEssentials, EventResult, div::WidgetDiv, label::{WidgetLabel, WidgetLabelParams}, rectangle::{WidgetRectangle, WidgetRectangleParams}, util::WLength, - ConstructEssentials, EventResult, }, }; @@ -80,7 +80,8 @@ struct Data { body: WidgetID, // Div slider_handle_rect_id: WidgetID, // Rectangle slider_text_id: WidgetID, // Text - slider_handle_node: taffy::NodeId, + slider_handle: WidgetPair, + slider_handle_node_id: taffy::NodeId, slider_body_node: taffy::NodeId, } @@ -98,13 +99,17 @@ pub struct ComponentSlider { } impl ComponentTrait for ComponentSlider { - fn init(&self, init_data: &mut InitData) { + fn refresh(&self, init_data: &mut RefreshData) { let mut state = self.state.borrow_mut(); let value = state.values.value; state.set_value(init_data.common, &self.data, value); } - fn base(&mut self) -> &mut ComponentBase { + fn base(&self) -> &ComponentBase { + &self.base + } + + fn base_mut(&mut self) -> &mut ComponentBase { &mut self.base } } @@ -187,17 +192,15 @@ impl State { } fn set_value(&mut self, common: &mut CallbackDataCommon, data: &Data, value: f32) { - //common.call_on_widget(data.slider_handle_id, |_div: &mut Div| {}); - let before = self.values.value; self.values.set_value(value); let changed = self.values.value != before; - let mut style = common.state.tree.style(data.slider_handle_node).unwrap().clone(); + let mut style = common.state.tree.style(data.slider_handle_node_id).unwrap().clone(); conf_handle_style(&self.values, data.slider_body_node, &mut style, &common.state.tree); - common.alterables.mark_dirty(data.slider_handle_node); + common.alterables.mark_dirty(data.slider_handle_node_id); common.alterables.mark_redraw(); - common.alterables.set_style(data.slider_handle_node, style); + common.alterables.set_style(data.slider_handle.id, style); if let Some(mut label) = common.state.widgets.get_as::(data.slider_text_id) { Self::update_text(common, &mut label, self.values.value); @@ -401,9 +404,10 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul }; // invisible outer handle body - let (slider_handle, slider_handle_node) = ess - .layout - .add_child(body_id, WidgetDiv::create(), slider_handle_style)?; + let (slider_handle, slider_handle_node_id) = + ess + .layout + .add_child(body_id, WidgetDiv::create(), slider_handle_style)?; let (slider_handle_rect, _) = ess.layout.add_child( slider_handle.id, @@ -452,15 +456,17 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul let data = Rc::new(Data { body: body_id, - slider_handle_node, + slider_handle, slider_handle_rect_id: slider_handle_rect.id, slider_body_node, + slider_handle_node_id, slider_text_id: slider_text.id, }); let state = Rc::new(RefCell::new(state)); let base = ComponentBase { + id: root.id, lhandles: { let mut widget = ess.layout.state.widgets.get(body_id).unwrap().state(); vec![ @@ -476,6 +482,6 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul let slider = Rc::new(ComponentSlider { base, data, state }); - ess.layout.defer_component_init(Component(slider.clone())); + ess.layout.defer_component_refresh(Component(slider.clone())); Ok((root, slider)) } diff --git a/wgui/src/components/tooltip.rs b/wgui/src/components/tooltip.rs index 9909901..b7c801b 100644 --- a/wgui/src/components/tooltip.rs +++ b/wgui/src/components/tooltip.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc}; use taffy::prelude::length; use crate::{ - components::{self, Component, ComponentBase, ComponentTrait, InitData}, + components::{self, Component, ComponentBase, ComponentTrait, RefreshData}, drawing::Color, i18n::Translation, layout::{self, LayoutTask, LayoutTasks, WidgetID, WidgetPair}, @@ -65,11 +65,15 @@ pub struct ComponentTooltip { } impl ComponentTrait for ComponentTooltip { - fn base(&mut self) -> &mut ComponentBase { + fn base_mut(&mut self) -> &mut ComponentBase { &mut self.base } - fn init(&self, _data: &mut InitData) {} + fn base(&self) -> &ComponentBase { + &self.base + } + + fn refresh(&self, _data: &mut RefreshData) {} } impl ComponentTooltip {} @@ -200,7 +204,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul tasks: ess.layout.tasks.clone(), }); - ess.layout.defer_component_init(Component(tooltip.clone())); + ess.layout.defer_component_refresh(Component(tooltip.clone())); Ok((div, tooltip)) } diff --git a/wgui/src/event.rs b/wgui/src/event.rs index 4088f91..cc361db 100644 --- a/wgui/src/event.rs +++ b/wgui/src/event.rs @@ -96,7 +96,7 @@ impl Event { #[derive(Default)] pub struct EventAlterables { pub dirty_nodes: Vec, - pub style_set_requests: Vec<(taffy::NodeId, taffy::Style)>, + pub style_set_requests: Vec<(WidgetID, taffy::Style)>, pub animations: Vec, pub widgets_to_tick: HashSet, // widgets which needs to be ticked in the next `Layout::update()` fn pub transform_stack: TransformStack, @@ -112,8 +112,8 @@ impl EventAlterables { self.needs_redraw = true; } - pub fn set_style(&mut self, node_id: taffy::NodeId, style: taffy::Style) { - self.style_set_requests.push((node_id, style)); + pub fn set_style(&mut self, widget_id: WidgetID, style: taffy::Style) { + self.style_set_requests.push((widget_id, style)); } pub fn mark_dirty(&mut self, node_id: taffy::NodeId) { diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index e46fb9b..a21f23e 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -7,7 +7,7 @@ use std::{ use crate::{ animation::Animations, - components::{Component, InitData}, + components::{Component, RefreshData}, drawing::{self, ANSI_BOLD_CODE, ANSI_RESET_CODE, Boundary, push_scissor_stack, push_transform_stack}, event::{self, CallbackDataCommon, EventAlterables}, globals::WguiGlobals, @@ -139,7 +139,8 @@ pub struct Layout { pub tasks: LayoutTasks, - pub components_to_init: Vec, + components_to_refresh: Vec, + pub widgets_to_tick: Vec, // *Main root* @@ -311,15 +312,19 @@ impl Layout { } fn process_pending_components(&mut self, alterables: &mut EventAlterables) { - for comp in &self.components_to_init { + for comp in &self.components_to_refresh { let mut common = CallbackDataCommon { state: &self.state, alterables, }; - comp.0.init(&mut InitData { common: &mut common }); + /* todo + let widget_id = comp.0.base().get_id(); + */ + + comp.0.refresh(&mut RefreshData { common: &mut common }); } - self.components_to_init.clear(); + self.components_to_refresh.clear(); } fn process_pending_widget_ticks(&mut self, alterables: &mut EventAlterables) { @@ -333,8 +338,8 @@ impl Layout { self.widgets_to_tick.clear(); } - pub fn defer_component_init(&mut self, component: Component) { - self.components_to_init.push(component); + pub fn defer_component_refresh(&mut self, component: Component) { + self.components_to_refresh.push(component); } /// Convenience function to avoid repeated `WidgetID` → `WidgetState` lookups. @@ -552,7 +557,7 @@ impl Layout { needs_redraw: true, haptics_triggered: false, animations: Animations::default(), - components_to_init: Vec::new(), + components_to_refresh: Vec::new(), widgets_to_tick: Vec::new(), tasks: LayoutTasks::new(), }) @@ -680,9 +685,11 @@ impl Layout { } } - for request in alterables.style_set_requests { - if let Err(e) = self.state.tree.set_style(request.0, request.1) { - log::error!("failed to set style for taffy widget ID {:?}: {:?}", request.0, e); + for (widget_id, style) in alterables.style_set_requests { + if let Some(node_id) = self.state.nodes.get(widget_id) { + if let Err(e) = self.state.tree.set_style(*node_id, style) { + log::error!("failed to set style for taffy widget ID {node_id:?}: {e:?}"); + } } } @@ -763,6 +770,14 @@ impl LayoutState { Vec2::new(layout.size.width, layout.size.height) } + pub fn get_node_style(&self, id: NodeId) -> Option<&taffy::Style> { + let Ok(style) = self.tree.style(id) else { + return None; + }; + + Some(style) + } + pub fn get_widget_boundary(&self, id: WidgetID) -> Boundary { let Some(node_id) = self.nodes.get(id) else { return Boundary::default(); @@ -778,4 +793,8 @@ impl LayoutState { self.get_node_size(*node_id) } + + pub fn get_widget_style(&self, id: WidgetID) -> Option<&taffy::Style> { + self.get_node_style(*self.nodes.get(id)?) + } } diff --git a/wlx-overlay-s/src/overlays/edit/tab.rs b/wlx-overlay-s/src/overlays/edit/tab.rs index 0e15a63..fa3b89a 100644 --- a/wlx-overlay-s/src/overlays/edit/tab.rs +++ b/wlx-overlay-s/src/overlays/edit/tab.rs @@ -80,7 +80,6 @@ impl ButtonPaneTabSwitcher { } fn set_tab_active(common: &mut CallbackDataCommon, data: &TabData, active: bool) { - let pane_node = common.state.nodes[data.pane]; let style = Style { display: if active { Display::Block @@ -89,7 +88,7 @@ fn set_tab_active(common: &mut CallbackDataCommon, data: &TabData, active: bool) }, ..Default::default() }; - common.alterables.set_style(pane_node, style); + common.alterables.set_style(data.pane, style); if let Some(button) = data.button.as_ref() { button.set_sticky_state(common, active); }