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

@@ -3,7 +3,7 @@ use taffy::{AlignItems, JustifyContent, prelude::length};
use crate::{
animation::{Animation, AnimationEasing},
components::Component,
components::{Component, InitData},
drawing::{self, Color},
event::{CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec},
layout::{Layout, WidgetID},
@@ -50,15 +50,20 @@ pub struct Button {
listener_handles: ListenerHandleVec,
}
impl Component for Button {}
impl Component for Button {
fn init(&self, _data: &mut InitData) {}
}
impl Button {
pub fn set_text<C>(&self, callback_data: &mut CallbackDataCommon, text: &str) {
callback_data.call_on_widget(self.data.text_id, |label: &mut TextLabel| {
label.set_text(text);
});
callback_data.mark_redraw();
callback_data.mark_dirty(self.data.text_node);
pub fn set_text<C>(&self, common: &mut CallbackDataCommon, text: &str) {
common
.refs
.widgets
.call(self.data.text_id, |label: &mut TextLabel| {
label.set_text(text);
});
common.alterables.mark_redraw();
common.alterables.mark_dirty(self.data.text_node);
}
}
@@ -77,12 +82,12 @@ fn anim_hover(rect: &mut Rectangle, data: &Data, pos: f32) {
fn anim_hover_in(data: Rc<Data>, widget_id: WidgetID) -> Animation {
Animation::new(
widget_id,
10,
5,
AnimationEasing::OutQuad,
Box::new(move |common, anim_data| {
let rect = anim_data.obj.get_as_mut::<Rectangle>();
anim_hover(rect, &data, anim_data.pos);
common.mark_redraw();
common.alterables.mark_redraw();
}),
)
}
@@ -90,12 +95,12 @@ fn anim_hover_in(data: Rc<Data>, widget_id: WidgetID) -> Animation {
fn anim_hover_out(data: Rc<Data>, widget_id: WidgetID) -> Animation {
Animation::new(
widget_id,
15,
8,
AnimationEasing::OutQuad,
Box::new(move |common, anim_data| {
let rect = anim_data.obj.get_as_mut::<Rectangle>();
anim_hover(rect, &data, 1.0 - anim_data.pos);
common.mark_redraw();
common.alterables.mark_redraw();
}),
)
}
@@ -164,7 +169,9 @@ pub fn construct<U1, U2>(
rect_id,
EventListenerKind::MouseEnter,
Box::new(move |common, event_data, _, _| {
common.animate(anim_hover_in(data.clone(), event_data.widget_id));
common
.alterables
.animate(anim_hover_in(data.clone(), event_data.widget_id));
}),
);
@@ -174,12 +181,17 @@ pub fn construct<U1, U2>(
rect_id,
EventListenerKind::MouseLeave,
Box::new(move |common, event_data, _, _| {
common.animate(anim_hover_out(data.clone(), event_data.widget_id));
common
.alterables
.animate(anim_hover_out(data.clone(), event_data.widget_id));
}),
);
Ok(Rc::new(Button {
let button = Rc::new(Button {
data: _data.clone(),
listener_handles,
}))
});
layout.defer_component_init(button.clone());
Ok(button)
}

View File

@@ -1,6 +1,20 @@
use crate::any::AnyTrait;
use taffy::TaffyTree;
use crate::{
any::AnyTrait,
event::EventAlterables,
layout::{WidgetID, WidgetMap},
};
pub mod button;
pub mod slider;
pub trait Component: AnyTrait {}
pub struct InitData<'a> {
pub alterables: &'a mut EventAlterables,
pub widgets: &'a WidgetMap,
pub tree: &'a TaffyTree<WidgetID>,
}
pub trait Component: AnyTrait {
fn init(&self, data: &mut InitData);
}

View File

@@ -1,23 +1,28 @@
use std::{
cell::{RefCell, RefMut},
rc::Rc,
};
use std::{cell::RefCell, rc::Rc};
use glam::{Mat4, Vec2, Vec3};
use taffy::prelude::{length, percent};
use taffy::{
TaffyTree,
prelude::{length, percent},
};
use crate::{
animation::{Animation, AnimationEasing},
components::Component,
components::{Component, InitData},
drawing::{self},
event::{
self, CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec,
self, CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerKind,
ListenerHandleVec,
},
layout::{Layout, WidgetID, WidgetMap},
renderer_vk::{
text::{FontWeight, HorizontalAlign, TextStyle},
util,
},
layout::{Layout, WidgetID},
renderer_vk::util,
widget::{
div::Div,
rectangle::{Rectangle, RectangleParams},
text::{TextLabel, TextParams},
util::WLength,
},
};
@@ -53,8 +58,8 @@ pub struct SliderState {
struct Data {
body: WidgetID, // Div
slider_handle_id: WidgetID, // Div
slider_handle_rect_id: WidgetID, // Rectangle
slider_text_id: WidgetID, // Text
slider_handle_node: taffy::NodeId,
slider_body_node: taffy::NodeId,
}
@@ -62,10 +67,24 @@ struct Data {
pub struct Slider {
data: Rc<Data>,
state: Rc<RefCell<SliderState>>,
#[allow(dead_code)]
listener_handles: ListenerHandleVec,
}
impl Component for Slider {}
impl Component for Slider {
fn init(&self, init_data: &mut InitData) {
let mut state = self.state.borrow_mut();
let value = state.values.value;
state.set_value(
&self.data,
init_data.alterables,
init_data.widgets,
init_data.tree,
value,
);
}
}
// NOTICE: this can be re-used in the future
fn map_mouse_x_to_normalized(mouse_x_rel: f32, widget_width: f32) -> f32 {
@@ -109,34 +128,44 @@ impl SliderState {
let norm = map_mouse_x_to_normalized(
mouse_pos.x - HANDLE_WIDTH / 2.0,
get_width(data.slider_body_node, common.get_tree()) - HANDLE_WIDTH,
get_width(data.slider_body_node, common.refs.tree) - HANDLE_WIDTH,
);
let target_value = self.values.get_from_normalized(norm);
let val = target_value;
self.set_value(data, common, val);
self.set_value(
data,
common.alterables,
common.refs.widgets,
common.refs.tree,
val,
);
}
fn set_value(&mut self, data: &Data, common: &mut CallbackDataCommon, value: f32) {
fn update_text(&self, text: &mut TextLabel, value: f32) {
// round displayed value, should be sufficient for now
text.set_text(&format!("{}", value.round()));
}
fn set_value(
&mut self,
data: &Data,
alterables: &mut EventAlterables,
widgets: &WidgetMap,
tree: &TaffyTree<WidgetID>,
value: f32,
) {
//common.call_on_widget(data.slider_handle_id, |_div: &mut Div| {});
self.values.value = value;
let mut style = common
.refs
.tree
.style(data.slider_handle_node)
.unwrap()
.clone();
conf_handle_style(
&self.values,
data.slider_body_node,
&mut style,
common.get_tree(),
);
common.mark_dirty(data.slider_handle_node);
common.mark_redraw();
common.set_style(data.slider_handle_node, style);
let mut style = tree.style(data.slider_handle_node).unwrap().clone();
conf_handle_style(&self.values, data.slider_body_node, &mut style, tree);
alterables.mark_dirty(data.slider_handle_node);
alterables.mark_redraw();
alterables.set_style(data.slider_handle_node, style);
widgets.call(data.slider_text_id, |label: &mut TextLabel| {
self.update_text(label, value);
});
}
}
@@ -162,21 +191,21 @@ fn anim_rect(rect: &mut Rectangle, pos: f32) {
}
fn on_enter_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
common.animate(Animation::new(
common.alterables.animate(Animation::new(
handle_id,
5,
AnimationEasing::OutQuad,
20,
AnimationEasing::OutBack,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<Rectangle>();
data.data.transform = get_anim_transform(data.pos, data.widget_size);
anim_rect(rect, data.pos);
common.mark_redraw();
common.alterables.mark_redraw();
}),
));
}
fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
common.animate(Animation::new(
common.alterables.animate(Animation::new(
handle_id,
10,
AnimationEasing::OutQuad,
@@ -184,7 +213,7 @@ fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
let rect = data.obj.get_as_mut::<Rectangle>();
data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_size);
anim_rect(rect, 1.0 - data.pos);
common.mark_redraw();
common.alterables.mark_redraw();
}),
));
}
@@ -200,7 +229,7 @@ fn register_event_mouse_enter<U1, U2>(
data.body,
EventListenerKind::MouseEnter,
Box::new(move |common, _data, _, _| {
common.trigger_haptics();
common.alterables.trigger_haptics();
state.borrow_mut().hovered = true;
on_enter_anim(common, data.slider_handle_rect_id);
}),
@@ -218,7 +247,7 @@ fn register_event_mouse_leave<U1, U2>(
data.body,
EventListenerKind::MouseLeave,
Box::new(move |common, _data, _, _| {
common.trigger_haptics();
common.alterables.trigger_haptics();
state.borrow_mut().hovered = false;
on_leave_anim(common, data.slider_handle_rect_id);
}),
@@ -256,7 +285,7 @@ fn register_event_mouse_press<U1, U2>(
data.body,
EventListenerKind::MousePress,
Box::new(move |common, event_data, _, _| {
common.trigger_haptics();
common.alterables.trigger_haptics();
let mut state = state.borrow_mut();
if state.hovered {
@@ -278,7 +307,7 @@ fn register_event_mouse_release<U1, U2>(
data.body,
EventListenerKind::MouseRelease,
Box::new(move |common, _data, _, _| {
common.trigger_haptics();
common.alterables.trigger_haptics();
let mut state = state.borrow_mut();
if state.dragging {
@@ -340,14 +369,6 @@ pub fn construct<U1, U2>(
..Default::default()
};
// TODO: dispatch style config after this taffy tree did a re-layout
/*conf_handle_style(
&params.values,
slider_body_node,
&mut slider_handle_style,
&layout.tree,
);*/
// invisible outer handle body
let (slider_handle_id, slider_handle_node) =
layout.add_child(body_id, Div::create()?, slider_handle_style)?;
@@ -371,19 +392,34 @@ pub fn construct<U1, U2>(
},
)?;
let state = SliderState {
dragging: false,
hovered: false,
values: params.values,
};
let (slider_text_id, _) = layout.add_child(
slider_handle_id,
TextLabel::create(TextParams {
content: String::new(),
style: TextStyle {
weight: Some(FontWeight::Bold),
align: Some(HorizontalAlign::Center),
..Default::default()
},
})?,
Default::default(),
)?;
let data = Rc::new(Data {
body: body_id,
slider_handle_node,
slider_handle_rect_id,
slider_handle_id,
slider_body_node,
slider_text_id,
});
let state = Rc::new(RefCell::new(SliderState {
dragging: false,
hovered: false,
values: params.values,
}));
let state = Rc::new(RefCell::new(state));
let mut lhandles = ListenerHandleVec::default();
@@ -400,5 +436,6 @@ pub fn construct<U1, U2>(
listener_handles: lhandles,
});
layout.defer_component_init(slider.clone());
Ok(slider)
}