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 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 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 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 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 self.draw_bg.begin(cx, walk, self.layout);
707 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 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 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 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 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 }
811 PopupMenuAction::WasSelected(node_id) => {
812 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 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 },
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 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}