makepad_widgets/
button.rs

1use crate::{makepad_derive_widget::*, makepad_draw::*, widget::*,};
2
3live_design! {
4    link widgets;
5    use link::theme::*;
6    use link::shaders::*;
7    
8    pub ButtonBase = {{Button}} {}
9    pub Button = <ButtonBase> {
10        text: ""
11        width: Fit, height: Fit,
12        spacing: (THEME_SPACE_2),
13        align: {x: 0.5, y: 0.5},
14        padding: <THEME_MSPACE_1> { left: (THEME_SPACE_2), right: (THEME_SPACE_2) }
15        margin: <THEME_MSPACE_V_1> {}
16        label_walk: { width: Fit, height: Fit },
17        
18        draw_text: {
19            instance hover: 0.0,
20            instance down: 0.0,
21            instance focus: 0.0,
22            instance disabled: 0.0
23
24            color: (THEME_COLOR_LABEL_INNER)
25            uniform color_hover: (THEME_COLOR_LABEL_INNER_HOVER)
26            uniform color_down: (THEME_COLOR_LABEL_INNER_DOWN)
27            uniform color_focus: (THEME_COLOR_LABEL_INNER_FOCUS)
28            uniform color_disabled: (THEME_COLOR_LABEL_INNER_DISABLED)
29
30            text_style: <THEME_FONT_REGULAR> {
31                font_size: (THEME_FONT_SIZE_P)
32            }
33            fn get_color(self) -> vec4 {
34                return mix(
35                    mix(
36                        mix(
37                            mix(self.color, self.color_focus, self.focus),
38                            self.color_hover,
39                            self.hover
40                        ),
41                        self.color_down,
42                        self.down
43                    ),
44                    self.color_disabled,
45                    self.disabled
46                )
47            }
48        }
49        
50        icon_walk: {
51            width: (THEME_DATA_ICON_WIDTH), height: Fit,
52        }
53        
54        draw_icon: {
55            instance hover: 0.0
56            instance down: 0.0
57            instance focus: 0.0
58            instance disabled: 0.0
59
60            uniform color: (THEME_COLOR_LABEL_INNER)
61            uniform color_hover: (THEME_COLOR_LABEL_INNER_HOVER)
62            uniform color_down: (THEME_COLOR_LABEL_INNER_DOWN)
63            uniform color_focus: (THEME_COLOR_LABEL_INNER_FOCUS)
64            uniform color_disabled: (THEME_COLOR_LABEL_INNER_DISABLED)
65
66            fn get_color(self) -> vec4 {
67                return mix(
68                    mix(
69                        mix(
70                            mix(self.color, self.color_focus, self.focus),
71                            self.color_hover,
72                            self.hover
73                        ),
74                        self.color_down,
75                        self.down
76                    ),
77                    self.color_disabled,
78                    self.disabled
79                )
80            }
81        }
82        
83        draw_bg: {
84            instance hover: 0.0
85            instance down: 0.0
86            instance enabled: 1.0
87            instance disabled: 0.0
88            instance focus: 0.0
89
90            uniform color_dither: 1.0
91
92            uniform border_size: (THEME_BEVELING)
93            uniform border_radius: (THEME_CORNER_RADIUS)
94
95            uniform color: (THEME_COLOR_OUTSET)
96            uniform color_hover: (THEME_COLOR_OUTSET_HOVER)
97            uniform color_down: (THEME_COLOR_OUTSET_DOWN)
98            uniform color_focus: (THEME_COLOR_OUTSET_FOCUS)
99            uniform color_disabled: (THEME_COLOR_OUTSET_DISABLED)
100
101            uniform border_color_1: (THEME_COLOR_BEVEL_OUTSET_1)
102            uniform border_color_1_hover: (THEME_COLOR_BEVEL_OUTSET_1_HOVER)
103            uniform border_color_1_down: (THEME_COLOR_BEVEL_OUTSET_1_DOWN)
104            uniform border_color_1_focus: (THEME_COLOR_BEVEL_OUTSET_1_FOCUS)
105            uniform border_color_1_disabled: (THEME_COLOR_BEVEL_OUTSET_1_DISABLED)
106
107            uniform border_color_2: (THEME_COLOR_BEVEL_OUTSET_2)
108            uniform border_color_2_hover: (THEME_COLOR_BEVEL_OUTSET_2_HOVER)
109            uniform border_color_2_down: (THEME_COLOR_BEVEL_OUTSET_2_DOWN)
110            uniform border_color_2_focus: (THEME_COLOR_BEVEL_OUTSET_2_FOCUS)
111            uniform border_color_2_disabled: (THEME_COLOR_BEVEL_OUTSET_2_DISABLED)
112
113            fn pixel(self) -> vec4 {
114                let sdf = Sdf2d::viewport(self.pos * self.rect_size)
115                let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
116
117                let border_sz_uv = vec2(
118                    self.border_size / self.rect_size.x,
119                    self.border_size / self.rect_size.y
120                )
121
122                let gradient_border = vec2(
123                    self.pos.x + dither,
124                    self.pos.y + dither
125                )
126
127                let sz_inner_px = vec2(
128                    self.rect_size.x - self.border_size * 2.,
129                    self.rect_size.y - self.border_size * 2.
130                );
131
132                let scale_factor_fill = vec2(
133                    self.rect_size.x / sz_inner_px.x,
134                    self.rect_size.y / sz_inner_px.y
135                );
136
137                let gradient_fill = vec2(
138                    self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
139                    self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
140                )
141
142                sdf.box(
143                    self.border_size,
144                    self.border_size,
145                    self.rect_size.x - self.border_size * 2.,
146                    self.rect_size.y - self.border_size * 2.,
147                    self.border_radius
148                )
149
150                sdf.fill_keep(
151                    mix(
152                        mix(
153                            mix(
154                                mix(self.color, self.color_focus, self.focus),
155                                self.color_hover,
156                                self.hover
157                            ),
158                            self.color_down,
159                            self.down
160                        ),
161                        self.color_disabled,
162                        self.disabled
163                    )
164                )
165                
166                sdf.stroke(
167                    mix(
168                        mix(
169                            mix(
170                                mix(
171                                    mix(self.border_color_1, self.border_color_2, gradient_border.y),
172                                    mix(self.border_color_1_focus, self.border_color_2_focus, gradient_border.y),
173                                    self.focus
174                                ),
175                                mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
176                                self.hover
177                            ),
178                            mix(self.border_color_1_down, self.border_color_2_down, gradient_border.y),
179                            self.down
180                        ),
181                        mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
182                        self.disabled
183                    ), self.border_size
184                )
185                return sdf.result;
186            }
187        }
188        
189        animator: {
190            disabled = {
191                default: off,
192                off = {
193                    from: {all: Forward {duration: 0.}}
194                    apply: {
195                        draw_bg: {disabled: 0.0}
196                        draw_text: {disabled: 0.0}
197                        draw_icon: {disabled: 0.0}
198                    }
199                }
200                on = {
201                    from: {all: Forward {duration: 0.2}}
202                    apply: {
203                        draw_bg: {disabled: 1.0}
204                        draw_text: {disabled: 1.0}
205                        draw_icon: {disabled: 1.0}
206                    }
207                }
208            }
209            time = {
210                default: off,
211                off = {
212                    from: {all: Forward {duration: 0.}}
213                    apply: {
214                        //draw_bg: {anim_time: 0.0}
215                    }
216                }
217                on = {
218                    from: {all: Loop {duration: 1.0, end:1000000000.0}}
219                    apply: {
220                        draw_bg: {anim_time: [{time: 0.0, value: 0.0},{time:1.0, value:1.0}]}
221                    }
222                }
223            }
224            hover = {
225                default: off,
226                off = {
227                    from: {all: Forward {duration: 0.1}}
228                    apply: {
229                        draw_bg: {down: 0.0, hover: 0.0}
230                        draw_icon: {down: 0.0, hover: 0.0}
231                        draw_text: {down: 0.0, hover: 0.0}
232                    }
233                }
234                
235                on = {
236                    from: {
237                        all: Forward {duration: 0.1}
238                        down: Forward {duration: 0.01}
239                    }
240                    apply: {
241                        draw_bg: {down: 0.0, hover: [{time: 0.0, value: 1.0}],}
242                        draw_icon: {down: 0.0, hover: [{time: 0.0, value: 1.0}],}
243                        draw_text: {down: 0.0, hover: [{time: 0.0, value: 1.0}],}
244                    }
245                }
246                
247                down = {
248                    from: {all: Forward {duration: 0.2}}
249                    apply: {
250                        draw_bg: {down: [{time: 0.0, value: 1.0}], hover: 1.0,}
251                        draw_icon: {down: [{time: 0.0, value: 1.0}], hover: 1.0,}
252                        draw_text: {down: [{time: 0.0, value: 1.0}], hover: 1.0,}
253                    }
254                }
255            }
256            focus = {
257                default: off
258                off = {
259                    from: {all: Forward {duration: 0.2}}
260                    apply: {
261                        draw_bg: {focus: 0.0}
262                        draw_icon: {focus: 0.0}
263                        draw_text: {focus: 0.0}
264                    }
265                }
266                on = {
267                    cursor: Arrow,
268                    from: {all: Forward {duration: 0.0}}
269                    apply: {
270                        draw_bg: {focus: 1.0}
271                        draw_icon: {focus: 1.0}
272                        draw_text: {focus: 1.0}
273                    }
274                }
275            }
276        }
277    }
278    
279    pub ButtonGradientX = <Button> {
280        draw_bg: {
281            instance hover: 0.0
282            instance down: 0.0
283            instance enabled: 1.0
284            instance disabled: 1.0
285
286            border_size: (THEME_BEVELING)
287            border_radius: (THEME_CORNER_RADIUS)
288
289            color_dither: 1.0
290
291            uniform color_1: (THEME_COLOR_OUTSET_1)
292            uniform color_1_hover: (THEME_COLOR_OUTSET_1_HOVER)
293            uniform color_1_down: (THEME_COLOR_OUTSET_1_DOWN)
294            uniform color_1_focus: (THEME_COLOR_OUTSET_1_FOCUS)
295            uniform color_1_disabled: (THEME_COLOR_OUTSET_1_DISABLED)
296
297            uniform color_2: (THEME_COLOR_OUTSET_2)
298            uniform color_2_hover: (THEME_COLOR_OUTSET_2_HOVER)
299            uniform color_2_down: (THEME_COLOR_OUTSET_2_DOWN)
300            uniform color_2_focus: (THEME_COLOR_OUTSET_2_FOCUS)
301            uniform color_2_disabled: (THEME_COLOR_OUTSET_2_DISABLED)
302
303            border_color_1: (THEME_COLOR_BEVEL_OUTSET_1)
304            border_color_1_hover: (THEME_COLOR_BEVEL_OUTSET_1_HOVER)
305            border_color_1_down: (THEME_COLOR_BEVEL_OUTSET_1_DOWN)
306            border_color_1_focus: (THEME_COLOR_BEVEL_OUTSET_1_FOCUS)
307            border_color_1_disabled: (THEME_COLOR_BEVEL_OUTSET_1_DISABLED)
308
309            border_color_2: (THEME_COLOR_BEVEL_OUTSET_2)
310            border_color_2_hover: (THEME_COLOR_BEVEL_OUTSET_2_HOVER)
311            border_color_2_down: (THEME_COLOR_BEVEL_OUTSET_2_DOWN)
312            border_color_2_focus: (THEME_COLOR_BEVEL_OUTSET_2_FOCUS)
313            border_color_2_disabled: (THEME_COLOR_BEVEL_OUTSET_2_DISABLED)
314
315            fn pixel(self) -> vec4 {
316                let sdf = Sdf2d::viewport(self.pos * self.rect_size)
317                let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
318
319                let border_sz_uv = vec2(
320                    self.border_size / self.rect_size.x,
321                    self.border_size / self.rect_size.y
322                )
323
324                let gradient_border = vec2(
325                    self.pos.x + dither,
326                    self.pos.y + dither
327                )
328
329                let sz_inner_px = vec2(
330                    self.rect_size.x - self.border_size * 2.,
331                    self.rect_size.y - self.border_size * 2.
332                );
333
334                let scale_factor_fill = vec2(
335                    self.rect_size.x / sz_inner_px.x,
336                    self.rect_size.y / sz_inner_px.y
337                );
338
339                let gradient_fill = vec2(
340                    self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
341                    self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
342                )
343
344                sdf.box(
345                    self.border_size,
346                    self.border_size,
347                    self.rect_size.x - self.border_size * 2.,
348                    self.rect_size.y - self.border_size * 2.,
349                    self.border_radius
350                )
351
352                sdf.fill_keep(
353                    mix(
354                        mix(
355                            mix(
356                                mix(
357                                    mix(self.color_1, self.color_2, gradient_fill.x),
358                                    mix(self.color_1_focus, self.color_2_focus, gradient_fill.x),
359                                    self.focus
360                                ),
361                                mix(self.color_1_hover, self.color_2_hover, gradient_fill.x),
362                                self.hover
363                            ),
364                            mix(self.color_1_down, self.color_2_down, gradient_fill.x),
365                            self.down
366                        ),
367                        mix(self.color_1_disabled, self.color_2_disabled, gradient_fill.x),
368                        self.disabled
369                    )
370                )
371
372                sdf.stroke(
373                    mix(
374                        mix(
375                            mix(
376                                mix(
377                                    mix(self.border_color_1, self.border_color_2, gradient_border.y),
378                                    mix(self.border_color_1_focus, self.border_color_2_focus, gradient_border.y),
379                                    self.focus
380                                ),
381                                mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
382                                self.hover
383                            ),
384                            mix(self.border_color_1_down, self.border_color_2_down, gradient_border.y),
385                            self.down
386                            ),
387                            mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
388                            self.disabled
389                        ), self.border_size
390                )
391                return sdf.result
392            }
393        }
394    }
395
396    pub ButtonGradientY = <ButtonGradientX> {
397        draw_bg: {
398            fn pixel(self) -> vec4 {
399                let sdf = Sdf2d::viewport(self.pos * self.rect_size)
400                let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
401
402                let border_sz_uv = vec2(
403                    self.border_size / self.rect_size.x,
404                    self.border_size / self.rect_size.y
405                )
406
407                let gradient_border = vec2(
408                    self.pos.x + dither,
409                    self.pos.y + dither
410                )
411
412                let sz_inner_px = vec2(
413                    self.rect_size.x - self.border_size * 2.,
414                    self.rect_size.y - self.border_size * 2.
415                );
416
417                let scale_factor_fill = vec2(
418                    self.rect_size.x / sz_inner_px.x,
419                    self.rect_size.y / sz_inner_px.y
420                );
421
422                let gradient_fill = vec2(
423                    self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
424                    self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
425                )
426
427                sdf.box(
428                    self.border_size,
429                    self.border_size,
430                    self.rect_size.x - self.border_size * 2.,
431                    self.rect_size.y - self.border_size * 2.,
432                    self.border_radius
433                )
434
435                sdf.fill_keep(
436                    mix(
437                        mix(
438                            mix(
439                                mix(
440                                    mix(self.color_1, self.color_2, gradient_fill.y),
441                                    mix(self.color_1_focus, self.color_2_focus, gradient_fill.y),
442                                    self.focus
443                                ),
444                                mix(self.color_1_hover, self.color_2_hover, gradient_fill.y),
445                                self.hover
446                            ),
447                            mix(self.color_1_down, self.color_2_down, gradient_fill.y),
448                            self.down
449                        ),
450                        mix(self.color_1_disabled, self.color_2_disabled, gradient_fill.y),
451                        self.disabled
452                    )
453                )
454
455                sdf.stroke(
456                    mix(
457                        mix(
458                            mix(
459                                mix(
460                                    mix((THEME_COLOR_BEVEL_OUTSET_1), self.border_color_2, gradient_border.y),
461                                    mix(self.border_color_1_focus, self.border_color_2_focus, gradient_border.y),
462                                    self.focus
463                                ),
464                                mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
465                                self.hover
466                            ),
467                            mix(self.border_color_1_down, self.border_color_2_down, gradient_border.y),
468                            self.down
469                        ),
470                        mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
471                        self.disabled
472                    ), self.border_size
473                )
474
475                return sdf.result
476            }
477        }
478    }
479
480    pub ButtonFlat = <Button> {
481        draw_bg: {
482            color: (THEME_COLOR_OUTSET)
483            color_hover: (THEME_COLOR_OUTSET_HOVER)
484            color_down: (THEME_COLOR_OUTSET_DOWN)
485            color_disabled: (THEME_COLOR_OUTSET_DISABLED)
486
487            border_color_1: (THEME_COLOR_BEVEL)
488            border_color_1_hover: (THEME_COLOR_BEVEL_HOVER)
489            border_color_1_down: (THEME_COLOR_BEVEL_DOWN)
490            border_color_1_focus: (THEME_COLOR_BEVEL_FOCUS)
491            border_color_1_disabled: (THEME_COLOR_BEVEL_DISABLED)
492
493            border_color_2: (THEME_COLOR_BEVEL)
494            border_color_2_hover: (THEME_COLOR_BEVEL_HOVER)
495            border_color_2_down: (THEME_COLOR_BEVEL_DOWN)
496            border_color_2_focus: (THEME_COLOR_BEVEL_FOCUS)
497            border_color_2_disabled: (THEME_COLOR_BEVEL_DISABLED)
498
499        }
500        
501    }
502    
503    pub ButtonFlatter = <ButtonFlat> {
504        draw_bg: {
505            
506            color: (THEME_COLOR_U_HIDDEN)
507            color_hover: (THEME_COLOR_U_HIDDEN)
508            color_down: (THEME_COLOR_U_HIDDEN)
509            color_disabled: (THEME_COLOR_OUTSET_DISABLED)
510
511            border_color_1: (THEME_COLOR_U_HIDDEN)
512            border_color_1_hover: (THEME_COLOR_U_HIDDEN)
513            border_color_1_down: (THEME_COLOR_U_HIDDEN)
514            border_color_1_focus: (THEME_COLOR_U_HIDDEN)
515            border_color_1_disabled: (THEME_COLOR_U_HIDDEN)
516
517            border_color_2: (THEME_COLOR_U_HIDDEN)
518            border_color_2_hover: (THEME_COLOR_U_HIDDEN)
519            border_color_2_down: (THEME_COLOR_U_HIDDEN)
520            border_color_2_focus: (THEME_COLOR_U_HIDDEN)
521            border_color_2_disabled: (THEME_COLOR_U_HIDDEN)
522        }
523    }
524    
525    pub ButtonIcon = <Button> {
526        spacing: 0.
527        text: ""
528    }
529    
530    pub ButtonGradientXIcon = <ButtonGradientX> {
531        spacing: 0.
532        text: ""
533    }
534    
535    pub ButtonGradientYIcon = <ButtonGradientY> {
536        spacing: 0.
537        text: ""
538    }
539    
540    pub ButtonFlatIcon = <ButtonFlat> {
541        spacing: 0.
542        text: ""
543    }
544    
545    pub ButtonFlatterIcon = <ButtonFlatter> {
546        draw_bg: { color_focus: (THEME_COLOR_U_HIDDEN)}        
547        spacing: 0.
548        text: ""
549    }
550    
551}
552
553/// Actions emitted by a button widget, including the key modifiers
554/// that were active when the action occurred.
555///
556/// The sequence of actions emitted by a button is as follows:
557/// 1. `ButtonAction::Pressed` when the button is pressed.
558/// 2. `ButtonAction::LongPressed` when the button has been pressed for a long time.
559///    * This only occurs on platforms that support a *native* long press, e.g., mobile.
560/// 3. Then, either one of the following, but not both:
561///    * `ButtonAction::Clicked` when the mouse/finger is lifted up while over the button area.
562///    * `ButtonAction::Released` when the mouse/finger is lifted up while *not* over the button area.
563#[derive(Clone, Debug, DefaultNone)]
564pub enum ButtonAction {
565    None,
566    /// The button was pressed (a "down" event).
567    Pressed(KeyModifiers),
568    /// The button was pressed for a long time (only occurs on mobile platforms).
569    LongPressed,
570    /// The button was clicked (an "up" event).
571    Clicked(KeyModifiers),
572    /// The button was released (an "up" event), but should not be considered clicked
573    /// because the mouse/finger was not over the button area when released.
574    Released(KeyModifiers),
575}
576
577/// A clickable button widget that emits actions when pressed, and when either released or clicked.
578#[derive(Live, LiveHook, Widget)]
579pub struct Button {
580    #[animator]
581    animator: Animator,
582
583    #[redraw]
584    #[live]
585    draw_bg: DrawQuad,
586    #[live]
587    draw_text: DrawText,
588    #[live]
589    draw_icon: DrawIcon,
590    #[live]
591    icon_walk: Walk,
592    #[live]
593    label_walk: Walk,
594    #[walk]
595    walk: Walk,
596
597    #[layout]
598    layout: Layout,
599
600    #[live(true)]
601    grab_key_focus: bool,
602
603    #[live(true)]
604    enabled: bool,
605
606    #[live(true)]
607    #[visible] visible: bool,
608
609    /// Set the long-press handling behavior of this button.
610    /// * If `false` (default), the button will ignore long-press events
611    ///   and will never emit [`ButtonAction::LongPressed`].
612    ///   * Also, the button logic will *not* call [`FingerUpEvent::was_tap()`]
613    ///     to check if the button press was a short tap.
614    ///     This means that this button will consider itself to be clicked
615    ///     (and thus emit a [`ButtonAction::Clicked`] event)
616    ///     if the finger-up/release event occurs within the button area,
617    ///     *regardless* of how long the button was pressed down before it was released.
618    /// * If `true`, the button will respond to a long-press event
619    ///   by emitting [`ButtonAction::LongPressed`], which can only occur on
620    ///   mobile platforms that support a *native* long press event.
621    ///   * Also, the button will only consider itself to be clicked
622    ///     (and thus emit [`ButtonAction::Clicked`]) if [`FingerUpEvent::was_tap()`] returns `true`,
623    ///     meaning that a long press did *not* occur and that the button was released over the button area
624    ///     within a short time frame (~0.5 seconds) after the initial down press.
625    #[live]
626    pub enable_long_press: bool,
627
628    /// It indicates if the hover state will be reset when the button is clicked.
629    /// This could be useful for buttons that disappear when clicked, where the hover state
630    /// should not be preserved.
631    #[live]
632    reset_hover_on_click: bool,
633
634    #[live]
635    pub text: ArcStringMut,
636    
637    #[action_data] #[rust] action_data: WidgetActionData,
638}
639
640impl Widget for Button {
641    fn set_disabled(&mut self, cx:&mut Cx, disabled:bool){
642        self.animator_toggle(cx, disabled, Animate::Yes, id!(disabled.on), id!(disabled.off));
643    }
644                
645    fn disabled(&self, cx:&Cx) -> bool {
646        self.animator_in_state(cx, id!(disabled.on))
647    }
648
649    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
650        let uid = self.widget_uid();
651        if self.animator_handle_event(cx, event).must_redraw() {
652            self.draw_bg.redraw(cx);
653        }
654        
655        match event.hit_designer(cx, self.draw_bg.area()){
656            HitDesigner::DesignerPick(_e)=>{
657                cx.widget_action_with_data(&self.action_data, uid, &scope.path, WidgetDesignAction::PickedBody)
658            }
659            _=>()
660        }
661        
662        
663        // The button only handles hits when it's visible and enabled.
664        // If it's not enabled, we still show the button, but we set
665        // the NotAllowed mouse cursor upon hover instead of the Hand cursor.
666        match event.hits(cx, self.draw_bg.area()) {
667            Hit::KeyFocus(_) => {
668                self.animator_play(cx, id!(focus.on));
669            }
670            Hit::KeyFocusLost(_) => {
671                self.animator_play(cx, id!(focus.off));
672                self.draw_bg.redraw(cx);
673            }
674            Hit::FingerDown(fe) if self.enabled && fe.is_primary_hit() => {
675                if self.grab_key_focus {
676                    cx.set_key_focus(self.draw_bg.area());
677                }
678                cx.widget_action_with_data(&self.action_data, uid, &scope.path, ButtonAction::Pressed(fe.modifiers));
679                    self.animator_play(cx, id!(hover.down));
680                    self.set_key_focus(cx);
681            }
682            Hit::FingerHoverIn(_) => {
683                if self.enabled {
684                    cx.set_cursor(MouseCursor::Hand);
685                    self.animator_play(cx, id!(hover.on));
686                } else {
687                    cx.set_cursor(MouseCursor::NotAllowed);
688                }
689            }
690            Hit::FingerHoverOut(_) => {
691                self.animator_play(cx, id!(hover.off));
692            }
693            Hit::FingerLongPress(_lp) if self.enabled && self.enable_long_press => {
694                cx.widget_action_with_data(&self.action_data, uid, &scope.path, ButtonAction::LongPressed);
695            }
696            Hit::FingerUp(fe) if self.enabled && fe.is_primary_hit() => {
697                let was_clicked = fe.is_over && if self.enable_long_press { fe.was_tap() } else { true };
698                if was_clicked {
699                    cx.widget_action_with_data(&self.action_data, uid, &scope.path, ButtonAction::Clicked(fe.modifiers));
700                    if self.reset_hover_on_click {
701                        self.animator_cut(cx, id!(hover.off));
702                    } else if fe.has_hovers() {
703                        self.animator_play(cx, id!(hover.on));
704                    } else {
705                        self.animator_play(cx, id!(hover.off));
706                    }
707                } else {
708                    cx.widget_action_with_data(&self.action_data, uid, &scope.path, ButtonAction::Released(fe.modifiers));
709                    self.animator_play(cx, id!(hover.off));
710                }
711            }
712            _ => (),
713        }
714    }
715
716    fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
717        if !self.visible {
718            return DrawStep::done();
719        }
720
721        self.draw_bg.begin(cx, walk, self.layout);
722        self.draw_icon.draw_walk(cx, self.icon_walk);
723        self.draw_text
724            .draw_walk(cx, self.label_walk, Align::default(), self.text.as_ref());
725        self.draw_bg.end(cx);
726        cx.add_nav_stop(self.draw_bg.area(), NavRole::TextInput, Margin::default());
727        DrawStep::done()
728    }
729
730    fn text(&self) -> String {
731        self.text.as_ref().to_string()
732    }
733
734    fn set_text(&mut self, cx:&mut Cx, v: &str) {
735        self.text.as_mut_empty().push_str(v);
736        self.redraw(cx);
737    }
738}
739
740impl Button {
741        
742    pub fn draw_button(&mut self, cx: &mut Cx2d, label:&str) {
743        self.draw_bg.begin(cx, self.walk, self.layout);
744        self.draw_icon.draw_walk(cx, self.icon_walk);
745        self.draw_text
746            .draw_walk(cx, self.label_walk, Align::default(), label);
747        self.draw_bg.end(cx);
748    }
749    
750    /// Returns `true` if this button was clicked.
751    ///
752    /// See [`ButtonAction`] for more details.
753    pub fn clicked(&self, actions: &Actions) -> bool {
754        self.clicked_modifiers(actions).is_some()
755    }
756
757    /// Returns `true` if this button was pressed down.
758    ///
759    /// See [`ButtonAction`] for more details.
760    pub fn pressed(&self, actions: &Actions) -> bool {
761        self.pressed_modifiers(actions).is_some()
762    }
763
764    /// Returns `true` if this button was long-pressed on.
765    ///
766    /// Note that this does not mean the button has been released yet.
767    /// See [`ButtonAction`] for more details.
768    pub fn long_pressed(&self, actions: &Actions) -> bool {
769        matches!(
770            actions.find_widget_action(self.widget_uid()).cast_ref(),
771            ButtonAction::LongPressed,
772        )
773    }
774
775    /// Returns `true` if this button was released, which is *not* considered to be clicked.
776    ///
777    /// See [`ButtonAction`] for more details.
778    pub fn released(&self, actions: &Actions) -> bool {
779        self.released_modifiers(actions).is_some()
780    }
781
782    /// Returns `Some` (with active keyboard modifiers) if this button was clicked.
783    ///
784    /// See [`ButtonAction`] for more details.
785    pub fn clicked_modifiers(&self, actions: &Actions) -> Option<KeyModifiers> {
786        if let ButtonAction::Clicked(m) = actions.find_widget_action(self.widget_uid()).cast_ref() {
787            Some(*m)
788        } else {
789            None
790        }
791    }
792
793    /// Returns `Some` (with active keyboard modifiers) if this button was pressed down.
794    ///
795    /// See [`ButtonAction`] for more details.
796    pub fn pressed_modifiers(&self, actions: &Actions) -> Option<KeyModifiers> {
797        if let ButtonAction::Pressed(m) = actions.find_widget_action(self.widget_uid()).cast_ref() {
798            Some(*m)
799        } else {
800            None
801        }
802    }
803
804    /// Returns `Some` (with active keyboard modifiers) if this button was released,
805    /// which is *not* considered to be clicked.
806    ///
807    /// See [`ButtonAction`] for more details.
808    pub fn released_modifiers(&self, actions: &Actions) -> Option<KeyModifiers> {
809        if let ButtonAction::Released(m) = actions.find_widget_action(self.widget_uid()).cast_ref() {
810            Some(*m)
811        } else {
812            None
813        }
814    }
815}
816
817impl ButtonRef {
818    /// See [`Button::clicked()`].
819    pub fn clicked(&self, actions: &Actions) -> bool {
820        self.borrow().is_some_and(|inner| inner.clicked(actions))
821    }
822
823    /// See [`Button::pressed()`].
824    pub fn pressed(&self, actions: &Actions) -> bool {
825        self.borrow().is_some_and(|inner| inner.pressed(actions))
826    }
827
828    /// See [`Button::long_pressed()`].
829    pub fn long_pressed(&self, actions: &Actions) -> bool {
830        self.borrow().is_some_and(|inner| inner.long_pressed(actions))
831    }
832
833    /// See [`Button::released()`].
834    pub fn released(&self, actions: &Actions) -> bool {
835        self.borrow().is_some_and(|inner| inner.released(actions))
836    }
837
838    /// See [`Button::clicked_modifiers()`].
839    pub fn clicked_modifiers(&self, actions: &Actions) -> Option<KeyModifiers> {
840        self.borrow().and_then(|inner| inner.clicked_modifiers(actions))
841    }
842
843    /// See [`Button::pressed_modifiers()`].
844    pub fn pressed_modifiers(&self, actions: &Actions) ->  Option<KeyModifiers> {
845        self.borrow().and_then(|inner| inner.pressed_modifiers(actions))
846    }
847
848    /// See [`Button::released_modifiers()`].
849    pub fn released_modifiers(&self, actions: &Actions) -> Option<KeyModifiers> {
850        self.borrow().and_then(|inner| inner.released_modifiers(actions))
851    }
852
853    pub fn set_visible(&self, cx: &mut Cx, visible: bool) {
854        if let Some(mut inner) = self.borrow_mut() {
855            inner.visible = visible;
856            inner.redraw(cx);
857        }
858    }
859
860    pub fn set_enabled(&self, cx: &mut Cx, enabled: bool) {
861        if let Some(mut inner) = self.borrow_mut() {
862            inner.enabled = enabled;
863            inner.redraw(cx);
864        }
865    }
866
867    /// Resets the hover state of this button.
868    ///
869    /// This is useful in certain cases where the hover state should be reset 
870    /// (cleared) regardelss of whether the mouse is over it.
871    pub fn reset_hover(&self, cx: &mut Cx) {
872        if let Some(mut inner) = self.borrow_mut() {
873            inner.animator_cut(cx, id!(hover.off));
874        }
875    }
876}
877
878impl ButtonSet {
879    pub fn clicked(&self, actions: &Actions) -> bool {
880        self.iter().any(|v| v.clicked(actions))
881    }
882    pub fn pressed(&self, actions: &Actions) -> bool {
883        self.iter().any(|v| v.pressed(actions))
884    }
885    pub fn released(&self, actions: &Actions) -> bool {
886        self.iter().any(|v| v.released(actions))
887    }
888
889    pub fn reset_hover(&self, cx: &mut Cx) {
890        for item in self.iter() {
891            item.reset_hover(cx)
892        }
893    }
894    
895    pub fn which_clicked_modifiers(&self, actions: &Actions) -> Option<(usize,KeyModifiers)> {
896        for (index,btn) in self.iter().enumerate(){
897            if let Some(km) = btn.clicked_modifiers(actions){
898                return Some((index, km))
899            }
900        }
901        None
902    }
903
904    pub fn set_visible(&self, cx:&mut Cx, visible: bool) {
905        for item in self.iter() {
906            item.set_visible(cx, visible)
907        }
908    }
909    pub fn set_enabled(&self, cx:&mut Cx, enabled: bool) {
910        for item in self.iter() {
911            item.set_enabled(cx, enabled)
912        }
913    }
914}