makepad_widgets/
drop_down.rs

1use {
2    std::rc::Rc,
3    std::cell::RefCell,
4    crate::{
5        makepad_derive_widget::*,
6        popup_menu::{PopupMenu, PopupMenuAction},
7        makepad_draw::*,
8        widget::*,
9    }
10};
11
12live_design!{
13    link widgets;
14    use link::theme::*;
15    use link::shaders::*;
16    use crate::popup_menu::PopupMenu;
17    use crate::popup_menu::PopupMenuFlat;
18    use crate::popup_menu::PopupMenuFlatter;
19    use crate::popup_menu::PopupMenuGradientX;
20    use crate::popup_menu::PopupMenuGradientY;
21    
22    pub DrawLabelText = {{DrawLabelText}} {}
23    pub DropDownBase = {{DropDown}} {}
24    
25    pub DropDown = <DropDownBase> {
26        width: Fit, height: Fit,
27        align: {x: 0., y: 0.}
28
29        padding: <THEME_MSPACE_1> { left: (THEME_SPACE_2), right: 22.5 }
30        margin: <THEME_MSPACE_V_1> {}
31        
32        draw_text: {
33            instance disabled: 0.0,
34            instance down: 0.0,
35
36            uniform color: (THEME_COLOR_LABEL_INNER)
37            uniform color_hover: (THEME_COLOR_LABEL_INNER_HOVER)
38            uniform color_focus: (THEME_COLOR_LABEL_INNER_FOCUS)
39            uniform color_down: (THEME_COLOR_LABEL_INNER_DOWN)
40            uniform color_disabled: (THEME_COLOR_LABEL_INNER_DISABLED)
41
42            text_style: <THEME_FONT_REGULAR> {
43                font_size: (THEME_FONT_SIZE_P)
44            }
45            
46            fn get_color(self) -> vec4 {
47                return mix(
48                    mix(
49                        mix(
50                            self.color,
51                            mix(
52                                self.color_focus,
53                                self.color_hover,
54                                self.hover
55                            ),
56                            self.focus
57                        ),
58                        mix(
59                            self.color_hover,
60                            self.color_down,
61                            self.down
62                        ),
63                        self.hover
64                    ),
65                    self.color_disabled,
66                    self.disabled
67                )
68            }
69        }
70        
71        draw_bg: {
72            instance hover: 0.0
73            instance focus: 0.0
74            instance active: 0.0
75            instance disabled: 0.0
76            instance down: 0.0
77                        
78            uniform border_size: (THEME_BEVELING)
79            uniform border_radius: (THEME_CORNER_RADIUS)
80
81            uniform color_dither: 1.0
82
83            uniform color: (THEME_COLOR_OUTSET)
84            uniform color_hover: (THEME_COLOR_OUTSET_HOVER)
85            uniform color_down: (THEME_COLOR_OUTSET_DOWN)
86            uniform color_focus: (THEME_COLOR_OUTSET_FOCUS)
87            uniform color_disabled: (THEME_COLOR_OUTSET_DISABLED)
88
89            uniform border_color_1: (THEME_COLOR_BEVEL_OUTSET_1)
90            uniform border_color_1_hover: (THEME_COLOR_BEVEL_OUTSET_1_HOVER)
91            uniform border_color_1_focus: (THEME_COLOR_BEVEL_OUTSET_1_FOCUS)
92            uniform border_color_1_down: (THEME_COLOR_BEVEL_OUTSET_1_DOWN)
93            uniform border_color_1_disabled: (THEME_COLOR_BEVEL_OUTSET_1_DISABLED)
94
95            uniform border_color_2: (THEME_COLOR_BEVEL_OUTSET_2)
96            uniform border_color_2_hover: (THEME_COLOR_BEVEL_OUTSET_2_HOVER)
97            uniform border_color_2_focus: (THEME_COLOR_BEVEL_OUTSET_2_FOCUS)
98            uniform border_color_2_down: (THEME_COLOR_BEVEL_OUTSET_2_DOWN)
99            uniform border_color_2_disabled: (THEME_COLOR_BEVEL_OUTSET_2_DISABLED)
100
101            uniform arrow_color: (THEME_COLOR_LABEL_INNER)
102            uniform arrow_color_hover: (THEME_COLOR_LABEL_INNER_HOVER)
103            uniform arrow_color_focus: (THEME_COLOR_LABEL_INNER_FOCUS)
104            uniform arrow_color_down: (THEME_COLOR_LABEL_INNER_DOWN)
105            uniform arrow_color_disabled: (THEME_COLOR_LABEL_INNER_DISABLED)
106            
107            fn pixel(self) -> vec4 {
108                let sdf = Sdf2d::viewport(self.pos * self.rect_size);
109                let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
110
111                // lets draw a little triangle in the corner
112                let c = vec2(self.rect_size.x - 10.0, self.rect_size.y * 0.5)
113                let sz = 2.5;
114                let offset = 1.;
115                let offset_x = 2.;
116                
117                sdf.move_to(c.x - sz - offset_x, c.y - sz + offset);
118                sdf.line_to(c.x + sz - offset_x, c.y - sz + offset);
119                sdf.line_to(c.x - offset_x, c.y + sz * 0.25 + offset);
120                sdf.close_path();
121                
122                sdf.fill_keep(
123                    mix(
124                        mix(
125                            mix(
126                                self.arrow_color,
127                                self.arrow_color_focus,
128                                self.focus
129                            ),
130                            mix(
131                                self.arrow_color_hover,
132                                self.arrow_color_down,
133                                self.down
134                            ),
135                            self.hover
136                        ),
137                        self.arrow_color_disabled,
138                        self.disabled
139                    )
140                );
141
142                let border_sz_uv = vec2(
143                    self.border_size / self.rect_size.x,
144                    self.border_size / self.rect_size.y
145                )
146
147                let gradient_border = vec2(
148                    self.pos.x + dither,
149                    self.pos.y + dither
150                )
151
152                let sz_inner_px = vec2(
153                    self.rect_size.x - self.border_size * 2.,
154                    self.rect_size.y - self.border_size * 2.
155                );
156
157                let scale_factor_fill = vec2(
158                    self.rect_size.x / sz_inner_px.x,
159                    self.rect_size.y / sz_inner_px.y
160                );
161
162                let gradient_fill = vec2(
163                    self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
164                    self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
165                )
166
167                sdf.box(
168                    self.border_size,
169                    self.border_size,
170                    self.rect_size.x - self.border_size * 2.,
171                    self.rect_size.y - self.border_size * 2.,
172                    self.border_radius
173                )
174
175                sdf.fill_keep(
176                    mix(
177                        mix(
178                            mix(
179                                self.color,
180                                self.color_focus,
181                                self.focus
182                            ),
183                            mix(
184                                self.color_hover,
185                                self.color_down,
186                                self.down
187                            ),
188                            self.hover
189                        ),
190                        self.color_disabled,
191                        self.disabled
192                    )
193                )
194
195                sdf.stroke(
196                    mix(
197                        mix(
198                            mix(
199                                mix(self.border_color_1, self.border_color_2, gradient_border.y),
200                                mix(self.border_color_1_focus, self.border_color_2_focus, gradient_border.y),
201                                self.focus
202                            ),
203                            mix(
204                                mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
205                                mix(self.border_color_1_down, self.border_color_2_down, gradient_border.y),
206                                self.down
207                            ),
208                            self.hover
209                        ),
210                        mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
211                        self.disabled
212                    ), self.border_size
213                )
214
215
216
217                return sdf.result
218            }
219        }
220        
221        popup_menu: <PopupMenu> {}
222        
223        selected_item: 0,
224        
225        animator: {
226            disabled = {
227                default: off,
228                off = {
229                    from: {all: Forward {duration: 0.}}
230                    apply: {
231                        draw_bg: {disabled: 0.0}
232                        draw_text: {disabled: 0.0}
233                    }
234                }
235                on = {
236                    from: {all: Forward {duration: 0.2}}
237                    apply: {
238                        draw_bg: {disabled: 1.0}
239                        draw_text: {disabled: 1.0}
240                    }
241                }
242            }
243            hover = {
244                default: off,
245                off = {
246                    from: {all: Forward {duration: 0.1}}
247                    apply: {
248                        draw_bg: {down: 0.0, hover: 0.0}
249                        draw_text: {down: 0.0, hover: 0.0}
250                    }
251                }
252                
253                on = {
254                    from: {
255                        all: Forward {duration: 0.1}
256                        down: Forward {duration: 0.01}
257                    }
258                    apply: {
259                        draw_bg: {down: 0.0, hover: [{time: 0.0, value: 1.0}],}
260                        draw_text: {down: 0.0, hover: [{time: 0.0, value: 1.0}],}
261                    }
262                }
263                
264                down = {
265                    from: {all: Forward {duration: 0.2}}
266                    apply: {
267                        draw_bg: {down: [{time: 0.0, value: 1.0}], hover: 1.0,}
268                        draw_text: {down: [{time: 0.0, value: 1.0}], hover: 1.0,}
269                    }
270                }
271            }
272            focus = {
273                default: off
274                off = {
275                    from: {all: Forward {duration: 0.2}}
276                    apply: {
277                        draw_bg: {focus: 0.0}
278                        draw_text: {focus: 0.0}
279                    }
280                }
281                on = {
282                    cursor: Arrow,
283                    from: {all: Forward {duration: 0.0}}
284                    apply: {
285                        draw_bg: {focus: 1.0}
286                        draw_text: {focus: 1.0}
287                    }
288                }
289            }
290        }
291    }
292    
293    pub DropDownFlat = <DropDown> {
294        draw_bg: {
295            color: (THEME_COLOR_OUTSET)
296            color_hover: (THEME_COLOR_OUTSET_HOVER)
297            color_focus: (THEME_COLOR_OUTSET_FOCUS)
298            color_down: (THEME_COLOR_OUTSET_DOWN)
299            color_disabled: (THEME_COLOR_U_HIDDEN)
300
301            border_color_1: (THEME_COLOR_BEVEL)
302            border_color_1_hover: (THEME_COLOR_BEVEL_HOVER)
303            border_color_1_focus: (THEME_COLOR_BEVEL_FOCUS)
304            border_color_1_down: (THEME_COLOR_BEVEL_DOWN)
305            border_color_1_disabled: (THEME_COLOR_BEVEL_DISABLED)
306
307            border_color_2: (THEME_COLOR_BEVEL)
308            border_color_2_hover: (THEME_COLOR_BEVEL_HOVER)
309            border_color_2_focus: (THEME_COLOR_BEVEL_FOCUS)
310            border_color_2_down: (THEME_COLOR_BEVEL_DOWN)
311            border_color_2_disabled: (THEME_COLOR_BEVEL_DISABLED)
312        }
313
314        popup_menu: <PopupMenuFlat> {}
315    }
316
317    pub DropDownFlatter = <DropDownFlat> {
318        draw_bg: {
319            border_size: 0.
320            color: (THEME_COLOR_U_HIDDEN)
321            color_hover: (THEME_COLOR_U_HIDDEN)
322            color_down: (THEME_COLOR_U_HIDDEN)
323        }
324        popup_menu: <PopupMenuFlatter> {}
325    }
326
327
328    pub DropDownGradientX = <DropDown> {
329        popup_menu: <PopupMenuGradientX> {}
330
331        draw_bg: {
332            instance hover: 0.0
333            instance focus: 0.0
334            instance down: 0.0
335                        
336            uniform border_size: (THEME_BEVELING)
337            uniform border_radius: (THEME_CORNER_RADIUS)
338
339            uniform color_dither: 1.0
340
341            uniform color_1: (THEME_COLOR_OUTSET_1)
342            uniform color_1_hover: (THEME_COLOR_OUTSET_1_HOVER)
343            uniform color_1_focus: (THEME_COLOR_OUTSET_1_FOCUS)
344            uniform color_1_down: (THEME_COLOR_OUTSET_1_DOWN)
345            uniform color_1_disabled: (THEME_COLOR_OUTSET_1_DISABLED)
346
347            uniform color_2: (THEME_COLOR_OUTSET_2)
348            uniform color_2_hover: (THEME_COLOR_OUTSET_2_HOVER)
349            uniform color_2_focus: (THEME_COLOR_OUTSET_2_FOCUS)
350            uniform color_2_down: (THEME_COLOR_OUTSET_2_DOWN)
351            uniform color_2_disabled: (THEME_COLOR_OUTSET_2_DISABLED)
352
353            uniform border_color_1: (THEME_COLOR_BEVEL_OUTSET_1)
354            uniform border_color_1_hover: (THEME_COLOR_BEVEL_OUTSET_1_HOVER)
355            uniform border_color_1_focus: (THEME_COLOR_BEVEL_OUTSET_1_FOCUS)
356            uniform border_color_1_down: (THEME_COLOR_BEVEL_OUTSET_1_DOWN)
357            uniform border_color_1_disabled: (THEME_COLOR_BEVEL_OUTSET_1_DISABLED)
358
359            uniform border_color_2: (THEME_COLOR_BEVEL_OUTSET_2)
360            uniform border_color_2_hover: (THEME_COLOR_BEVEL_OUTSET_2_HOVER)
361            uniform border_color_2_focus: (THEME_COLOR_BEVEL_OUTSET_2_FOCUS)
362            uniform border_color_2_down: (THEME_COLOR_BEVEL_OUTSET_2_DOWN)
363            uniform border_color_2_disabled: (THEME_COLOR_BEVEL_OUTSET_2_DISABLED)
364
365            uniform arrow_color: (THEME_COLOR_LABEL_INNER)
366            uniform arrow_color_focus: (THEME_COLOR_LABEL_INNER_FOCUS)
367            uniform arrow_color_hover: (THEME_COLOR_LABEL_INNER_HOVER)
368            uniform arrow_color_down: (THEME_COLOR_LABEL_INNER_DOWN)
369            uniform arrow_color_disabled: (THEME_COLOR_LABEL_INNER_DISABLED)
370            
371            fn pixel(self) -> vec4 {
372                let sdf = Sdf2d::viewport(self.pos * self.rect_size);
373                let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
374
375                // lets draw a little triangle in the corner
376                let c = vec2(self.rect_size.x - 10.0, self.rect_size.y * 0.5)
377                let sz = 2.5;
378                let offset = 1.;
379                let offset_x = 2.;
380                
381                sdf.move_to(c.x - sz - offset_x, c.y - sz + offset);
382                sdf.line_to(c.x + sz - offset_x, c.y - sz + offset);
383                sdf.line_to(c.x - offset_x, c.y + sz * 0.25 + offset);
384                sdf.close_path();
385                
386                sdf.fill_keep(
387                    mix(
388                        mix(
389                            mix(
390                                self.arrow_color,
391                                self.arrow_color_focus,
392                                self.focus
393                            ),
394                            mix(
395                                self.arrow_color_hover,
396                                self.arrow_color_down,
397                                self.down
398                            ),
399                            self.hover
400                        ),
401                        self.arrow_color_disabled,
402                        self.disabled
403                    )
404                );
405
406                let border_sz_uv = vec2(
407                    self.border_size / self.rect_size.x,
408                    self.border_size / self.rect_size.y
409                )
410
411                let gradient_border = vec2(
412                    self.pos.x + dither,
413                    self.pos.y + dither
414                )
415
416                let sz_inner_px = vec2(
417                    self.rect_size.x - self.border_size * 2.,
418                    self.rect_size.y - self.border_size * 2.
419                );
420
421                let scale_factor_fill = vec2(
422                    self.rect_size.x / sz_inner_px.x,
423                    self.rect_size.y / sz_inner_px.y
424                );
425
426                let gradient_fill = vec2(
427                    self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
428                    self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
429                )
430
431                sdf.box(
432                    self.border_size,
433                    self.border_size,
434                    self.rect_size.x - self.border_size * 2.,
435                    self.rect_size.y - self.border_size * 2.,
436                    self.border_radius
437                )
438
439                sdf.fill_keep(
440                    mix(
441                        mix(
442                            mix(
443                                mix(self.color_1, self.color_2, gradient_fill.x),
444                                mix(self.color_1_focus, self.color_2_focus, gradient_fill.x),
445                                self.focus
446                            ),
447                            mix(
448                                mix(self.color_1_hover, self.color_2_hover, gradient_fill.x),
449                                mix(self.color_1_down, self.color_2_down, gradient_fill.x),
450                                self.down
451                            ),
452                            self.hover
453                        ),
454                        mix(self.color_1_disabled, self.color_2_disabled, gradient_fill.x),
455                        self.disabled
456                    )
457                )
458
459                sdf.stroke(
460                    mix(
461                        mix(
462                            mix(
463                                mix(self.border_color_1, self.border_color_2, gradient_border.y),
464                                mix(self.border_color_1_focus, self.border_color_2_focus, gradient_border.y),
465                                self.focus
466                            ),
467                            mix(
468                                mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
469                                mix(self.border_color_1_down, self.border_color_2_down, gradient_border.y),
470                                self.down
471                            ),
472                            self.hover
473                        ),
474                        mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
475                        self.disabled
476                    ), self.border_size
477                )
478                
479                return sdf.result
480            }
481        }    
482    }
483
484
485    pub DropDownGradientY = <DropDownGradientX> {
486        popup_menu: <PopupMenuGradientY> {}
487        
488        draw_bg: {
489            fn pixel(self) -> vec4 {
490                let sdf = Sdf2d::viewport(self.pos * self.rect_size);
491                let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
492
493                // lets draw a little triangle in the corner
494                let c = vec2(self.rect_size.x - 10.0, self.rect_size.y * 0.5)
495                let sz = 2.5;
496                let offset = 1.;
497                let offset_x = 2.;
498                
499                sdf.move_to(c.x - sz - offset_x, c.y - sz + offset);
500                sdf.line_to(c.x + sz - offset_x, c.y - sz + offset);
501                sdf.line_to(c.x - offset_x, c.y + sz * 0.25 + offset);
502                sdf.close_path();
503                
504                sdf.fill_keep(
505                    mix(
506                        mix(
507                            mix(
508                                self.arrow_color,
509                                self.arrow_color_focus,
510                                self.focus
511                            ),
512                            mix(
513                                self.arrow_color_hover,
514                                self.arrow_color_down,
515                                self.down
516                            ),
517                            self.hover
518                        ),
519                        self.arrow_color_disabled,
520                        self.disabled
521                    )
522                );
523
524                let border_sz_uv = vec2(
525                    self.border_size / self.rect_size.x,
526                    self.border_size / self.rect_size.y
527                )
528
529                let gradient_border = vec2(
530                    self.pos.x + dither,
531                    self.pos.y + dither
532                )
533
534                let sz_inner_px = vec2(
535                    self.rect_size.x - self.border_size * 2.,
536                    self.rect_size.y - self.border_size * 2.
537                );
538
539                let scale_factor_fill = vec2(
540                    self.rect_size.x / sz_inner_px.x,
541                    self.rect_size.y / sz_inner_px.y
542                );
543
544                let gradient_fill = vec2(
545                    self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
546                    self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
547                )
548
549                sdf.box(
550                    self.border_size,
551                    self.border_size,
552                    self.rect_size.x - self.border_size * 2.,
553                    self.rect_size.y - self.border_size * 2.,
554                    self.border_radius
555                )
556
557                sdf.fill_keep(
558                    mix(
559                        mix(
560                            mix(
561                                mix(self.color_1, self.color_2, gradient_fill.y),
562                                mix(self.color_1_focus, self.color_2_focus, gradient_fill.y),
563                                self.focus
564                            ),
565                            mix(
566                                mix(self.color_1_hover, self.color_2_hover, gradient_fill.y),
567                                mix(self.color_1_down, self.color_2_down, gradient_fill.y),
568                                self.down
569                            ),
570                            self.hover
571                        ),
572                        mix(self.color_1_disabled, self.color_2_disabled, gradient_fill.y),
573                        self.disabled
574                    )
575                )
576
577                sdf.stroke(
578                    mix(
579                        mix(
580                            mix(
581                                mix(self.border_color_1, self.border_color_2, gradient_border.y),
582                                mix(self.border_color_1_focus, self.border_color_2_focus, gradient_border.y),
583                                self.focus
584                            ),
585                            mix(
586                                mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
587                                mix(self.border_color_1_down, self.border_color_2_down, gradient_border.y),
588                                self.down
589                            ),
590                            self.hover
591                        ),
592                        mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
593                        self.disabled
594                    ), self.border_size
595                )
596                
597                return sdf.result
598            }
599        }     
600    }
601
602}
603
604#[derive(Copy, Clone, Debug, Live, LiveHook)]
605#[live_ignore]
606pub enum PopupMenuPosition {
607    #[pick] OnSelected,
608    BelowInput,
609}
610
611#[derive(Live, Widget)]
612pub struct DropDown {
613    #[animator] animator: Animator,
614    
615    #[redraw] #[live] draw_bg: DrawQuad,
616    #[live] draw_text: DrawLabelText,
617    
618    #[walk] walk: Walk,
619    
620    #[live] bind: String,
621    #[live] bind_enum: String,
622    
623    #[live] popup_menu: Option<LivePtr>,
624    
625    #[live] labels: Vec<String>,
626    #[live] values: Vec<LiveValue>,
627    
628    #[live] popup_menu_position: PopupMenuPosition,
629    
630    #[rust] is_active: bool,
631    
632    #[live] selected_item: usize,
633    
634    #[layout] layout: Layout,
635}
636
637#[derive(Default, Clone)]
638struct PopupMenuGlobal {
639    map: Rc<RefCell<ComponentMap<LivePtr, PopupMenu >> >
640}
641
642#[derive(Live, LiveHook, LiveRegister)]#[repr(C)]
643struct DrawLabelText {
644    #[deref] draw_super: DrawText,
645    #[live] focus: f32,
646    #[live] hover: f32,
647}
648
649impl LiveHook for DropDown {
650    fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
651        if self.popup_menu.is_none() || !apply.from.is_from_doc() {
652            return
653        }
654        let global = cx.global::<PopupMenuGlobal>().clone();
655        let mut map = global.map.borrow_mut();
656        
657        // when live styling clean up old style references
658        map.retain( | k, _ | cx.live_registry.borrow().generation_valid(*k));
659        
660        let list_box = self.popup_menu.unwrap();
661        map.get_or_insert(cx, list_box, | cx | {
662            PopupMenu::new_from_ptr(cx, Some(list_box))
663        });
664        
665    }
666}
667#[derive(Clone, Debug, DefaultNone)]
668pub enum DropDownAction {
669    Select(usize, LiveValue),
670    None
671}
672
673impl DropDown {
674    
675    pub fn set_active(&mut self, cx: &mut Cx) {
676        self.is_active = true;
677        self.draw_bg.apply_over(cx, live!{active: 1.0});
678        self.draw_bg.redraw(cx);
679        let global = cx.global::<PopupMenuGlobal>().clone();
680        let mut map = global.map.borrow_mut();
681        let lb = map.get_mut(&self.popup_menu.unwrap()).unwrap();
682        let node_id = LiveId(self.selected_item as u64).into();
683        lb.init_select_item(node_id);
684        cx.sweep_lock(self.draw_bg.area());
685    }
686    
687    pub fn set_closed(&mut self, cx: &mut Cx) {
688        self.is_active = false;
689        self.draw_bg.apply_over(cx, live!{active: 0.0});
690        self.draw_bg.redraw(cx);
691        cx.sweep_unlock(self.draw_bg.area());
692    }
693    
694    pub fn draw_text(&mut self, cx: &mut Cx2d, label: &str) {
695        self.draw_bg.begin(cx, self.walk, self.layout);
696        self.draw_text.draw_walk(cx, Walk::fit(), Align::default(), label);
697        self.draw_bg.end(cx);
698    }
699    
700    pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk) {
701        //cx.clear_sweep_lock(self.draw_bg.area());
702        // ok so what if. what do we have
703        // we have actions
704        // and we have applying states/values in response
705       
706        self.draw_bg.begin(cx, walk, self.layout);
707        //let start_pos = cx.turtle().rect().pos;
708        
709        if let Some(val) = self.labels.get(self.selected_item) {
710            self.draw_text.draw_walk(cx, Walk::fit(), Align::default(), val);
711        }
712        else {
713            self.draw_text.draw_walk(cx, Walk::fit(), Align::default(), " ");
714        }
715        self.draw_bg.end(cx);
716        
717        cx.add_nav_stop(self.draw_bg.area(), NavRole::DropDown, Margin::default());
718        
719        if self.is_active && self.popup_menu.is_some() {
720            //cx.set_sweep_lock(self.draw_bg.area());
721            // ok so if self was not active, we need to
722            // ok so how will we solve this one
723            let global = cx.global::<PopupMenuGlobal>().clone();
724            let mut map = global.map.borrow_mut();
725            let popup_menu = map.get_mut(&self.popup_menu.unwrap()).unwrap();
726
727            // we kinda need to draw it twice.
728            popup_menu.begin(cx);
729
730            match self.popup_menu_position {
731                PopupMenuPosition::OnSelected => {
732                    let mut item_pos = None;
733                    for (i, item) in self.labels.iter().enumerate() {
734                        let node_id = LiveId(i as u64).into();
735                        if i == self.selected_item {
736                            item_pos = Some(cx.turtle().pos());
737                        }
738                        popup_menu.draw_item(cx, node_id, &item);
739                    }
740                    
741                    // ok we shift the entire menu. however we shouldnt go outside the screen area
742                    popup_menu.end(cx, self.draw_bg.area(), -item_pos.unwrap_or(dvec2(0.0, 0.0)));
743                }
744                PopupMenuPosition::BelowInput => {
745                    for (i, item) in self.labels.iter().enumerate() {
746                        let node_id = LiveId(i as u64).into();
747                        popup_menu.draw_item(cx, node_id, &item);
748                    }
749
750                    let area = self.draw_bg.area().rect(cx);
751                    let shift = DVec2 {
752                        x: 0.0,
753                        y: area.size.y,
754                    };
755                    
756                    popup_menu.end(cx, self.draw_bg.area(), shift);
757                }
758            }
759        }
760    }
761}
762
763impl Widget for DropDown {
764    fn set_disabled(&mut self, cx:&mut Cx, disabled:bool){
765        self.animator_toggle(cx, disabled, Animate::Yes, id!(disabled.on), id!(disabled.off));
766    }
767                
768    fn disabled(&self, cx:&Cx) -> bool {
769        self.animator_in_state(cx, id!(disabled.on))
770    }
771    
772    fn widget_to_data(&self, _cx: &mut Cx, actions: &Actions, nodes: &mut LiveNodeVec, path: &[LiveId]) -> bool {
773        match actions.find_widget_action_cast(self.widget_uid()) {
774            DropDownAction::Select(_, value) => {
775                nodes.write_field_value(path, value.clone());
776                true
777            }
778            _ => false
779        }
780    }
781    
782    fn data_to_widget(&mut self, cx: &mut Cx, nodes: &[LiveNode], path: &[LiveId]) {
783        if let Some(value) = nodes.read_field_value(path) {
784            if let Some(index) = self.values.iter().position( | v | v == value) {
785                if self.selected_item != index {
786                    self.selected_item = index;
787                    self.redraw(cx);
788                }
789            }
790            else {
791                error!("Value not in values list {:?}", value);
792            }
793        }
794    }
795    
796    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope)  {
797        self.animator_handle_event(cx, event);
798        let uid = self.widget_uid();
799                
800        if self.is_active && self.popup_menu.is_some() {
801            // ok so how will we solve this one
802            let global = cx.global::<PopupMenuGlobal>().clone();
803            let mut map = global.map.borrow_mut();
804            let menu = map.get_mut(&self.popup_menu.unwrap()).unwrap();
805            let mut close = false;
806            menu.handle_event_with(cx, event, self.draw_bg.area(), &mut | cx, action | {
807                match action {
808                    PopupMenuAction::WasSweeped(_node_id) => {
809                        //dispatch_action(cx, PopupMenuAction::WasSweeped(node_id));
810                    }
811                    PopupMenuAction::WasSelected(node_id) => {
812                        //dispatch_action(cx, PopupMenuAction::WasSelected(node_id));
813                        self.selected_item = node_id.0.0 as usize;
814                        cx.widget_action(uid, &scope.path, DropDownAction::Select(self.selected_item, self.values.get(self.selected_item).cloned().unwrap_or(LiveValue::None)));
815                        self.draw_bg.redraw(cx);
816                        close = true;
817                    }
818                    _ => ()
819                }
820            });
821            if close {
822                self.set_closed(cx);
823            }
824                        
825            // check if we clicked outside of the popup menu
826            if let Event::MouseDown(e) = event {
827                if !menu.menu_contains_pos(cx, e.abs) {
828                    self.set_closed(cx);
829                    self.animator_play(cx, id!(hover.off));
830                    return;
831                }
832            }
833        }
834                
835        match event.hits_with_sweep_area(cx, self.draw_bg.area(), self.draw_bg.area()) {
836            Hit::KeyFocusLost(_) => {
837                self.animator_play(cx, id!(focus.off));
838                self.set_closed(cx);
839                self.animator_play(cx, id!(hover.off));
840                self.draw_bg.redraw(cx);
841            }
842            Hit::KeyFocus(_) => {
843                self.animator_play(cx, id!(focus.on));
844            }
845            Hit::KeyDown(ke) => match ke.key_code {
846                KeyCode::ArrowUp => {
847                    if self.selected_item > 0 {
848                        self.selected_item -= 1;
849                        cx.widget_action(uid, &scope.path, DropDownAction::Select(self.selected_item, self.values.get(self.selected_item).cloned().unwrap_or(LiveValue::None)));
850                        self.set_closed(cx);
851                        self.draw_bg.redraw(cx);
852                    }
853                }
854                KeyCode::ArrowDown => {
855                    if self.values.len() > 0 && self.selected_item < self.values.len() - 1 {
856                        self.selected_item += 1;
857                        cx.widget_action(uid, &scope.path, DropDownAction::Select(self.selected_item, self.values.get(self.selected_item).cloned().unwrap_or(LiveValue::None)));
858                        self.set_closed(cx);
859                        self.draw_bg.redraw(cx);
860                    }
861                },
862                _ => ()
863            }
864            Hit::FingerDown(fe) if fe.is_primary_hit() => {
865                if self.animator.animator_in_state(cx, id!(disabled.off)) {
866                    cx.set_key_focus(self.draw_bg.area());
867                    self.animator_play(cx, id!(hover.down));
868                    self.set_active(cx);
869                }
870                // self.animator_play(cx, id!(hover.down));
871            },
872            Hit::FingerHoverIn(_) => {
873                cx.set_cursor(MouseCursor::Hand);
874                self.animator_play(cx, id!(hover.on));
875            }
876            Hit::FingerHoverOut(_) => {
877                self.animator_play(cx, id!(hover.off));
878            }
879            Hit::FingerUp(fe) if fe.is_primary_hit() => {
880                if fe.is_over {
881                    if fe.device.has_hovers() {
882                        self.animator_play(cx, id!(hover.on));
883                    }
884                }
885                else {
886                    self.animator_play(cx, id!(hover.off));
887                }
888            }
889            _ => ()
890        };
891    }
892        
893    fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
894        self.draw_walk(cx, walk);
895        DrawStep::done()
896    }
897}
898
899impl DropDownRef {
900    
901    pub fn set_labels_with<F:FnMut(&mut String)>(&self, cx: &mut Cx, mut f:F) {
902        if let Some(mut inner) = self.borrow_mut() {
903            let mut i = 0;
904            loop {
905                if i>=inner.labels.len(){
906                    inner.labels.push(String::new());
907                }
908                let s = &mut inner.labels[i];
909                s.clear();
910                f(s);
911                if s.len()==0{
912                    break;
913                }
914                i+=1;
915            }
916            inner.labels.truncate(i);
917            inner.draw_bg.redraw(cx);
918        }
919    }
920    
921    pub fn set_labels(&self, cx: &mut Cx, labels: Vec<String>) {
922        if let Some(mut inner) = self.borrow_mut() {
923            inner.labels = labels;
924            inner.draw_bg.redraw(cx);
925        }
926    }
927    
928    //DEPRICATED
929    pub fn selected(&self, actions: &Actions) -> Option<usize> {
930        if let Some(item) = actions.find_widget_action(self.widget_uid()) {
931            if let DropDownAction::Select(id, _) = item.cast() {
932                return Some(id)
933            }
934        }
935        None
936    }
937    
938    pub fn changed(&self, actions: &Actions) -> Option<usize> {
939        if let Some(item) = actions.find_widget_action(self.widget_uid()) {
940            if let DropDownAction::Select(id, _) = item.cast() {
941                return Some(id)
942            }
943        }
944        None
945    }
946        
947    pub fn changed_label(&self, actions: &Actions) -> Option<String> {
948        if let Some(item) = actions.find_widget_action(self.widget_uid()) {
949            if let DropDownAction::Select(id, _) = item.cast() {
950                 if let Some(inner) = self.borrow() {
951                    return Some(inner.labels[id].clone())
952                }
953            }
954        }
955        None
956    }
957    
958    pub fn set_selected_item(&self, cx: &mut Cx, item: usize) {
959        if let Some(mut inner) = self.borrow_mut() {
960            let new_selected = item.min(inner.labels.len().max(1) - 1);
961            if new_selected != inner.selected_item{
962                inner.selected_item = new_selected;
963                inner.draw_bg.redraw(cx);
964            }
965        }
966    }
967    pub fn selected_item(&self) -> usize {
968        if let Some(inner) = self.borrow() {
969            return inner.selected_item
970        }
971        0
972    }
973    
974    pub fn selected_label(&self) -> String {
975        if let Some(inner) = self.borrow() {
976            return inner.labels[inner.selected_item].clone()
977        }
978        "".to_string()
979    }
980    
981    pub fn set_selected_by_label(&self, label: &str, cx: &mut Cx) {
982        if let Some(mut inner) = self.borrow_mut() {
983            if let Some(index) = inner.labels.iter().position( | v | v == label) {
984                if inner.selected_item != index{
985                    inner.selected_item = index;
986                    inner.draw_bg.redraw(cx);
987                }
988            }
989        }
990    }
991}