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::{
event::{
EventListenerCollection, MouseButton, MouseDownEvent, MouseMotionEvent, MouseUpEvent,
EventListenerCollection, MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent,
MouseWheelEvent,
},
gfx::WGfx,
@@ -160,7 +160,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
&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<dyn std::error::Error>> {
&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<dyn std::error::Error>> {
return;
}
log::trace!("drawing frame {}", frame_index);
log::trace!("drawing frame {frame_index}");
frame_index += 1;
profiler.start();

View File

@@ -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<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 {
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<U1, U2>(
data: Rc<Data>,
state: Rc<RefCell<SliderState>>,
@@ -176,7 +227,7 @@ fn register_event_mouse_leave<U1, U2>(
fn register_event_mouse_motion<U1, U2>(
data: Rc<Data>,
_state: Rc<RefCell<SliderState>>,
state: Rc<RefCell<SliderState>>,
listeners: &mut EventListenerCollection<U1, U2>,
listener_handles: &mut ListenerHandleVec,
) {
@@ -184,7 +235,13 @@ fn register_event_mouse_motion<U1, U2>(
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<U1, U2>(
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<U1, U2>(
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,11 +322,7 @@ pub fn construct<U1, U2>(
},
)?;
// invisible outer handle body
let (slider_handle_id, slider_handle_node) = layout.add_child(
body_id,
Div::create()?,
taffy::Style {
let slider_handle_style = taffy::Style {
size: taffy::Size {
width: length(0.0),
height: percent(1.0),
@@ -279,9 +330,27 @@ pub fn construct<U1, U2>(
position: taffy::Position::Absolute,
align_items: Some(taffy::AlignItems::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(
slider_handle_id,
@@ -307,14 +376,13 @@ pub fn construct<U1, U2>(
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();

View File

@@ -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<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)]
pub enum EventListenerKind {
MousePress,

View File

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

View File

@@ -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!(

View File

@@ -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<S> OverlayBackend for GuiPanel<S> {
}
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<S> OverlayBackend for GuiPanel<S> {
app,
&WguiEvent::MouseDown(MouseDownEvent {
pos: hit.uv * self.layout.content_size,
button,
index,
device: hit.pointer,
}),
);
@@ -244,7 +244,7 @@ impl<S> OverlayBackend for GuiPanel<S> {
app,
&WguiEvent::MouseUp(MouseUpEvent {
pos: hit.uv * self.layout.content_size,
button,
index,
device: hit.pointer,
}),
);

View File

@@ -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,
};