wgui: StyleSetRequest

This commit is contained in:
Aleksander
2025-11-26 22:49:50 +01:00
parent 85eab33c94
commit 9696d6730d
6 changed files with 88 additions and 72 deletions

View File

@@ -13,6 +13,7 @@ use wgui::{
checkbox::ComponentCheckbox, checkbox::ComponentCheckbox,
}, },
drawing::Color, drawing::Color,
event::StyleSetRequest,
font_config::WguiFontConfig, font_config::WguiFontConfig,
globals::WguiGlobals, globals::WguiGlobals,
i18n::Translation, i18n::Translation,
@@ -123,17 +124,14 @@ impl TestbedGeneric {
let div_visibility = state.fetch_widget(&layout.state, "div_visibility")?; let div_visibility = state.fetch_widget(&layout.state, "div_visibility")?;
cb_visible.on_toggle(Box::new(move |common, evt| { cb_visible.on_toggle(Box::new(move |common, evt| {
let mut style = common common.alterables.set_style(
.state div_visibility.id,
.get_widget_style(div_visibility.id) StyleSetRequest::Display(if evt.checked {
.unwrap()
.clone();
style.display = if evt.checked {
taffy::Display::Flex taffy::Display::Flex
} else { } else {
taffy::Display::None taffy::Display::None
}; }),
common.alterables.set_style(div_visibility.id, style); );
Ok(()) Ok(())
})); }));

View File

@@ -7,7 +7,7 @@ use crate::{
animation::{Animation, AnimationEasing}, animation::{Animation, AnimationEasing},
components::{Component, ComponentBase, ComponentTrait, RefreshData}, components::{Component, ComponentBase, ComponentTrait, RefreshData},
drawing::{self}, drawing::{self},
event::{self, CallbackDataCommon, EventListenerCollection, EventListenerKind}, event::{self, CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerKind, StyleSetRequest},
i18n::Translation, i18n::Translation,
layout::{WidgetID, WidgetPair}, layout::{WidgetID, WidgetPair},
renderer_vk::{ renderer_vk::{
@@ -76,13 +76,11 @@ struct State {
} }
struct Data { struct Data {
#[allow(dead_code)] body_node: taffy::NodeId,
body: WidgetID, // Div
slider_handle_rect_id: WidgetID, // Rectangle slider_handle_rect_id: WidgetID, // Rectangle
slider_text_id: WidgetID, // Text slider_text_id: WidgetID, // Text
slider_handle: WidgetPair, slider_handle_id: WidgetID,
slider_handle_node_id: taffy::NodeId, slider_handle_node_id: taffy::NodeId,
slider_body_node: taffy::NodeId,
} }
pub struct SliderValueChangedEvent { pub struct SliderValueChangedEvent {
@@ -140,26 +138,31 @@ fn get_width(slider_body_node: taffy::NodeId, tree: &taffy::tree::TaffyTree<Widg
} }
fn conf_handle_style( fn conf_handle_style(
alterables: &mut EventAlterables,
values: &ValuesMinMax, values: &ValuesMinMax,
slider_body_node: taffy::NodeId, slider_handle_id: WidgetID,
body_node: taffy::NodeId,
slider_handle_style: &taffy::Style, slider_handle_style: &taffy::Style,
tree: &taffy::tree::TaffyTree<WidgetID>, tree: &taffy::tree::TaffyTree<WidgetID>,
) -> Option<taffy::Style> { ) -> bool {
/* returns false if nothing changed */
let norm = values.to_normalized(); let norm = values.to_normalized();
// convert normalized value to taffy percentage margin in percent // convert normalized value to taffy percentage margin in percent
let width = get_width(slider_body_node, tree); let width = get_width(body_node, tree);
let percent_margin = (HANDLE_WIDTH / width) / 2.0; let percent_margin = (HANDLE_WIDTH / width) / 2.0;
let new_percent = percent(percent_margin + norm * (1.0 - percent_margin * 2.0)); let new_percent = percent(percent_margin + norm * (1.0 - percent_margin * 2.0));
if slider_handle_style.margin.left == new_percent { if slider_handle_style.margin.left == new_percent {
None // nothing changed return false; // nothing changed
} else {
let mut new_style = slider_handle_style.clone();
new_style.margin.left = new_percent;
Some(new_style)
} }
let mut margin = slider_handle_style.margin;
margin.left = new_percent;
alterables.set_style(slider_handle_id, StyleSetRequest::Margin(margin));
true
} }
const PAD_PERCENT: f32 = 0.75; const PAD_PERCENT: f32 = 0.75;
@@ -180,7 +183,7 @@ impl State {
let norm = map_mouse_x_to_normalized( let norm = map_mouse_x_to_normalized(
mouse_pos.x - HANDLE_WIDTH / 2.0, mouse_pos.x - HANDLE_WIDTH / 2.0,
get_width(data.slider_body_node, &common.state.tree) - HANDLE_WIDTH, get_width(data.body_node, &common.state.tree) - HANDLE_WIDTH,
); );
let target_value = self.values.get_from_normalized(norm); let target_value = self.values.get_from_normalized(norm);
@@ -206,13 +209,19 @@ impl State {
let changed = self.values.value != before; let changed = self.values.value != before;
let style = common.state.tree.style(data.slider_handle_node_id).unwrap(); let style = common.state.tree.style(data.slider_handle_node_id).unwrap();
let Some(new_style) = conf_handle_style(&self.values, data.slider_body_node, style, &common.state.tree) else { if !conf_handle_style(
common.alterables,
&self.values,
data.slider_handle_id,
data.body_node,
style,
&common.state.tree,
) {
return; // nothing changed visually return; // nothing changed visually
}; }
common.alterables.mark_dirty(data.slider_handle_node_id); common.alterables.mark_dirty(data.slider_handle_node_id);
common.alterables.mark_redraw(); common.alterables.mark_redraw();
common.alterables.set_style(data.slider_handle.id, new_style);
if let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(data.slider_text_id) { if let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(data.slider_text_id) {
Self::update_text(common, &mut label, self.values.value); Self::update_text(common, &mut label, self.values.value);
@@ -467,10 +476,9 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
)?; )?;
let data = Rc::new(Data { let data = Rc::new(Data {
body: body_id,
slider_handle,
slider_handle_rect_id: slider_handle_rect.id, slider_handle_rect_id: slider_handle_rect.id,
slider_body_node, body_node: slider_body_node,
slider_handle_id: slider_handle.id,
slider_handle_node_id, slider_handle_node_id,
slider_text_id: slider_text.id, slider_text_id: slider_text.id,
}); });

View File

@@ -92,11 +92,16 @@ impl Event {
} }
} }
pub enum StyleSetRequest {
Display(taffy::Display),
Margin(taffy::Rect<taffy::LengthPercentageAuto>),
}
// alterables which will be dispatched in the next loop iteration phase // alterables which will be dispatched in the next loop iteration phase
#[derive(Default)] #[derive(Default)]
pub struct EventAlterables { pub struct EventAlterables {
pub dirty_nodes: Vec<taffy::NodeId>, pub dirty_nodes: Vec<taffy::NodeId>,
pub style_set_requests: Vec<(WidgetID, taffy::Style)>, pub style_set_requests: Vec<(WidgetID, StyleSetRequest)>,
pub animations: Vec<animation::Animation>, pub animations: Vec<animation::Animation>,
pub widgets_to_tick: HashSet<WidgetID>, // widgets which needs to be ticked in the next `Layout::update()` fn pub widgets_to_tick: HashSet<WidgetID>, // widgets which needs to be ticked in the next `Layout::update()` fn
pub transform_stack: TransformStack, pub transform_stack: TransformStack,
@@ -112,8 +117,8 @@ impl EventAlterables {
self.needs_redraw = true; self.needs_redraw = true;
} }
pub fn set_style(&mut self, widget_id: WidgetID, style: taffy::Style) { pub fn set_style(&mut self, widget_id: WidgetID, request: StyleSetRequest) {
self.style_set_requests.push((widget_id, style)); self.style_set_requests.push((widget_id, request));
} }
pub fn mark_dirty(&mut self, node_id: taffy::NodeId) { pub fn mark_dirty(&mut self, node_id: taffy::NodeId) {

View File

@@ -721,20 +721,33 @@ impl Layout {
} }
} }
for (widget_id, new_style) in alterables.style_set_requests { for (widget_id, style_request) in alterables.style_set_requests {
if let Some(node_id) = self.state.nodes.get(widget_id) { let Some(node_id) = self.state.nodes.get(widget_id) else {
let old_style = self.state.tree.style(*node_id).unwrap() /* always safe */; continue;
};
// taffy requires us to copy this whole 536-byte style struct.
// we can't get `&mut Style` directly from taffy unfortunately
let mut cur_style = self.state.tree.style(*node_id).unwrap().clone() /* always safe */;
match style_request {
event::StyleSetRequest::Display(display) => {
// refresh the component in case if visibility/display mode has changed // refresh the component in case if visibility/display mode has changed
if old_style.display != new_style.display if cur_style.display != display
&& let Some(component) = self.registered_components_to_refresh.get(node_id) && let Some(component) = self.registered_components_to_refresh.get(node_id)
{ {
self.components_to_refresh_once.insert(component.clone()); self.components_to_refresh_once.insert(component.clone());
} }
if let Err(e) = self.state.tree.set_style(*node_id, new_style) { cur_style.display = display;
log::error!("failed to set style for taffy widget ID {node_id:?}: {e:?}");
} }
event::StyleSetRequest::Margin(margin) => {
cur_style.margin = margin;
}
}
if let Err(e) = self.state.tree.set_style(*node_id, cur_style) {
log::error!("failed to set style for taffy widget ID {node_id:?}: {e:?}");
} }
} }

View File

@@ -1,7 +1,7 @@
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use button::setup_custom_button; use button::setup_custom_button;
use glam::{vec2, Affine2, Vec2}; use glam::{Affine2, Vec2, vec2};
use label::setup_custom_label; use label::setup_custom_label;
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
@@ -9,14 +9,14 @@ use wgui::{
event::{ event::{
Event as WguiEvent, EventAlterables, EventCallback, EventListenerID, EventListenerKind, Event as WguiEvent, EventAlterables, EventCallback, EventListenerID, EventListenerKind,
InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent, InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent,
MouseMotionEvent, MouseUpEvent, MouseWheelEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent, StyleSetRequest,
}, },
gfx::cmd::WGfxClearMode, gfx::cmd::WGfxClearMode,
layout::{Layout, LayoutParams, WidgetID}, layout::{Layout, LayoutParams, WidgetID},
parser::{CustomAttribsInfoOwned, ParserState}, parser::{CustomAttribsInfoOwned, ParserState},
renderer_vk::context::Context as WguiContext, renderer_vk::context::Context as WguiContext,
taffy, taffy,
widget::{label::WidgetLabel, rectangle::WidgetRectangle, EventResult}, widget::{EventResult, label::WidgetLabel, rectangle::WidgetRectangle},
}; };
use crate::{ use crate::{
@@ -24,7 +24,7 @@ use crate::{
state::AppState, state::AppState,
subsystem::hid::WheelDelta, subsystem::hid::WheelDelta,
windowing::backend::{ windowing::backend::{
ui_transform, FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender, FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender, ui_transform,
}, },
}; };
@@ -228,14 +228,7 @@ impl<S: 'static> GuiPanel<S> {
display: taffy::Display, display: taffy::Display,
alterables: &mut EventAlterables, alterables: &mut EventAlterables,
) { ) {
let mut style = self alterables.set_style(widget_id, StyleSetRequest::Display(display));
.layout
.state
.get_widget_style(widget_id)
.unwrap_or(&taffy::Style::DEFAULT)
.clone();
style.display = display;
alterables.set_style(widget_id, style);
} }
} }

View File

@@ -1,8 +1,11 @@
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
use wgui::{ use wgui::{
components::button::ComponentButton, event::CallbackDataCommon, layout::WidgetID, components::button::ComponentButton,
parser::Fetchable, taffy, event::{CallbackDataCommon, StyleSetRequest},
layout::WidgetID,
parser::Fetchable,
taffy,
}; };
use crate::gui::panel::GuiPanel; use crate::gui::panel::GuiPanel;
@@ -76,19 +79,15 @@ impl ButtonPaneTabSwitcher {
} }
fn set_tab_active(common: &mut CallbackDataCommon, data: &TabData, active: bool) { fn set_tab_active(common: &mut CallbackDataCommon, data: &TabData, active: bool) {
let mut style = common common.alterables.set_style(
.state data.pane,
.get_widget_style(data.pane) StyleSetRequest::Display(if active {
.unwrap_or(&taffy::Style::DEFAULT)
.clone();
style.display = if active {
taffy::Display::Block taffy::Display::Block
} else { } else {
taffy::Display::None taffy::Display::None
}; }),
);
common.alterables.set_style(data.pane, style);
if let Some(button) = data.button.as_ref() { if let Some(button) = data.button.as_ref() {
button.set_sticky_state(common, active); button.set_sticky_state(common, active);
} }