geng_ui/widgets/
slider.rs

1use super::*;
2
3pub struct Slider<'a> {
4    sense: &'a mut Sense,
5    pos: &'a mut Option<Aabb2<f64>>,
6    hover_pos: &'a mut Option<f64>,
7    tick_radius: &'a mut f32,
8    value: f64,
9    range: RangeInclusive<f64>,
10    f: Box<dyn FnMut(f64) + 'a>,
11}
12
13impl<'a> Slider<'a> {
14    const ANIMATION_SPEED: f32 = 5.0;
15
16    pub fn new(
17        cx: &'a Controller,
18        value: f64,
19        range: RangeInclusive<f64>,
20        f: Box<dyn FnMut(f64) + 'a>,
21    ) -> Self {
22        Slider {
23            sense: cx.get_state(),
24            tick_radius: cx.get_state(),
25            pos: cx.get_state(),
26            hover_pos: cx.get_state(),
27            value,
28            range,
29            f,
30        }
31    }
32}
33
34impl<'a> Widget for Slider<'a> {
35    fn sense(&mut self) -> Option<&mut Sense> {
36        Some(self.sense)
37    }
38    fn update(&mut self, delta_time: f64) {
39        let target_tick_radius = if self.sense.is_hovered() || self.sense.is_captured() {
40            1.0 / 2.0
41        } else {
42            1.0 / 6.0
43        };
44        *self.tick_radius += (target_tick_radius - *self.tick_radius)
45            .clamp_abs(Self::ANIMATION_SPEED * delta_time as f32);
46    }
47    fn draw(&mut self, cx: &mut DrawContext) {
48        *self.pos = Some(cx.position);
49        let draw2d = cx.draw2d;
50        let position = cx.position.map(|x| x as f32);
51        let line_width = position.height() / 3.0;
52        let value_position = if self.range.end() == self.range.start() {
53            *self.tick_radius
54        } else {
55            *self.tick_radius
56                + ((self.value - *self.range.start()) / (*self.range.end() - *self.range.start()))
57                    as f32
58                    * (position.width() - line_width)
59        };
60        draw2d.draw2d(
61            cx.framebuffer,
62            &PixelPerfectCamera,
63            &draw2d::Quad::new(
64                Aabb2::from_corners(
65                    position.bottom_left()
66                        + vec2(value_position, (position.height() - line_width) / 2.0),
67                    position.top_right()
68                        - vec2(line_width / 2.0, (position.height() - line_width) / 2.0),
69                ),
70                cx.theme.usable_color,
71            ),
72        );
73        draw2d.circle(
74            cx.framebuffer,
75            &PixelPerfectCamera,
76            position.top_right() - vec2(line_width / 2.0, position.height() / 2.0),
77            line_width / 2.0,
78            cx.theme.usable_color,
79        );
80        draw2d.draw2d(
81            cx.framebuffer,
82            &PixelPerfectCamera,
83            &draw2d::Quad::new(
84                Aabb2::from_corners(
85                    position.bottom_left()
86                        + vec2(line_width / 2.0, (position.height() - line_width) / 2.0),
87                    position.bottom_left()
88                        + vec2(value_position, (position.height() + line_width) / 2.0),
89                ),
90                cx.theme.hover_color,
91            ),
92        );
93        draw2d.draw2d(
94            cx.framebuffer,
95            &PixelPerfectCamera,
96            &draw2d::Ellipse::circle(
97                position.bottom_left() + vec2(line_width / 2.0, position.height() / 2.0),
98                line_width / 2.0,
99                cx.theme.hover_color,
100            ),
101        );
102        draw2d.circle(
103            cx.framebuffer,
104            &PixelPerfectCamera,
105            position.bottom_left() + vec2(value_position, position.height() / 2.0),
106            *self.tick_radius * position.height(),
107            cx.theme.hover_color,
108        );
109    }
110    fn handle_event(&mut self, event: &Event) {
111        let aabb = match *self.pos {
112            Some(pos) => pos,
113            None => return,
114        };
115        if let Event::CursorMove { position } = &event {
116            let position = position.x - aabb.min.x;
117            *self.hover_pos = Some(
118                *self.range.start()
119                    + ((position - aabb.height() / 6.0) / (aabb.width() - aabb.height() / 3.0))
120                        .clamp(0.0, 1.0)
121                        * (*self.range.end() - *self.range.start()),
122            );
123        }
124        if self.sense.is_captured() {
125            if let Event::MousePress { .. } | Event::CursorMove { .. } = &event {
126                if let Some(new_value) = *self.hover_pos {
127                    (self.f)(new_value);
128                }
129            }
130        }
131    }
132
133    fn calc_constraints(&mut self, _children: &ConstraintsContext) -> Constraints {
134        Constraints::default()
135    }
136}