slider events and value setting

This commit is contained in:
Aleksander
2025-07-07 21:58:41 +02:00
parent 857c5ec865
commit 77421b4e82
7 changed files with 187 additions and 81 deletions

View File

@@ -20,7 +20,7 @@ use vulkano::{
}; };
use wgui::{ use wgui::{
event::{ event::{
EventListenerCollection, MouseButton, MouseDownEvent, MouseMotionEvent, MouseUpEvent, EventListenerCollection, MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent,
MouseWheelEvent, MouseWheelEvent,
}, },
gfx::WGfx, gfx::WGfx,
@@ -160,7 +160,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
&mut listeners, &mut listeners,
&wgui::event::Event::MouseDown(MouseDownEvent { &wgui::event::Event::MouseDown(MouseDownEvent {
pos: mouse / scale, pos: mouse / scale,
button: MouseButton::Left, index: MouseButtonIndex::Left,
device: 0, device: 0,
}), }),
(&mut (), &mut ()), (&mut (), &mut ()),
@@ -173,7 +173,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
&mut listeners, &mut listeners,
&wgui::event::Event::MouseUp(MouseUpEvent { &wgui::event::Event::MouseUp(MouseUpEvent {
pos: mouse / scale, pos: mouse / scale,
button: MouseButton::Left, index: MouseButtonIndex::Left,
device: 0, device: 0,
}), }),
(&mut (), &mut ()), (&mut (), &mut ()),
@@ -280,7 +280,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
return; return;
} }
log::trace!("drawing frame {}", frame_index); log::trace!("drawing frame {frame_index}");
frame_index += 1; frame_index += 1;
profiler.start(); profiler.start();

View File

@@ -22,30 +22,33 @@ use crate::{
}, },
}; };
pub struct Params { #[derive(Default)]
pub style: taffy::Style, pub struct ValuesMinMax {
pub initial_value: f32, pub value: f32,
pub min_value: f32, pub min_value: f32,
pub max_value: f32, pub max_value: f32,
} }
impl Default for Params { impl ValuesMinMax {
fn default() -> Self { fn to_normalized(&self) -> f32 {
Self { (self.value - self.min_value) / (self.max_value - self.min_value)
style: Default::default(), }
initial_value: 0.5,
min_value: 0.0, fn get_from_normalized(&self, normalized: f32) -> f32 {
max_value: 1.0, 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 { pub struct SliderState {
dragging: bool, dragging: bool,
hovered: bool, hovered: bool,
value: f32, values: ValuesMinMax,
min_value: f32,
max_value: f32,
} }
struct Data { struct Data {
@@ -53,6 +56,7 @@ struct Data {
slider_handle_id: WidgetID, // Div slider_handle_id: WidgetID, // Div
slider_handle_rect_id: WidgetID, // Rectangle slider_handle_rect_id: WidgetID, // Rectangle
slider_handle_node: taffy::NodeId, slider_handle_node: taffy::NodeId,
slider_body_node: taffy::NodeId,
} }
pub struct Slider { pub struct Slider {
@@ -63,12 +67,58 @@ pub struct Slider {
impl Component for 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<WidgetID>) -> 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<WidgetID>,
) {
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 { 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) { fn set_value(&mut self, data: &Data, common: &mut CallbackDataCommon, value: f32) {
self.value = value; //common.call_on_widget(data.slider_handle_id, |_div: &mut Div| {});
common.mark_dirty(data.slider_handle_node); self.values.value = value;
common.call_on_widget(data.slider_handle_id, |div: &mut Div| {});
common.mark_redraw();
let mut style = common let mut style = common
.refs .refs
@@ -77,9 +127,15 @@ impl SliderState {
.unwrap() .unwrap()
.clone(); .clone();
// todo conf_handle_style(
style.margin.left = percent(1.0); &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); 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<U1, U2>( fn register_event_mouse_enter<U1, U2>(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<SliderState>>, state: Rc<RefCell<SliderState>>,
@@ -176,7 +227,7 @@ fn register_event_mouse_leave<U1, U2>(
fn register_event_mouse_motion<U1, U2>( fn register_event_mouse_motion<U1, U2>(
data: Rc<Data>, data: Rc<Data>,
_state: Rc<RefCell<SliderState>>, state: Rc<RefCell<SliderState>>,
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection<U1, U2>,
listener_handles: &mut ListenerHandleVec, listener_handles: &mut ListenerHandleVec,
) { ) {
@@ -184,7 +235,13 @@ fn register_event_mouse_motion<U1, U2>(
listener_handles, listener_handles,
data.body, data.body,
EventListenerKind::MouseMotion, 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<U1, U2>(
listener_handles, listener_handles,
data.body, data.body,
EventListenerKind::MousePress, EventListenerKind::MousePress,
Box::new(move |common, _data, _, _| { Box::new(move |common, event_data, _, _| {
common.trigger_haptics(); common.trigger_haptics();
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
if state.hovered { if state.hovered {
state.dragging = true; state.dragging = true;
let val = 1.0; state.update_value_to_mouse(event_data, &data, common)
state.set_value(&data, common, val);
} }
}), }),
); );
@@ -244,7 +299,7 @@ pub fn construct<U1, U2>(
style.min_size = style.size; style.min_size = style.size;
style.max_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( let (_background_id, _) = layout.add_child(
body_id, body_id,
@@ -267,11 +322,7 @@ pub fn construct<U1, U2>(
}, },
)?; )?;
// invisible outer handle body let slider_handle_style = taffy::Style {
let (slider_handle_id, slider_handle_node) = layout.add_child(
body_id,
Div::create()?,
taffy::Style {
size: taffy::Size { size: taffy::Size {
width: length(0.0), width: length(0.0),
height: percent(1.0), height: percent(1.0),
@@ -279,9 +330,27 @@ pub fn construct<U1, U2>(
position: taffy::Position::Absolute, position: taffy::Position::Absolute,
align_items: Some(taffy::AlignItems::Center), align_items: Some(taffy::AlignItems::Center),
justify_content: Some(taffy::JustifyContent::Center), justify_content: Some(taffy::JustifyContent::Center),
..Default::default() 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(
&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)?;
let (slider_handle_rect_id, _) = layout.add_child( let (slider_handle_rect_id, _) = layout.add_child(
slider_handle_id, slider_handle_id,
@@ -307,14 +376,13 @@ pub fn construct<U1, U2>(
slider_handle_node, slider_handle_node,
slider_handle_rect_id, slider_handle_rect_id,
slider_handle_id, slider_handle_id,
slider_body_node,
}); });
let state = Rc::new(RefCell::new(SliderState { let state = Rc::new(RefCell::new(SliderState {
dragging: false, dragging: false,
hovered: false, hovered: false,
max_value: params.max_value, values: params.values,
value: params.initial_value,
min_value: params.min_value,
})); }));
let mut lhandles = ListenerHandleVec::default(); let mut lhandles = ListenerHandleVec::default();

View File

@@ -11,15 +11,26 @@ use crate::{
}; };
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum MouseButton { pub enum MouseButtonIndex {
Left, Left,
Right, Right,
Middle, 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 struct MouseDownEvent {
pub pos: Vec2, pub pos: Vec2,
pub button: MouseButton, pub index: MouseButtonIndex,
pub device: usize, pub device: usize,
} }
@@ -34,7 +45,7 @@ pub struct MouseMotionEvent {
pub struct MouseUpEvent { pub struct MouseUpEvent {
pub pos: Vec2, pub pos: Vec2,
pub button: MouseButton, pub index: MouseButtonIndex,
pub device: usize, pub device: usize,
} }
@@ -150,9 +161,27 @@ pub struct CallbackData<'a> {
pub enum CallbackMetadata { pub enum CallbackMetadata {
None, None,
MouseButton(MouseButton), MouseButton(MouseButton),
MousePosition(MousePosition),
Custom(usize), Custom(usize),
} }
impl CallbackMetadata {
// helper function
pub fn get_mouse_pos_absolute(&self) -> Option<Vec2> {
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<Vec2> {
let mouse_pos_abs = self.get_mouse_pos_absolute()?;
Some(mouse_pos_abs - transform_stack.get_pos())
}
}
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum EventListenerKind { pub enum EventListenerKind {
MousePress, MousePress,

View File

@@ -37,10 +37,12 @@ pub fn parse_component_slider<'a, U1, U2>(
ctx.listeners, ctx.listeners,
parent_id, parent_id,
slider::Params { slider::Params {
style,
values: slider::ValuesMinMax {
min_value, min_value,
max_value, max_value,
initial_value, value: initial_value,
style, },
}, },
)?); )?);

View File

@@ -1,12 +1,13 @@
use glam::Vec2; use glam::Vec2;
use super::drawing::RenderPrimitive; use super::drawing::RenderPrimitive;
use crate::{ use crate::{
any::AnyTrait, any::AnyTrait,
drawing, drawing,
event::{ event::{
CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventAlterables, EventListener, self, CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventAlterables,
EventListenerCollection, EventListenerKind, EventListenerVec, EventRefs, MouseWheelEvent, EventListenerKind, EventListenerVec, EventRefs, MouseWheelEvent,
}, },
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
transform_stack::TransformStack, transform_stack::TransformStack,
@@ -338,7 +339,10 @@ impl WidgetState {
params, params,
MousePress, MousePress,
user_data, user_data,
CallbackMetadata::MouseButton(e.button) CallbackMetadata::MouseButton(event::MouseButton {
index: e.index,
pos: e.pos
})
); );
} }
} }
@@ -352,15 +356,13 @@ impl WidgetState {
params, params,
MouseRelease, MouseRelease,
user_data, 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) => { Event::MouseMotion(e) => {
if self.data.set_device_hovered(e.device, hovered) { if self.data.set_device_hovered(e.device, hovered) {
if self.data.is_hovered() { if self.data.is_hovered() {
@@ -396,9 +398,14 @@ impl WidgetState {
params, params,
MouseMotion, MouseMotion,
user_data, 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) => { Event::MouseLeave(e) => {
if self.data.set_device_hovered(e.device, false) { if self.data.set_device_hovered(e.device, false) {
call_event!( call_event!(

View File

@@ -5,8 +5,8 @@ use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView};
use wgui::{ use wgui::{
event::{ event::{
Event as WguiEvent, EventListenerCollection, InternalStateChangeEvent, ListenerHandleVec, Event as WguiEvent, EventListenerCollection, InternalStateChangeEvent, ListenerHandleVec,
MouseButton, MouseDownEvent, MouseLeaveEvent, MouseMotionEvent, MouseUpEvent, MouseButton, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent, MouseMotionEvent,
MouseWheelEvent, MouseUpEvent, MouseWheelEvent,
}, },
layout::Layout, layout::Layout,
parser::ParserState, parser::ParserState,
@@ -223,10 +223,10 @@ impl<S> OverlayBackend for GuiPanel<S> {
} }
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
let button = match hit.mode { let index = match hit.mode {
PointerMode::Left => MouseButton::Left, PointerMode::Left => MouseButtonIndex::Left,
PointerMode::Right => MouseButton::Right, PointerMode::Right => MouseButtonIndex::Right,
PointerMode::Middle => MouseButton::Middle, PointerMode::Middle => MouseButtonIndex::Middle,
_ => return, _ => return,
}; };
@@ -235,7 +235,7 @@ impl<S> OverlayBackend for GuiPanel<S> {
app, app,
&WguiEvent::MouseDown(MouseDownEvent { &WguiEvent::MouseDown(MouseDownEvent {
pos: hit.uv * self.layout.content_size, pos: hit.uv * self.layout.content_size,
button, index,
device: hit.pointer, device: hit.pointer,
}), }),
); );
@@ -244,7 +244,7 @@ impl<S> OverlayBackend for GuiPanel<S> {
app, app,
&WguiEvent::MouseUp(MouseUpEvent { &WguiEvent::MouseUp(MouseUpEvent {
pos: hit.uv * self.layout.content_size, pos: hit.uv * self.layout.content_size,
button, index,
device: hit.pointer, device: hit.pointer,
}), }),
); );

View File

@@ -7,7 +7,7 @@ use std::{
use vulkano::image::view::ImageView; use vulkano::image::view::ImageView;
use wgui::{ use wgui::{
drawing, drawing,
event::{InternalStateChangeEvent, MouseButton}, event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex},
}; };
use crate::{ use crate::{
@@ -134,9 +134,9 @@ fn handle_press(
) { ) {
match &key.button_state { match &key.button_state {
KeyButtonData::Key { vk, pressed } => { KeyButtonData::Key { vk, pressed } => {
keyboard.modifiers |= match button { keyboard.modifiers |= match button.index {
MouseButton::Right => SHIFT, MouseButtonIndex::Right => SHIFT,
MouseButton::Middle => keyboard.alt_modifier, MouseButtonIndex::Middle => keyboard.alt_modifier,
_ => 0, _ => 0,
}; };