diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index 0262abf..83dfe39 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -13,6 +13,7 @@ use wgui::{ checkbox::ComponentCheckbox, }, drawing::Color, + event::StyleSetRequest, font_config::WguiFontConfig, globals::WguiGlobals, i18n::Translation, @@ -123,17 +124,14 @@ impl TestbedGeneric { 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); + common.alterables.set_style( + div_visibility.id, + StyleSetRequest::Display(if evt.checked { + taffy::Display::Flex + } else { + taffy::Display::None + }), + ); Ok(()) })); diff --git a/wgui/src/components/slider.rs b/wgui/src/components/slider.rs index f0a22bb..2672716 100644 --- a/wgui/src/components/slider.rs +++ b/wgui/src/components/slider.rs @@ -7,7 +7,7 @@ use crate::{ animation::{Animation, AnimationEasing}, components::{Component, ComponentBase, ComponentTrait, RefreshData}, drawing::{self}, - event::{self, CallbackDataCommon, EventListenerCollection, EventListenerKind}, + event::{self, CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerKind, StyleSetRequest}, i18n::Translation, layout::{WidgetID, WidgetPair}, renderer_vk::{ @@ -76,13 +76,11 @@ struct State { } struct Data { - #[allow(dead_code)] - body: WidgetID, // Div + body_node: taffy::NodeId, slider_handle_rect_id: WidgetID, // Rectangle slider_text_id: WidgetID, // Text - slider_handle: WidgetPair, + slider_handle_id: WidgetID, slider_handle_node_id: taffy::NodeId, - slider_body_node: taffy::NodeId, } pub struct SliderValueChangedEvent { @@ -140,26 +138,31 @@ fn get_width(slider_body_node: taffy::NodeId, tree: &taffy::tree::TaffyTree, -) -> Option { +) -> bool { + /* returns false if nothing changed */ let norm = values.to_normalized(); // 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 new_percent = percent(percent_margin + norm * (1.0 - percent_margin * 2.0)); if slider_handle_style.margin.left == new_percent { - None // nothing changed - } else { - let mut new_style = slider_handle_style.clone(); - new_style.margin.left = new_percent; - Some(new_style) + return false; // nothing changed } + + 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; @@ -180,7 +183,7 @@ impl State { let norm = map_mouse_x_to_normalized( 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); @@ -206,13 +209,19 @@ impl State { let changed = self.values.value != before; 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 { - return; //nothing changed visually - }; + if !conf_handle_style( + common.alterables, + &self.values, + data.slider_handle_id, + data.body_node, + style, + &common.state.tree, + ) { + return; // nothing changed visually + } common.alterables.mark_dirty(data.slider_handle_node_id); common.alterables.mark_redraw(); - common.alterables.set_style(data.slider_handle.id, new_style); if let Some(mut label) = common.state.widgets.get_as::(data.slider_text_id) { 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 { - body: body_id, - slider_handle, 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_text_id: slider_text.id, }); diff --git a/wgui/src/event.rs b/wgui/src/event.rs index cc361db..05d3223 100644 --- a/wgui/src/event.rs +++ b/wgui/src/event.rs @@ -92,11 +92,16 @@ impl Event { } } +pub enum StyleSetRequest { + Display(taffy::Display), + Margin(taffy::Rect), +} + // alterables which will be dispatched in the next loop iteration phase #[derive(Default)] pub struct EventAlterables { pub dirty_nodes: Vec, - pub style_set_requests: Vec<(WidgetID, taffy::Style)>, + pub style_set_requests: Vec<(WidgetID, StyleSetRequest)>, 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 +117,8 @@ impl EventAlterables { self.needs_redraw = true; } - pub fn set_style(&mut self, widget_id: WidgetID, style: taffy::Style) { - self.style_set_requests.push((widget_id, style)); + pub fn set_style(&mut self, widget_id: WidgetID, request: StyleSetRequest) { + self.style_set_requests.push((widget_id, request)); } pub fn mark_dirty(&mut self, node_id: taffy::NodeId) { diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index 67d4876..b5ea5bc 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -721,20 +721,33 @@ impl Layout { } } - for (widget_id, new_style) in alterables.style_set_requests { - if let Some(node_id) = self.state.nodes.get(widget_id) { - let old_style = self.state.tree.style(*node_id).unwrap() /* always safe */; + for (widget_id, style_request) in alterables.style_set_requests { + let Some(node_id) = self.state.nodes.get(widget_id) else { + continue; + }; - // refresh the component in case if visibility/display mode has changed - if old_style.display != new_style.display - && let Some(component) = self.registered_components_to_refresh.get(node_id) - { - self.components_to_refresh_once.insert(component.clone()); - } + // 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 */; - if let Err(e) = self.state.tree.set_style(*node_id, new_style) { - log::error!("failed to set style for taffy widget ID {node_id:?}: {e:?}"); + match style_request { + event::StyleSetRequest::Display(display) => { + // refresh the component in case if visibility/display mode has changed + if cur_style.display != display + && let Some(component) = self.registered_components_to_refresh.get(node_id) + { + self.components_to_refresh_once.insert(component.clone()); + } + + cur_style.display = display; } + 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:?}"); } } diff --git a/wlx-overlay-s/src/gui/panel/mod.rs b/wlx-overlay-s/src/gui/panel/mod.rs index f6a4ac4..bc08300 100644 --- a/wlx-overlay-s/src/gui/panel/mod.rs +++ b/wlx-overlay-s/src/gui/panel/mod.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, rc::Rc}; use button::setup_custom_button; -use glam::{vec2, Affine2, Vec2}; +use glam::{Affine2, Vec2, vec2}; use label::setup_custom_label; use wgui::{ assets::AssetPath, @@ -9,14 +9,14 @@ use wgui::{ event::{ Event as WguiEvent, EventAlterables, EventCallback, EventListenerID, EventListenerKind, InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent, - MouseMotionEvent, MouseUpEvent, MouseWheelEvent, + MouseMotionEvent, MouseUpEvent, MouseWheelEvent, StyleSetRequest, }, gfx::cmd::WGfxClearMode, layout::{Layout, LayoutParams, WidgetID}, parser::{CustomAttribsInfoOwned, ParserState}, renderer_vk::context::Context as WguiContext, taffy, - widget::{label::WidgetLabel, rectangle::WidgetRectangle, EventResult}, + widget::{EventResult, label::WidgetLabel, rectangle::WidgetRectangle}, }; use crate::{ @@ -24,7 +24,7 @@ use crate::{ state::AppState, subsystem::hid::WheelDelta, windowing::backend::{ - ui_transform, FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender, + FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender, ui_transform, }, }; @@ -228,14 +228,7 @@ impl GuiPanel { display: taffy::Display, alterables: &mut EventAlterables, ) { - let mut style = self - .layout - .state - .get_widget_style(widget_id) - .unwrap_or(&taffy::Style::DEFAULT) - .clone(); - style.display = display; - alterables.set_style(widget_id, style); + alterables.set_style(widget_id, StyleSetRequest::Display(display)); } } diff --git a/wlx-overlay-s/src/overlays/edit/tab.rs b/wlx-overlay-s/src/overlays/edit/tab.rs index da27372..8399802 100644 --- a/wlx-overlay-s/src/overlays/edit/tab.rs +++ b/wlx-overlay-s/src/overlays/edit/tab.rs @@ -1,8 +1,11 @@ use std::{collections::HashMap, rc::Rc}; use wgui::{ - components::button::ComponentButton, event::CallbackDataCommon, layout::WidgetID, - parser::Fetchable, taffy, + components::button::ComponentButton, + event::{CallbackDataCommon, StyleSetRequest}, + layout::WidgetID, + parser::Fetchable, + taffy, }; use crate::gui::panel::GuiPanel; @@ -76,19 +79,15 @@ impl ButtonPaneTabSwitcher { } fn set_tab_active(common: &mut CallbackDataCommon, data: &TabData, active: bool) { - let mut style = common - .state - .get_widget_style(data.pane) - .unwrap_or(&taffy::Style::DEFAULT) - .clone(); + common.alterables.set_style( + data.pane, + StyleSetRequest::Display(if active { + taffy::Display::Block + } else { + taffy::Display::None + }), + ); - style.display = if active { - taffy::Display::Block - } else { - taffy::Display::None - }; - - common.alterables.set_style(data.pane, style); if let Some(button) = data.button.as_ref() { button.set_sticky_state(common, active); }