From 77421b4e8210060e29a596c709d5df3a1f95be97 Mon Sep 17 00:00:00 2001 From: Aleksander Date: Mon, 7 Jul 2025 21:58:41 +0200 Subject: [PATCH] slider events and value setting --- uidev/src/main.rs | 8 +- wgui/src/components/slider.rs | 166 +++++++++++++++------ wgui/src/event.rs | 35 ++++- wgui/src/parser/component_slider.rs | 8 +- wgui/src/widget/mod.rs | 27 ++-- wlx-overlay-s/src/gui/panel.rs | 16 +- wlx-overlay-s/src/overlays/keyboard/mod.rs | 8 +- 7 files changed, 187 insertions(+), 81 deletions(-) diff --git a/uidev/src/main.rs b/uidev/src/main.rs index a325fe9..f1ccc5a 100644 --- a/uidev/src/main.rs +++ b/uidev/src/main.rs @@ -20,7 +20,7 @@ use vulkano::{ }; use wgui::{ event::{ - EventListenerCollection, MouseButton, MouseDownEvent, MouseMotionEvent, MouseUpEvent, + EventListenerCollection, MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent, }, gfx::WGfx, @@ -160,7 +160,7 @@ fn main() -> Result<(), Box> { &mut listeners, &wgui::event::Event::MouseDown(MouseDownEvent { pos: mouse / scale, - button: MouseButton::Left, + index: MouseButtonIndex::Left, device: 0, }), (&mut (), &mut ()), @@ -173,7 +173,7 @@ fn main() -> Result<(), Box> { &mut listeners, &wgui::event::Event::MouseUp(MouseUpEvent { pos: mouse / scale, - button: MouseButton::Left, + index: MouseButtonIndex::Left, device: 0, }), (&mut (), &mut ()), @@ -280,7 +280,7 @@ fn main() -> Result<(), Box> { return; } - log::trace!("drawing frame {}", frame_index); + log::trace!("drawing frame {frame_index}"); frame_index += 1; profiler.start(); diff --git a/wgui/src/components/slider.rs b/wgui/src/components/slider.rs index c8b9e9d..4b74fd1 100644 --- a/wgui/src/components/slider.rs +++ b/wgui/src/components/slider.rs @@ -22,30 +22,33 @@ use crate::{ }, }; -pub struct Params { - pub style: taffy::Style, - pub initial_value: f32, +#[derive(Default)] +pub struct ValuesMinMax { + pub value: f32, pub min_value: f32, pub max_value: f32, } -impl Default for Params { - fn default() -> Self { - Self { - style: Default::default(), - initial_value: 0.5, - min_value: 0.0, - max_value: 1.0, - } +impl ValuesMinMax { + fn to_normalized(&self) -> f32 { + (self.value - self.min_value) / (self.max_value - self.min_value) } + + fn get_from_normalized(&self, normalized: f32) -> f32 { + normalized * (self.max_value - self.min_value) + self.min_value + } +} + +#[derive(Default)] +pub struct Params { + pub style: taffy::Style, + pub values: ValuesMinMax, } pub struct SliderState { dragging: bool, hovered: bool, - value: f32, - min_value: f32, - max_value: f32, + values: ValuesMinMax, } struct Data { @@ -53,6 +56,7 @@ struct Data { slider_handle_id: WidgetID, // Div slider_handle_rect_id: WidgetID, // Rectangle slider_handle_node: taffy::NodeId, + slider_body_node: taffy::NodeId, } pub struct Slider { @@ -63,12 +67,58 @@ pub struct Slider { impl Component for Slider {} +// NOTICE: this can be re-used in the future +fn map_mouse_x_to_normalized(mouse_x_rel: f32, widget_width: f32) -> f32 { + (mouse_x_rel / widget_width).clamp(0.0, 1.0) +} + +fn get_width(slider_body_node: taffy::NodeId, tree: &taffy::tree::TaffyTree) -> f32 { + let layout = tree.layout(slider_body_node).unwrap(); /* shouldn't fail */ + layout.size.width +} + +fn conf_handle_style( + values: &ValuesMinMax, + slider_body_node: taffy::NodeId, + slider_handle_style: &mut taffy::Style, + tree: &taffy::tree::TaffyTree, +) { + let norm = values.to_normalized(); + + // convert normalized value to taffy percentage margin in percent + let width = get_width(slider_body_node, tree); + let percent_margin = (HANDLE_WIDTH / width) / 2.0; + slider_handle_style.margin.left = percent(percent_margin + norm * (1.0 - percent_margin * 2.0)); +} + +const PAD_PERCENT: f32 = 0.75; +const HANDLE_WIDTH: f32 = 32.0; +const HANDLE_HEIGHT: f32 = 24.0; + impl SliderState { + fn update_value_to_mouse( + &mut self, + event_data: &event::CallbackData<'_>, + data: &Data, + common: &mut CallbackDataCommon, + ) { + let mouse_pos = event_data + .metadata + .get_mouse_pos_relative(&common.alterables.transform_stack) + .unwrap(); // safe + + 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, + ); + let target_value = self.values.get_from_normalized(norm); + let val = target_value; + self.set_value(data, common, val); + } + fn set_value(&mut self, data: &Data, common: &mut CallbackDataCommon, value: f32) { - self.value = value; - common.mark_dirty(data.slider_handle_node); - common.call_on_widget(data.slider_handle_id, |div: &mut Div| {}); - common.mark_redraw(); + //common.call_on_widget(data.slider_handle_id, |_div: &mut Div| {}); + self.values.value = value; let mut style = common .refs @@ -77,9 +127,15 @@ impl SliderState { .unwrap() .clone(); - // todo - style.margin.left = percent(1.0); + 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); } } @@ -133,11 +189,6 @@ fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) { )); } -const PAD_PERCENT: f32 = 0.75; - -const HANDLE_WIDTH: f32 = 32.0; -const HANDLE_HEIGHT: f32 = 24.0; - fn register_event_mouse_enter( data: Rc, state: Rc>, @@ -176,7 +227,7 @@ fn register_event_mouse_leave( fn register_event_mouse_motion( data: Rc, - _state: Rc>, + state: Rc>, listeners: &mut EventListenerCollection, listener_handles: &mut ListenerHandleVec, ) { @@ -184,7 +235,13 @@ fn register_event_mouse_motion( listener_handles, data.body, EventListenerKind::MouseMotion, - Box::new(move |_common, _data, _, _| {}), + Box::new(move |common, event_data, _, _| { + let mut state = state.borrow_mut(); + + if state.dragging { + state.update_value_to_mouse(event_data, &data, common); + } + }), ); } @@ -198,15 +255,13 @@ fn register_event_mouse_press( listener_handles, data.body, EventListenerKind::MousePress, - Box::new(move |common, _data, _, _| { + Box::new(move |common, event_data, _, _| { common.trigger_haptics(); - let mut state = state.borrow_mut(); if state.hovered { state.dragging = true; - let val = 1.0; - state.set_value(&data, common, val); + state.update_value_to_mouse(event_data, &data, common) } }), ); @@ -244,7 +299,7 @@ pub fn construct( style.min_size = style.size; style.max_size = style.size; - let (body_id, _) = layout.add_child(parent, Div::create()?, style)?; + let (body_id, slider_body_node) = layout.add_child(parent, Div::create()?, style)?; let (_background_id, _) = layout.add_child( body_id, @@ -267,21 +322,35 @@ pub fn construct( }, )?; - // invisible outer handle body - let (slider_handle_id, slider_handle_node) = layout.add_child( - body_id, - Div::create()?, - taffy::Style { - size: taffy::Size { - width: length(0.0), - height: percent(1.0), - }, - position: taffy::Position::Absolute, - align_items: Some(taffy::AlignItems::Center), - justify_content: Some(taffy::JustifyContent::Center), - ..Default::default() + let slider_handle_style = taffy::Style { + size: taffy::Size { + width: length(0.0), + height: percent(1.0), }, - )?; + position: taffy::Position::Absolute, + align_items: Some(taffy::AlignItems::Center), + justify_content: Some(taffy::JustifyContent::Center), + margin: taffy::Rect { + // FIXME: temporary just for testing + left: percent(0.5), + bottom: length(0.0), + right: length(0.0), + top: length(0.0), + }, + ..Default::default() + }; + + // TODO: dispatch style config after this taffy tree did a re-layout + /*conf_handle_style( + ¶ms.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)?; let (slider_handle_rect_id, _) = layout.add_child( slider_handle_id, @@ -307,14 +376,13 @@ pub fn construct( slider_handle_node, slider_handle_rect_id, slider_handle_id, + slider_body_node, }); let state = Rc::new(RefCell::new(SliderState { dragging: false, hovered: false, - max_value: params.max_value, - value: params.initial_value, - min_value: params.min_value, + values: params.values, })); let mut lhandles = ListenerHandleVec::default(); diff --git a/wgui/src/event.rs b/wgui/src/event.rs index e82fb04..ffa9f96 100644 --- a/wgui/src/event.rs +++ b/wgui/src/event.rs @@ -11,15 +11,26 @@ use crate::{ }; #[derive(Debug, Clone, Copy)] -pub enum MouseButton { +pub enum MouseButtonIndex { Left, Right, Middle, } +#[derive(Debug, Clone, Copy)] +pub struct MouseButton { + pub index: MouseButtonIndex, + pub pos: Vec2, +} + +#[derive(Debug, Clone, Copy)] +pub struct MousePosition { + pub pos: Vec2, +} + pub struct MouseDownEvent { pub pos: Vec2, - pub button: MouseButton, + pub index: MouseButtonIndex, pub device: usize, } @@ -34,7 +45,7 @@ pub struct MouseMotionEvent { pub struct MouseUpEvent { pub pos: Vec2, - pub button: MouseButton, + pub index: MouseButtonIndex, pub device: usize, } @@ -150,9 +161,27 @@ pub struct CallbackData<'a> { pub enum CallbackMetadata { None, MouseButton(MouseButton), + MousePosition(MousePosition), Custom(usize), } +impl CallbackMetadata { + // helper function + pub fn get_mouse_pos_absolute(&self) -> Option { + match *self { + CallbackMetadata::None => None, + CallbackMetadata::MouseButton(b) => Some(b.pos), + CallbackMetadata::MousePosition(b) => Some(b.pos), + CallbackMetadata::Custom(_) => None, + } + } + + pub fn get_mouse_pos_relative(&self, transform_stack: &TransformStack) -> Option { + let mouse_pos_abs = self.get_mouse_pos_absolute()?; + Some(mouse_pos_abs - transform_stack.get_pos()) + } +} + #[derive(Clone, Copy, PartialEq, Eq)] pub enum EventListenerKind { MousePress, diff --git a/wgui/src/parser/component_slider.rs b/wgui/src/parser/component_slider.rs index 7f05131..a8ffe93 100644 --- a/wgui/src/parser/component_slider.rs +++ b/wgui/src/parser/component_slider.rs @@ -37,10 +37,12 @@ pub fn parse_component_slider<'a, U1, U2>( ctx.listeners, parent_id, slider::Params { - min_value, - max_value, - initial_value, style, + values: slider::ValuesMinMax { + min_value, + max_value, + value: initial_value, + }, }, )?); diff --git a/wgui/src/widget/mod.rs b/wgui/src/widget/mod.rs index 16a64fd..9fa5655 100644 --- a/wgui/src/widget/mod.rs +++ b/wgui/src/widget/mod.rs @@ -1,12 +1,13 @@ use glam::Vec2; use super::drawing::RenderPrimitive; + use crate::{ any::AnyTrait, drawing, event::{ - CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventAlterables, EventListener, - EventListenerCollection, EventListenerKind, EventListenerVec, EventRefs, MouseWheelEvent, + self, CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventAlterables, + EventListenerKind, EventListenerVec, EventRefs, MouseWheelEvent, }, layout::{Layout, WidgetID}, transform_stack::TransformStack, @@ -338,7 +339,10 @@ impl WidgetState { params, MousePress, user_data, - CallbackMetadata::MouseButton(e.button) + CallbackMetadata::MouseButton(event::MouseButton { + index: e.index, + pos: e.pos + }) ); } } @@ -352,15 +356,13 @@ impl WidgetState { params, MouseRelease, user_data, - CallbackMetadata::MouseButton(e.button) + CallbackMetadata::MouseButton(event::MouseButton { + index: e.index, + pos: e.pos, + }) ); } } - Event::MouseWheel(e) => { - if hovered && self.process_wheel(params, e) { - return EventResult::Consumed; - } - } Event::MouseMotion(e) => { if self.data.set_device_hovered(e.device, hovered) { if self.data.is_hovered() { @@ -396,9 +398,14 @@ impl WidgetState { params, MouseMotion, user_data, - CallbackMetadata::None + CallbackMetadata::MousePosition(event::MousePosition { pos: e.pos }) ); } + Event::MouseWheel(e) => { + if hovered && self.process_wheel(params, e) { + return EventResult::Consumed; + } + } Event::MouseLeave(e) => { if self.data.set_device_hovered(e.device, false) { call_event!( diff --git a/wlx-overlay-s/src/gui/panel.rs b/wlx-overlay-s/src/gui/panel.rs index 48ba7b2..839a02e 100644 --- a/wlx-overlay-s/src/gui/panel.rs +++ b/wlx-overlay-s/src/gui/panel.rs @@ -5,8 +5,8 @@ use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView}; use wgui::{ event::{ Event as WguiEvent, EventListenerCollection, InternalStateChangeEvent, ListenerHandleVec, - MouseButton, MouseDownEvent, MouseLeaveEvent, MouseMotionEvent, MouseUpEvent, - MouseWheelEvent, + MouseButton, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent, MouseMotionEvent, + MouseUpEvent, MouseWheelEvent, }, layout::Layout, parser::ParserState, @@ -223,10 +223,10 @@ impl OverlayBackend for GuiPanel { } fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { - let button = match hit.mode { - PointerMode::Left => MouseButton::Left, - PointerMode::Right => MouseButton::Right, - PointerMode::Middle => MouseButton::Middle, + let index = match hit.mode { + PointerMode::Left => MouseButtonIndex::Left, + PointerMode::Right => MouseButtonIndex::Right, + PointerMode::Middle => MouseButtonIndex::Middle, _ => return, }; @@ -235,7 +235,7 @@ impl OverlayBackend for GuiPanel { app, &WguiEvent::MouseDown(MouseDownEvent { pos: hit.uv * self.layout.content_size, - button, + index, device: hit.pointer, }), ); @@ -244,7 +244,7 @@ impl OverlayBackend for GuiPanel { app, &WguiEvent::MouseUp(MouseUpEvent { pos: hit.uv * self.layout.content_size, - button, + index, device: hit.pointer, }), ); diff --git a/wlx-overlay-s/src/overlays/keyboard/mod.rs b/wlx-overlay-s/src/overlays/keyboard/mod.rs index 0a54c18..5393a28 100644 --- a/wlx-overlay-s/src/overlays/keyboard/mod.rs +++ b/wlx-overlay-s/src/overlays/keyboard/mod.rs @@ -7,7 +7,7 @@ use std::{ use vulkano::image::view::ImageView; use wgui::{ drawing, - event::{InternalStateChangeEvent, MouseButton}, + event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex}, }; use crate::{ @@ -134,9 +134,9 @@ fn handle_press( ) { match &key.button_state { KeyButtonData::Key { vk, pressed } => { - keyboard.modifiers |= match button { - MouseButton::Right => SHIFT, - MouseButton::Middle => keyboard.alt_modifier, + keyboard.modifiers |= match button.index { + MouseButtonIndex::Right => SHIFT, + MouseButtonIndex::Middle => keyboard.alt_modifier, _ => 0, };