wgui: Refactoring, various changes (see desc)

- use parking_lot for mutex (less restrictive and easier to use)
- simplify event callbacks and widget type casting
- defer component initialization at start (required for setting the initial state of sliders)
- fix non-working scroll events
- update testbed.xml
- replace slider with the real one in bar.xml
- show slider text on its handle
This commit is contained in:
Aleksander
2025-07-29 22:12:01 +02:00
parent f950273a2d
commit 4e46c45bcf
21 changed files with 450 additions and 334 deletions

View File

@@ -1,14 +1,16 @@
use std::sync::{Arc, Mutex};
use std::{collections::VecDeque, rc::Rc, sync::Arc};
use crate::{
animation::{self, Animations},
animation::Animations,
assets::AssetProvider,
components::{Component, InitData},
event::{self, EventAlterables, EventListenerCollection, EventRefs},
transform_stack::{Transform, TransformStack},
widget::{self, EventParams, WidgetState, div::Div},
transform_stack::Transform,
widget::{self, EventParams, WidgetObj, WidgetState, div::Div},
};
use glam::{Vec2, vec2};
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
use slotmap::{HopSlotMap, SecondaryMap, new_key_type};
use taffy::{TaffyTree, TraversePartialTree};
@@ -17,13 +19,52 @@ new_key_type! {
}
pub type BoxWidget = Arc<Mutex<WidgetState>>;
pub type WidgetMap = HopSlotMap<WidgetID, BoxWidget>;
pub struct WidgetMap(HopSlotMap<WidgetID, BoxWidget>);
pub type WidgetNodeMap = SecondaryMap<WidgetID, taffy::NodeId>;
impl WidgetMap {
fn new() -> Self {
Self(HopSlotMap::with_key())
}
pub fn get_as<T: 'static>(&self, handle: WidgetID) -> Option<MappedMutexGuard<T>> {
let widget = self.0.get(handle)?;
Some(MutexGuard::map(widget.lock(), |w| w.obj.get_as_mut::<T>()))
}
pub fn get(&self, handle: WidgetID) -> Option<&BoxWidget> {
self.0.get(handle)
}
pub fn insert(&mut self, obj: BoxWidget) -> WidgetID {
self.0.insert(obj)
}
// cast to specific widget type, does nothing if widget ID is expired
// panics in case if the widget type is wrong
pub fn call<WIDGET, FUNC>(&self, widget_id: WidgetID, func: FUNC)
where
WIDGET: WidgetObj,
FUNC: FnOnce(&mut WIDGET),
{
let Some(widget) = self.get(widget_id) else {
debug_assert!(false);
return;
};
let mut lock = widget.lock();
let m = lock.obj.get_as_mut::<WIDGET>();
func(m);
}
}
pub struct Layout {
pub tree: TaffyTree<WidgetID>,
pub assets: Box<dyn AssetProvider>,
pub components_to_init: VecDeque<Rc<dyn Component>>,
pub widget_map: WidgetMap,
pub widget_node_map: WidgetNodeMap,
@@ -82,6 +123,26 @@ impl Layout {
)
}
fn process_pending_components(&mut self) -> anyhow::Result<()> {
let mut alterables = EventAlterables::default();
while let Some(c) = self.components_to_init.pop_front() {
c.init(&mut InitData {
widgets: &self.widget_map,
alterables: &mut alterables,
tree: &self.tree,
});
}
self.process_alterables(alterables)?;
Ok(())
}
pub fn defer_component_init(&mut self, component: Rc<dyn Component>) {
self.components_to_init.push_back(component);
}
fn push_event_children<U1, U2>(
&self,
listeners: &EventListenerCollection<U1, U2>,
@@ -117,7 +178,7 @@ impl Layout {
anyhow::bail!("invalid widget");
};
let mut widget = widget.lock().unwrap();
let mut widget = widget.lock();
let transform = Transform {
pos: Vec2::new(l.location.x, l.location.y),
@@ -132,8 +193,8 @@ impl Layout {
let mut params = EventParams {
refs: &EventRefs {
tree: &self.tree,
widget_map: &self.widget_map,
widget_node_map: &self.widget_node_map,
widgets: &self.widget_map,
nodes: &self.widget_node_map,
},
layout: l,
alterables,
@@ -141,20 +202,27 @@ impl Layout {
style,
};
if let Some(listeners) = listeners.get(widget_id) {
match widget.process_event(widget_id, listeners, node_id, event, user_data, &mut params) {
widget::EventResult::Pass => {
// go on
}
widget::EventResult::Consumed => {
iter_children = false;
}
widget::EventResult::Outside => {
iter_children = false;
}
widget::EventResult::Unused => {
iter_children = false;
}
let listeners_vec = listeners.get(widget_id);
match widget.process_event(
widget_id,
listeners_vec,
node_id,
event,
user_data,
&mut params,
) {
widget::EventResult::Pass => {
// go on
}
widget::EventResult::Consumed => {
iter_children = false;
}
widget::EventResult::Outside => {
iter_children = false;
}
widget::EventResult::Unused => {
iter_children = false;
}
}
@@ -213,7 +281,7 @@ impl Layout {
pub fn new(assets: Box<dyn AssetProvider>) -> anyhow::Result<Self> {
let mut tree = TaffyTree::new();
let mut widget_node_map = WidgetNodeMap::default();
let mut widget_map = HopSlotMap::with_key();
let mut widget_map = WidgetMap::new();
let (root_widget, root_node) = add_child_internal(
&mut tree,
@@ -239,6 +307,7 @@ impl Layout {
haptics_triggered: false,
animations: Animations::default(),
assets,
components_to_init: VecDeque::new(),
})
}
@@ -247,8 +316,8 @@ impl Layout {
let refs = EventRefs {
tree: &self.tree,
widget_map: &self.widget_map,
widget_node_map: &self.widget_node_map,
widgets: &self.widget_map,
nodes: &self.widget_node_map,
};
self
@@ -280,10 +349,7 @@ impl Layout {
None => taffy::Size::ZERO,
Some(h) => {
if let Some(w) = self.widget_map.get(*h) {
w.lock()
.unwrap()
.obj
.measure(known_dimensions, available_space)
w.lock().obj.measure(known_dimensions, available_space)
} else {
taffy::Size::ZERO
}
@@ -309,12 +375,13 @@ impl Layout {
let refs = EventRefs {
tree: &self.tree,
widget_map: &self.widget_map,
widget_node_map: &self.widget_node_map,
widgets: &self.widget_map,
nodes: &self.widget_node_map,
};
self.animations.tick(&refs, &mut alterables);
self.process_alterables(alterables)?;
self.process_pending_components()?;
Ok(())
}