1use crate::draw::*;
2use crate::event::{Event, Key};
3use crate::layout::{Rectangle, Size};
4use crate::node::{GenericNode, IntoNode, Node};
5use crate::style::Stylesheet;
6use crate::widget::{dummy::Dummy, Context, Widget};
7
8pub struct Slider<'a, T, F> {
11 scrollbar: Node<'a, T>,
12 min: f32,
13 max: f32,
14 value: f32,
15 on_slide: F,
16}
17
18pub struct State {
20 inner: InnerState,
21 cursor_x: f32,
22 cursor_y: f32,
23}
24
25#[derive(Clone, Copy)]
26enum InnerState {
27 Idle,
28 Hover,
29 Drag(f32),
30}
31
32impl<'a, T: 'a, F: 'a + Fn(f32) -> T> Slider<'a, T, F> {
33 pub fn new(min: f32, max: f32, value: f32, on_slide: F) -> Slider<'a, T, F> {
35 Self {
36 scrollbar: Dummy::new("handle").into_node(),
37 min,
38 max,
39 value: value.max(min).min(max),
40 on_slide,
41 }
42 }
43
44 pub fn min(mut self, min: f32) -> Self {
46 self.min = min;
47 self.value = self.value.max(min);
48 self
49 }
50
51 pub fn max(mut self, max: f32) -> Self {
53 self.max = max;
54 self.value = self.value.min(max);
55 self
56 }
57
58 pub fn val(mut self, value: f32) -> Self {
60 self.value = value.min(self.max).min(self.min);
61 self
62 }
63
64 pub fn on_slide<N: Fn(f32) -> T>(self, on_slide: N) -> Slider<'a, T, N> {
66 Slider {
67 scrollbar: self.scrollbar,
68 min: self.min,
69 max: self.max,
70 value: self.value,
71 on_slide,
72 }
73 }
74
75 fn scrollbar(&self, layout: Rectangle, style: &Stylesheet) -> Rectangle {
76 let content = style.background.content_rect(layout, style.padding);
77
78 let (handle_width, _) = self.scrollbar.size();
79 let handle_width = match handle_width {
80 Size::Shrink => content.width() * 0.1,
81 Size::Exact(x) => x,
82 Size::Fill(_) => content.width() * 0.1,
83 };
84
85 let mut t = (self.value - self.min) / (self.max - self.min);
86 t = t.max(0.0).min(1.0);
87
88 Rectangle {
89 left: content.left + (content.width() - handle_width) * t,
90 right: content.left + (content.width() - handle_width) * t + handle_width,
91 ..content
92 }
93 }
94}
95
96impl<'a, T: 'a> Default for Slider<'a, T, fn(f32) -> T> {
97 fn default() -> Self {
98 Self {
99 scrollbar: Dummy::new("handle").into_node(),
100 min: 0.0,
101 max: 1.0,
102 value: 0.0,
103 on_slide: |_| panic!("on_slide of `Slider` must be set"),
104 }
105 }
106}
107
108impl<'a, T: 'a, F: 'a + Send + Fn(f32) -> T> Widget<'a, T> for Slider<'a, T, F> {
109 type State = State;
110
111 fn mount(&self) -> Self::State {
112 State::default()
113 }
114
115 fn widget(&self) -> &'static str {
116 "slider"
117 }
118
119 fn len(&self) -> usize {
120 1
121 }
122
123 fn visit_children(&mut self, visitor: &mut dyn FnMut(&mut dyn GenericNode<'a, T>)) {
124 visitor(&mut *self.scrollbar);
125 }
126
127 fn size(&self, _: &State, style: &Stylesheet) -> (Size, Size) {
128 style
129 .background
130 .resolve_size((style.width, style.height), self.scrollbar.size(), style.padding)
131 }
132
133 fn event(
134 &mut self,
135 state: &mut State,
136 layout: Rectangle,
137 clip: Rectangle,
138 style: &Stylesheet,
139 event: Event,
140 context: &mut Context<T>,
141 ) {
142 let content_rect = style.background.content_rect(layout, style.padding);
143 let bar = self.scrollbar(layout, style);
144
145 match (event, state.inner) {
146 (Event::Cursor(cx, cy), InnerState::Drag(x)) => {
147 context.redraw();
148 state.cursor_x = cx;
149 state.cursor_y = cy;
150
151 let begin = content_rect.left;
152 let end = content_rect.right - bar.width();
153 let next_bar_left = (cx - x).max(begin).min(end);
154 let t = (next_bar_left - begin) / (end - begin);
155
156 self.value = self.min + t * (self.max - self.min);
157 context.push((self.on_slide)(self.value));
158 }
159 (Event::Cursor(x, y), _) => {
160 state.cursor_x = x;
161 state.cursor_y = y;
162 if bar.point_inside(x, y) && clip.point_inside(x, y) {
163 state.inner = InnerState::Hover;
164 } else {
165 state.inner = InnerState::Idle;
166 }
167 }
168 (Event::Press(Key::LeftMouseButton), InnerState::Hover) => {
169 state.inner = InnerState::Drag(state.cursor_x - bar.left);
170 }
171 (Event::Release(Key::LeftMouseButton), InnerState::Drag(_)) => {
172 if bar.point_inside(state.cursor_x, state.cursor_y) && clip.point_inside(state.cursor_x, state.cursor_y)
173 {
174 state.inner = InnerState::Hover;
175 } else {
176 state.inner = InnerState::Idle;
177 }
178 }
179 _ => (),
180 }
181 }
182
183 fn draw(&mut self, _: &mut State, layout: Rectangle, clip: Rectangle, style: &Stylesheet) -> Vec<Primitive<'a>> {
184 let mut result = Vec::new();
185 result.extend(style.background.render(layout));
186 let scrollbar = self.scrollbar(layout, style);
187 result.extend(self.scrollbar.draw(scrollbar, clip));
188 result
189 }
190}
191
192impl<'a, T: 'a, F: 'a + Send + Fn(f32) -> T> IntoNode<'a, T> for Slider<'a, T, F> {
193 fn into_node(self) -> Node<'a, T> {
194 Node::from_widget(self)
195 }
196}
197
198impl Default for State {
199 fn default() -> State {
200 State {
201 inner: InnerState::Idle,
202 cursor_x: 0.0,
203 cursor_y: 0.0,
204 }
205 }
206}