From 92444bb5c4769762bb34ceebabb3e8af5e45ce27 Mon Sep 17 00:00:00 2001 From: Aleksander Date: Wed, 2 Jul 2025 22:55:42 +0200 Subject: [PATCH] simplify and optimize event states, deduplication --- wgui/src/animation.rs | 72 ++------ wgui/src/components/button.rs | 8 +- wgui/src/components/slider.rs | 16 +- wgui/src/event.rs | 50 ++++-- wgui/src/layout.rs | 161 ++++++++---------- wgui/src/widget/mod.rs | 52 +++--- .../src/overlays/keyboard/builder.rs | 20 ++- 7 files changed, 167 insertions(+), 212 deletions(-) diff --git a/wgui/src/animation.rs b/wgui/src/animation.rs index 03660d5..1df6fea 100644 --- a/wgui/src/animation.rs +++ b/wgui/src/animation.rs @@ -1,7 +1,7 @@ use glam::{FloatExt, Vec2}; use crate::{ - event::CallbackDataCommon, + event::{CallbackDataCommon, EventAlterables, EventRefs}, layout::{WidgetID, WidgetMap, WidgetNodeMap}, widget::{WidgetData, WidgetObj}, }; @@ -59,11 +59,6 @@ pub struct Animation { callback: AnimationCallback, } -#[derive(Default)] -struct CallResult { - needs_redraw: bool, -} - impl Animation { pub fn new( target_widget: WidgetID, @@ -94,22 +89,13 @@ impl Animation { } } - fn call( - &self, - widget_map: &WidgetMap, - widget_node_map: &WidgetNodeMap, - tree: &taffy::tree::TaffyTree, - dirty_nodes: &mut Vec, - pos: f32, - ) -> CallResult { - let mut res = CallResult::default(); - - let Some(widget) = widget_map.get(self.target_widget).cloned() else { - return res; // failed + fn call(&self, refs: &EventRefs, alterables: &mut EventAlterables, pos: f32) { + let Some(widget) = refs.widget_map.get(self.target_widget).cloned() else { + return; // failed }; - let widget_node = *widget_node_map.get(self.target_widget).unwrap(); - let layout = tree.layout(widget_node).unwrap(); // should always succeed + let widget_node = *refs.widget_node_map.get(self.target_widget).unwrap(); + let layout = refs.tree.layout(widget_node).unwrap(); // should always succeed let mut widget = widget.lock().unwrap(); @@ -123,21 +109,9 @@ impl Animation { pos, }; - let common = &mut CallbackDataCommon { - dirty_nodes, - needs_redraw: false, - trigger_haptics: false, - widgets: widget_map, - taffy_layout: layout, - }; + let common = &mut CallbackDataCommon { refs, alterables }; (self.callback)(common, data); - - if common.needs_redraw { - res.needs_redraw = true; - } - - res } } @@ -147,14 +121,7 @@ pub struct Animations { } impl Animations { - pub fn tick( - &mut self, - widget_map: &WidgetMap, - widget_node_map: &WidgetNodeMap, - tree: &taffy::tree::TaffyTree, - dirty_nodes: &mut Vec, - needs_redraw: &mut bool, - ) { + pub fn tick(&mut self, refs: &EventRefs, alterables: &mut EventAlterables) { for anim in &mut self.running_animations { let x = 1.0 - (anim.ticks_remaining as f32 / anim.ticks_duration as f32); let pos = if anim.ticks_remaining > 0 { @@ -166,11 +133,10 @@ impl Animations { anim.pos_prev = anim.pos; anim.pos = pos; + anim.call(refs, alterables, 1.0); - let res = anim.call(widget_map, widget_node_map, tree, dirty_nodes, 1.0); - - if anim.last_tick || res.needs_redraw { - *needs_redraw = true; + if anim.last_tick { + alterables.needs_redraw = true; } anim.ticks_remaining -= 1; @@ -181,22 +147,10 @@ impl Animations { .retain(|anim| anim.ticks_remaining > 0); } - pub fn process( - &mut self, - widget_map: &WidgetMap, - widget_node_map: &WidgetNodeMap, - tree: &taffy::tree::TaffyTree, - dirty_nodes: &mut Vec, - alpha: f32, - needs_redraw: &mut bool, - ) { + pub fn process(&mut self, refs: &EventRefs, alterables: &mut EventAlterables, alpha: f32) { for anim in &mut self.running_animations { let pos = anim.pos_prev.lerp(anim.pos, alpha); - let res = anim.call(widget_map, widget_node_map, tree, dirty_nodes, pos); - - if res.needs_redraw { - *needs_redraw = true; - } + anim.call(refs, alterables, pos); } } diff --git a/wgui/src/components/button.rs b/wgui/src/components/button.rs index 30c5cc2..5a11df5 100644 --- a/wgui/src/components/button.rs +++ b/wgui/src/components/button.rs @@ -159,9 +159,7 @@ pub fn construct( rect_id, EventListenerKind::MouseEnter, Box::new(move |common, data, _, _| { - data - .animations - .push(anim_hover_in(button.clone(), data.widget_id)); + common.animate(anim_hover_in(button.clone(), data.widget_id)); }), ); @@ -170,9 +168,7 @@ pub fn construct( rect_id, EventListenerKind::MouseLeave, Box::new(move |common, data, _, _| { - data - .animations - .push(anim_hover_out(button.clone(), data.widget_id)); + common.animate(anim_hover_out(button.clone(), data.widget_id)); }), ); diff --git a/wgui/src/components/slider.rs b/wgui/src/components/slider.rs index be54d9b..9744c93 100644 --- a/wgui/src/components/slider.rs +++ b/wgui/src/components/slider.rs @@ -93,8 +93,8 @@ fn anim_rect(rect: &mut Rectangle, pos: f32) { drawing::Color::lerp(&HANDLE_BORDER_COLOR, &HANDLE_BORDER_COLOR_HOVERED, pos); } -fn on_enter_anim(data: &mut event::CallbackData, handle_id: WidgetID) { - data.animations.push(Animation::new( +fn on_enter_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) { + common.animate(Animation::new( handle_id, 5, AnimationEasing::OutQuad, @@ -107,8 +107,8 @@ fn on_enter_anim(data: &mut event::CallbackData, handle_id: WidgetID) { )); } -fn on_leave_anim(data: &mut event::CallbackData, handle_id: WidgetID) { - data.animations.push(Animation::new( +fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) { + common.animate(Animation::new( handle_id, 10, AnimationEasing::OutQuad, @@ -130,10 +130,10 @@ fn register_event_mouse_enter( listeners.add( slider.body, EventListenerKind::MouseEnter, - Box::new(move |common, data, _, _| { + Box::new(move |common, _data, _, _| { common.trigger_haptics(); slider.get_state().hovered = true; - on_enter_anim(data, slider.slider_handle_rect_id); + on_enter_anim(common, slider.slider_handle_rect_id); }), ); } @@ -145,10 +145,10 @@ fn register_event_mouse_leave( listeners.add( slider.body, EventListenerKind::MouseLeave, - Box::new(move |common, data, _, _| { + Box::new(move |common, _data, _, _| { common.trigger_haptics(); slider.get_state().hovered = false; - on_leave_anim(data, slider.slider_handle_rect_id); + on_leave_anim(common, slider.slider_handle_rect_id); }), ); } diff --git a/wgui/src/event.rs b/wgui/src/event.rs index 7f68aee..89319bf 100644 --- a/wgui/src/event.rs +++ b/wgui/src/event.rs @@ -2,9 +2,9 @@ use glam::Vec2; use slotmap::SecondaryMap; use crate::{ - animation, - layout::{WidgetID, WidgetMap}, - transform_stack::Transform, + animation::{self, Animation}, + layout::{WidgetID, WidgetMap, WidgetNodeMap}, + transform_stack::{Transform, TransformStack}, widget::{WidgetData, WidgetObj}, }; @@ -74,21 +74,34 @@ impl Event { } } -pub struct CallbackDataCommon<'a> { - pub widgets: &'a WidgetMap, - pub taffy_layout: &'a taffy::Layout, - pub dirty_nodes: &'a mut Vec, +pub struct EventRefs<'a> { + pub widget_map: &'a WidgetMap, + pub widget_node_map: &'a WidgetNodeMap, + pub tree: &'a taffy::tree::TaffyTree, +} + +#[derive(Default)] +pub struct EventAlterables { + pub dirty_nodes: Vec, + pub style_set_requests: Vec<(taffy::NodeId, taffy::Style)>, + pub animations: Vec, + pub transform_stack: TransformStack, pub needs_redraw: bool, pub trigger_haptics: bool, } -impl<'a> CallbackDataCommon<'a> { +pub struct CallbackDataCommon<'a> { + pub refs: &'a EventRefs<'a>, + pub alterables: &'a mut EventAlterables, +} + +impl CallbackDataCommon<'_> { pub fn call_on_widget(&self, widget_id: WidgetID, func: FUNC) where WIDGET: WidgetObj, FUNC: FnOnce(&mut WIDGET), { - let Some(widget) = self.widgets.get(widget_id) else { + let Some(widget) = self.refs.widget_map.get(widget_id) else { debug_assert!(false); return; }; @@ -100,26 +113,33 @@ impl<'a> CallbackDataCommon<'a> { } pub fn mark_redraw(&mut self) { - self.needs_redraw = true; + self.alterables.needs_redraw = true; + } + + pub fn set_style(&mut self, node_id: taffy::NodeId, style: taffy::Style) { + self.alterables.style_set_requests.push((node_id, style)); } pub fn mark_dirty(&mut self, node_id: taffy::NodeId) { - self.dirty_nodes.push(node_id); + self.alterables.dirty_nodes.push(node_id); } pub fn trigger_haptics(&mut self) { - self.trigger_haptics = true; + self.alterables.trigger_haptics = true; } - pub fn get_taffy_layout(&self) -> &taffy::Layout { - self.taffy_layout + pub fn get_tree(&self) -> &taffy::TaffyTree { + self.refs.tree + } + + pub fn animate(&mut self, animation: Animation) { + self.alterables.animations.push(animation); } } pub struct CallbackData<'a> { pub obj: &'a mut dyn WidgetObj, pub widget_data: &'a mut WidgetData, - pub animations: &'a mut Vec, pub widget_id: WidgetID, pub node_id: taffy::NodeId, pub metadata: CallbackMetadata, diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index 92f0114..8ce7aa2 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex}; use crate::{ animation::{self, Animations}, assets::AssetProvider, - event::{self, EventListenerCollection}, + event::{self, EventAlterables, EventListenerCollection, EventRefs}, transform_stack::{Transform, TransformStack}, widget::{self, EventParams, WidgetState, div::Div}, }; @@ -20,13 +20,6 @@ pub type BoxWidget = Arc>; pub type WidgetMap = HopSlotMap; pub type WidgetNodeMap = SecondaryMap; -struct PushEventState<'a> { - pub animations: &'a mut Vec, - pub transform_stack: &'a mut TransformStack, - pub needs_redraw: bool, - pub trigger_haptics: bool, -} - pub struct Layout { pub tree: TaffyTree, @@ -93,13 +86,12 @@ impl Layout { &self, listeners: &EventListenerCollection, parent_node_id: taffy::NodeId, - state: &mut PushEventState, event: &event::Event, - dirty_nodes: &mut Vec, + alterables: &mut EventAlterables, user_data: &mut (&mut U1, &mut U2), ) -> anyhow::Result<()> { for child_id in self.tree.child_ids(parent_node_id) { - self.push_event_widget(listeners, state, child_id, event, dirty_nodes, user_data)?; + self.push_event_widget(listeners, child_id, event, alterables, user_data)?; } Ok(()) @@ -108,10 +100,9 @@ impl Layout { fn push_event_widget( &self, listeners: &EventListenerCollection, - state: &mut PushEventState, node_id: taffy::NodeId, event: &event::Event, - dirty_nodes: &mut Vec, + alterables: &mut EventAlterables, user_data: &mut (&mut U1, &mut U2), ) -> anyhow::Result<()> { let l = self.tree.layout(node_id)?; @@ -134,30 +125,24 @@ impl Layout { transform: glam::Mat4::IDENTITY, // TODO: event transformations? Not needed for now }; - state.transform_stack.push(transform); + alterables.transform_stack.push(transform); let mut iter_children = true; + let mut params = EventParams { + refs: &EventRefs { + tree: &self.tree, + widget_map: &self.widget_map, + widget_node_map: &self.widget_node_map, + }, + layout: l, + alterables, + node_id, + style, + }; + if let Some(listeners) = listeners.get(widget_id) { - match widget.process_event( - widget_id, - listeners, - node_id, - event, - user_data, - &mut EventParams { - transform_stack: state.transform_stack, - widgets: &self.widget_map, - tree: &self.tree, - animations: state.animations, - needs_redraw: &mut state.needs_redraw, - trigger_haptics: &mut state.trigger_haptics, - node_id, - style, - taffy_layout: l, - dirty_nodes, - }, - ) { + match widget.process_event(widget_id, listeners, node_id, event, user_data, &mut params) { widget::EventResult::Pass => { // go on } @@ -173,10 +158,10 @@ impl Layout { drop(widget); // free mutex if iter_children { - self.push_event_children(listeners, node_id, state, event, dirty_nodes, user_data)?; + self.push_event_children(listeners, node_id, event, alterables, user_data)?; } - state.transform_stack.pop(); + alterables.transform_stack.pop(); Ok(()) } @@ -205,44 +190,17 @@ impl Layout { event: &event::Event, mut user_data: (&mut U1, &mut U2), ) -> anyhow::Result<()> { - let mut transform_stack = TransformStack::new(); - let mut animations_to_add = Vec::::new(); - let mut dirty_nodes = Vec::new(); - - let mut state = PushEventState { - transform_stack: &mut transform_stack, - animations: &mut animations_to_add, - needs_redraw: false, - trigger_haptics: false, - }; + let mut alterables = EventAlterables::default(); self.push_event_widget( listeners, - &mut state, self.root_node, event, - &mut dirty_nodes, + &mut alterables, &mut user_data, )?; - for node in dirty_nodes { - self.tree.mark_dirty(node)?; - } - - if state.trigger_haptics { - self.haptics_triggered = true; - } - - if state.needs_redraw { - self.needs_redraw = true; - } - - if !animations_to_add.is_empty() { - self.needs_redraw = true; - for anim in animations_to_add { - self.animations.add(anim); - } - } + self.process_alterables(alterables)?; Ok(()) } @@ -280,20 +238,19 @@ impl Layout { } pub fn update(&mut self, size: Vec2, timestep_alpha: f32) -> anyhow::Result<()> { - let mut dirty_nodes = Vec::new(); + let mut alterables = EventAlterables::default(); - self.animations.process( - &self.widget_map, - &self.widget_node_map, - &self.tree, - &mut dirty_nodes, - timestep_alpha, - &mut self.needs_redraw, - ); + let refs = EventRefs { + tree: &self.tree, + widget_map: &self.widget_map, + widget_node_map: &self.widget_node_map, + }; - for node in dirty_nodes { - self.tree.mark_dirty(node)?; - } + self + .animations + .process(&refs, &mut alterables, timestep_alpha); + + self.process_alterables(alterables)?; if self.tree.dirty(self.root_node)? || self.prev_size != size { self.needs_redraw = true; @@ -343,20 +300,50 @@ impl Layout { } pub fn tick(&mut self) -> anyhow::Result<()> { - let mut dirty_nodes = Vec::new(); + let mut alterables = EventAlterables::default(); - self.animations.tick( - &self.widget_map, - &self.widget_node_map, - &self.tree, - &mut dirty_nodes, - &mut self.needs_redraw, - ); + let refs = EventRefs { + tree: &self.tree, + widget_map: &self.widget_map, + widget_node_map: &self.widget_node_map, + }; - for node in dirty_nodes { + self.animations.tick(&refs, &mut alterables); + self.process_alterables(alterables)?; + + Ok(()) + } + + fn process_alterables(&mut self, alterables: EventAlterables) -> anyhow::Result<()> { + for node in alterables.dirty_nodes { self.tree.mark_dirty(node)?; } + if alterables.needs_redraw { + self.needs_redraw = true; + } + + if alterables.trigger_haptics { + self.haptics_triggered = true; + } + + if !alterables.animations.is_empty() { + self.needs_redraw = true; + for anim in alterables.animations { + self.animations.add(anim); + } + } + + for request in alterables.style_set_requests { + if let Err(e) = self.tree.set_style(request.0, request.1) { + log::error!( + "failed to set style for taffy widget ID {:?}: {:?}", + request.0, + e + ); + } + } + Ok(()) } -} \ No newline at end of file +} diff --git a/wgui/src/widget/mod.rs b/wgui/src/widget/mod.rs index 7206d1e..6f8f7f9 100644 --- a/wgui/src/widget/mod.rs +++ b/wgui/src/widget/mod.rs @@ -2,14 +2,13 @@ use glam::Vec2; use super::drawing::RenderPrimitive; use crate::{ - animation, any::AnyTrait, drawing, event::{ - CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventListener, EventListenerKind, - MouseWheelEvent, + CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventAlterables, EventListener, + EventListenerKind, EventRefs, MouseWheelEvent, }, - layout::{Layout, WidgetID, WidgetMap}, + layout::{Layout, WidgetID}, transform_stack::TransformStack, }; @@ -124,14 +123,15 @@ pub trait WidgetObj: AnyTrait { pub struct EventParams<'a> { pub node_id: taffy::NodeId, pub style: &'a taffy::Style, - pub taffy_layout: &'a taffy::Layout, - pub widgets: &'a WidgetMap, - pub tree: &'a taffy::TaffyTree, - pub transform_stack: &'a TransformStack, - pub animations: &'a mut Vec, - pub needs_redraw: &'a mut bool, - pub trigger_haptics: &'a mut bool, - pub dirty_nodes: &'a mut Vec, + pub refs: &'a EventRefs<'a>, + pub alterables: &'a mut EventAlterables, + pub layout: &'a taffy::Layout, +} + +impl EventParams<'_> { + pub fn mark_redraw(&mut self) { + self.alterables.needs_redraw = true; + } } pub enum EventResult { @@ -189,27 +189,17 @@ macro_rules! call_event { let mut data = CallbackData { obj: $self.obj.as_mut(), widget_data: &mut $self.data, - animations: $params.animations, $widget_id, $node_id, metadata: $metadata, }; let mut common = CallbackDataCommon { - widgets: $params.widgets, - needs_redraw: false, - trigger_haptics: false, - dirty_nodes: $params.dirty_nodes, - taffy_layout: $params.taffy_layout, + refs: $params.refs, + alterables: $params.alterables, }; callback(&mut common, &mut data, $user_data.0, $user_data.1); - if common.trigger_haptics { - *$params.trigger_haptics = true; - } - if common.needs_redraw { - *$params.needs_redraw = true; - } } } }; @@ -290,13 +280,13 @@ impl WidgetState { return false; } - let l = params.taffy_layout; + let l = params.layout; let overflow = Vec2::new(l.scroll_width(), l.scroll_height()); if overflow.x == 0.0 && overflow.y == 0.0 { return false; // not overflowing } - let Some(info) = get_scrollbar_info(params.taffy_layout) else { + let Some(info) = get_scrollbar_info(params.layout) else { return false; }; @@ -308,7 +298,7 @@ impl WidgetState { let new_scroll = (self.data.scrolling.x + wheel.shift.x * mult).clamp(0.0, 1.0); if self.data.scrolling.x != new_scroll { self.data.scrolling.x = new_scroll; - *params.needs_redraw = true; + params.mark_redraw(); } } @@ -318,23 +308,23 @@ impl WidgetState { let new_scroll = (self.data.scrolling.y + wheel.shift.y * mult).clamp(0.0, 1.0); if self.data.scrolling.y != new_scroll { self.data.scrolling.y = new_scroll; - *params.needs_redraw = true; + params.mark_redraw(); } } true } - pub fn process_event( + pub fn process_event<'a, U1, U2>( &mut self, widget_id: WidgetID, listeners: &[EventListener], node_id: taffy::NodeId, event: &Event, user_data: &mut (&mut U1, &mut U2), - params: &mut EventParams, + params: &'a mut EventParams<'a>, ) -> EventResult { - let hovered = event.test_mouse_within_transform(params.transform_stack.get()); + let hovered = event.test_mouse_within_transform(params.alterables.transform_stack.get()); match &event { Event::MouseDown(e) => { diff --git a/wlx-overlay-s/src/overlays/keyboard/builder.rs b/wlx-overlay-s/src/overlays/keyboard/builder.rs index e3a62af..fc0938a 100644 --- a/wlx-overlay-s/src/overlays/keyboard/builder.rs +++ b/wlx-overlay-s/src/overlays/keyboard/builder.rs @@ -193,7 +193,7 @@ where let k = key_state.clone(); move |common, data, _app, _state| { common.trigger_haptics(); - on_enter_anim(k.clone(), data); + on_enter_anim(k.clone(), common, data); } }), ); @@ -204,7 +204,7 @@ where let k = key_state.clone(); move |common, data, _app, _state| { common.trigger_haptics(); - on_leave_anim(k.clone(), data); + on_leave_anim(k.clone(), common, data); } }), ); @@ -299,8 +299,12 @@ fn set_anim_color(key_state: &KeyState, rect: &mut Rectangle, pos: f32) { rect.params.color2.b = key_state.color2.b + br2; } -fn on_enter_anim(key_state: Rc, data: &mut event::CallbackData) { - data.animations.push(Animation::new( +fn on_enter_anim( + key_state: Rc, + common: &mut event::CallbackDataCommon, + data: &event::CallbackData, +) { + common.animate(Animation::new( data.widget_id, 10, AnimationEasing::OutBack, @@ -313,8 +317,12 @@ fn on_enter_anim(key_state: Rc, data: &mut event::CallbackData) { )); } -fn on_leave_anim(key_state: Rc, data: &mut event::CallbackData) { - data.animations.push(Animation::new( +fn on_leave_anim( + key_state: Rc, + common: &mut event::CallbackDataCommon, + data: &event::CallbackData, +) { + common.animate(Animation::new( data.widget_id, 15, AnimationEasing::OutQuad,