1use {
2 crate::{
3 makepad_derive_widget::*,
4 makepad_draw::*,
5 widget::*,
6 text_input::{TextInput, TextInputAction}
7 }
8};
9
10live_design!{
11 DrawSlider = {{DrawSlider}} {}
12 SliderBase = {{Slider}} {}
13}
14
15#[derive(Live, LiveHook)]
16#[live_ignore]
17#[repr(u32)]
18pub enum SliderType {
19 #[pick] Horizontal = shader_enum(1),
20 Vertical = shader_enum(2),
21 Rotary = shader_enum(3),
22}
23
24
25#[derive(Live, LiveHook)]
26#[repr(C)]
27pub struct DrawSlider {
28 #[deref] draw_super: DrawQuad,
29 #[live] slide_pos: f32,
30 #[live] slider_type: SliderType
31}
32
33#[derive(Live)]
34pub struct Slider {
35 #[live] draw_slider: DrawSlider,
36
37 #[walk] walk: Walk,
38
39 #[layout] layout: Layout,
40 #[animator] animator: Animator,
41
42 #[live] label_walk: Walk,
43 #[live] label_align: Align,
44 #[live] draw_text: DrawText,
45 #[live] text: String,
46
47 #[live] text_input: TextInput,
48
49 #[live] precision: usize,
50
51 #[live] min: f64,
52 #[live] max: f64,
53 #[live] step: f64,
54
55 #[live] bind: String,
56
57 #[rust] pub value: f64,
58 #[rust] pub dragging: Option<f64>,
59}
60
61impl LiveHook for Slider{
62 fn before_live_design(cx:&mut Cx){
63 register_widget!(cx,Slider)
64 }
65}
66
67#[derive(Clone, WidgetAction)]
68pub enum SliderAction {
69 StartSlide,
70 TextSlide(f64),
71 Slide(f64),
72 EndSlide,
73 None
74}
75
76
77impl Slider {
78
79 fn to_external(&self) -> f64 {
80 let val = self.value * (self.max - self.min) + self.min;
81 if self.step != 0.0{
82 return (val * self.step).floor() / self.step
83 }
84 else{
85 val
86 }
87 }
88
89 fn set_internal(&mut self, external: f64) -> bool {
90 let old = self.value;
91 self.value = (external - self.min) / (self.max - self.min);
92 old != self.value
93 }
94
95 pub fn handle_event_with(&mut self, cx: &mut Cx, event: &Event, dispatch_action: &mut dyn FnMut(&mut Cx, SliderAction)) {
96 self.animator_handle_event(cx, event);
97 for action in self.text_input.handle_event(cx, event) {
98 match action {
99 TextInputAction::KeyFocus => {
100 self.animator_play(cx, id!(focus.on));
101 }
102 TextInputAction::KeyFocusLost => {
103 self.animator_play(cx, id!(focus.off));
104 }
105 TextInputAction::Return(value) => {
106 if let Ok(v) = value.parse::<f64>() {
107 self.set_internal(v.max(self.min).min(self.max));
108 }
109 self.update_text_input(cx);
110 dispatch_action(cx, SliderAction::TextSlide(self.to_external()));
111 }
112 TextInputAction::Escape => {
113 self.update_text_input(cx);
114 }
115 _ => ()
116 }
117 };
118 match event.hits(cx, self.draw_slider.area()) {
119 Hit::FingerHoverIn(_) => {
120 cx.set_cursor(MouseCursor::Arrow);
121 self.animator_play(cx, id!(hover.on));
122 }
123 Hit::FingerHoverOut(_) => {
124 self.animator_play(cx, id!(hover.off));
125 },
126 Hit::FingerDown(_fe) => {
127 self.text_input.read_only = true;
129 self.text_input.set_key_focus(cx);
130 self.text_input.select_all();
131 self.text_input.redraw(cx);
132
133 self.animator_play(cx, id!(drag.on));
134 self.dragging = Some(self.value);
135 dispatch_action(cx, SliderAction::StartSlide);
136 },
137 Hit::FingerUp(fe) => {
138 self.text_input.read_only = false;
139 self.text_input.create_external_undo();
141 self.animator_play(cx, id!(drag.off));
142 if fe.is_over && fe.device.has_hovers() {
143 self.animator_play(cx, id!(hover.on));
144 }
145 else {
146 self.animator_play(cx, id!(hover.off));
147 }
148 self.dragging = None;
149 dispatch_action(cx, SliderAction::EndSlide);
150 }
151 Hit::FingerMove(fe) => {
152 let rel = fe.abs - fe.abs_start;
153 if let Some(start_pos) = self.dragging {
154 self.value = (start_pos + rel.x / fe.rect.size.x).max(0.0).min(1.0);
155 self.set_internal(self.to_external());
156 self.draw_slider.redraw(cx);
157 self.update_text_input(cx);
158 dispatch_action(cx, SliderAction::Slide(self.to_external()));
159 }
160 }
161 _ => ()
162 }
163 }
164
165 pub fn update_text_input(&mut self, cx: &mut Cx) {
166 let e = self.to_external();
167 self.text_input.text = match self.precision{
168 0=>format!("{:.0}",e),
169 1=>format!("{:.1}",e),
170 2=>format!("{:.2}",e),
171 3=>format!("{:.3}",e),
172 4=>format!("{:.4}",e),
173 5=>format!("{:.5}",e),
174 6=>format!("{:.6}",e),
175 7=>format!("{:.7}",e),
176 _=>format!("{}",e)
177 };
178 self.text_input.select_all();
179 self.text_input.redraw(cx)
180 }
181
182 pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk) {
183 self.draw_slider.slide_pos = self.value as f32;
184 self.draw_slider.begin(cx, walk, self.layout);
185
186 if let Some(mut dw) = cx.defer_walk(self.label_walk) {
187 let walk = self.text_input.walk(cx);
189 self.text_input.draw_walk(cx, walk);
190 self.draw_text.draw_walk(cx, dw.resolve(cx), self.label_align, &self.text);
191 }
192
193 self.draw_slider.end(cx);
194 }
195}
196
197
198impl Widget for Slider {
199 fn redraw(&mut self, cx: &mut Cx) {
200 self.draw_slider.redraw(cx);
201 }
202
203 fn handle_widget_event_with(&mut self, cx: &mut Cx, event: &Event, dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem)) {
204 let uid = self.widget_uid();
205 self.handle_event_with(cx, event, &mut | cx, action | {
206 dispatch_action(cx, WidgetActionItem::new(action.into(), uid))
207 });
208 }
209
210 fn walk(&mut self, _cx:&mut Cx) -> Walk {self.walk}
211
212 fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
213 self.draw_walk(cx, walk);
214 WidgetDraw::done()
215 }
216
217 fn widget_to_data(&self, _cx: &mut Cx, actions:&WidgetActions, nodes: &mut LiveNodeVec, path: &[LiveId])->bool{
218 match actions.single_action(self.widget_uid()) {
219 SliderAction::TextSlide(v) | SliderAction::Slide(v) => {
220 nodes.write_field_value(path, LiveValue::Float64(v as f64));
221 true
222 }
223 _ => false
224 }
225 }
226
227 fn data_to_widget(&mut self, cx: &mut Cx, nodes:&[LiveNode], path: &[LiveId]){
228 if let Some(value) = nodes.read_field_value(path) {
229 if let Some(value) = value.as_float() {
230 if self.set_internal(value) {
231 self.redraw(cx)
232 }
233 self.update_text_input(cx);
234 }
235 }
236 }
237}
238
239#[derive(Clone, PartialEq, WidgetRef)]
240pub struct SliderRef(WidgetRef);