makepad_widgets/
slider.rs

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                // cx.set_key_focus(self.slider.area());
128                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                // if the finger hasn't moved further than X we jump to edit-all on the text thing
140                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            //, (self.value*100.0) as usize);
188            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);