mirror of
https://github.com/griffi-gh/hUI.git
synced 2025-04-02 05:56:28 -05:00
142 lines
3.9 KiB
Rust
142 lines
3.9 KiB
Rust
//! a slider element that allows selecting a value in a range
|
|
|
|
use derive_setters::Setters;
|
|
use glam::{Vec2, vec2};
|
|
|
|
use crate::{
|
|
draw::UiDrawCommand,
|
|
element::{MeasureContext, ProcessContext, UiElement},
|
|
layout::{Size2d, compute_size},
|
|
measure::Response,
|
|
rect::FillColor,
|
|
signal::{trigger::SignalTriggerArg, Signal},
|
|
};
|
|
|
|
/// Follow mode for the slider
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
|
|
pub enum SliderFollowMode {
|
|
/// Slider will change based on the absolute mouse position in the slider
|
|
///
|
|
/// This is the default mode and is recommended for most use cases
|
|
#[default]
|
|
Absolute,
|
|
|
|
/// Slider will change based on the difference between the current and starting mouse position
|
|
///
|
|
/// This is an experimental option and does not currently work well for sliders with large step sizes
|
|
Relative,
|
|
}
|
|
|
|
/// A slider element that allows selecting a value in a range
|
|
#[derive(Setters)]
|
|
#[setters(prefix = "with_")]
|
|
pub struct Slider {
|
|
/// Value of the slider, should be in range 0..1
|
|
///
|
|
/// Out of range values will be clamped
|
|
pub value: f32,
|
|
|
|
/// Size of the element
|
|
#[setters(into)]
|
|
pub size: Size2d,
|
|
|
|
/// Color of the slider handle
|
|
#[setters(into)]
|
|
pub handle_color: FillColor,
|
|
|
|
/// Color of the slider track
|
|
#[setters(into)]
|
|
pub track_color: FillColor,
|
|
|
|
/// Follow mode
|
|
pub follow_mode: SliderFollowMode,
|
|
|
|
#[setters(skip)]
|
|
pub on_change: Option<SignalTriggerArg<f32>>,
|
|
}
|
|
|
|
impl Default for Slider {
|
|
fn default() -> Self {
|
|
Self {
|
|
value: 0.0,
|
|
size: Size2d::default(),
|
|
handle_color: (0.0, 0.0, 1.0).into(),
|
|
track_color: (0.5, 0.5, 0.5).into(),
|
|
follow_mode: SliderFollowMode::default(),
|
|
on_change: None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Slider {
|
|
pub const DEFAULT_HEIGHT: f32 = 21.0;
|
|
|
|
pub fn new(value: f32) -> Self {
|
|
Self {
|
|
value,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
pub fn on_change<S: Signal, T: Fn(f32) -> S + 'static>(self, f: T) -> Self {
|
|
Self {
|
|
on_change: Some(SignalTriggerArg::new(f)),
|
|
..self
|
|
}
|
|
}
|
|
}
|
|
|
|
impl UiElement for Slider {
|
|
fn name(&self) -> &'static str {
|
|
"Slider"
|
|
}
|
|
|
|
fn measure(&self, ctx: MeasureContext) -> Response {
|
|
Response {
|
|
size: compute_size(ctx.layout, self.size, (ctx.layout.max_size.x, Self::DEFAULT_HEIGHT).into()),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn process(&self, ctx: ProcessContext) {
|
|
//TODO: unhardcode this
|
|
let bgrect_height_ratio = 0.33;
|
|
|
|
ctx.draw.add(UiDrawCommand::Rectangle {
|
|
position: ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - bgrect_height_ratio / 2.),
|
|
size: ctx.measure.size * vec2(1., bgrect_height_ratio),
|
|
color: self.track_color.into(),
|
|
texture: None,
|
|
rounded_corners: None,
|
|
});
|
|
|
|
let value = self.value.clamp(0., 1.);
|
|
let handle_size = vec2(15., ctx.measure.size.y);
|
|
ctx.draw.add(UiDrawCommand::Rectangle {
|
|
position: ctx.layout.position + ((ctx.measure.size.x - handle_size.x) * value) * Vec2::X,
|
|
size: handle_size,
|
|
color: self.handle_color.into(),
|
|
texture: None,
|
|
rounded_corners: None,
|
|
});
|
|
|
|
//handle events
|
|
if let Some(res) = ctx.input.check_active(ctx.measure.rect(ctx.layout.position)) {
|
|
let new_value = match self.follow_mode {
|
|
SliderFollowMode::Absolute => ((res.position_in_rect.x - handle_size.x / 2.) / (ctx.measure.size.x - handle_size.x)).clamp(0., 1.),
|
|
SliderFollowMode::Relative => {
|
|
let delta = res.position_in_rect.x - res.last_position_in_rect.x;
|
|
let delta_ratio = delta / (ctx.measure.size.x - handle_size.x);
|
|
(self.value + delta_ratio).clamp(0., 1.)
|
|
}
|
|
};
|
|
if let Some(signal) = &self.on_change {
|
|
signal.fire(ctx.signal, new_value);
|
|
}
|
|
//TODO call signal with new value
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO
|