1use {
2 crate::{
3 makepad_draw::*,
4 },
5};
6
7live_design!{
8 link widgets;
9 use link::theme::*;
10 use makepad_draw::shader::std::*;
11
12 pub PopupMenuItemBase = {{PopupMenuItem}} {}
13 pub PopupMenuBase = {{PopupMenu}} {}
14
15 pub PopupMenuItem = <PopupMenuItemBase> {
16 width: Fill, height: Fit,
17 align: { y: 0.5 }
18 padding: <THEME_MSPACE_1> { left: 15. }
19
20 draw_text: {
21 instance active: 0.0
22 instance hover: 0.0
23 instance disabled: 0.0
24
25 uniform color: (THEME_COLOR_LABEL_INNER)
26 uniform color_hover: (THEME_COLOR_LABEL_INNER_HOVER)
27 uniform color_active: (THEME_COLOR_LABEL_INNER_ACTIVE)
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
34 fn get_color(self) -> vec4 {
35 return mix(
36 mix(
37 mix(
38 self.color,
39 self.color_active,
40 self.active
41 ),
42 self.color_hover,
43 self.hover
44 ),
45 self.color_disabled,
46 self.disabled
47 )
48 }
49 }
50
51 draw_bg: {
52 instance active: 0.0
53 instance hover: 0.0
54 instance disabled: 0.0
55
56 uniform border_size: (THEME_BEVELING)
57 uniform border_radius: (THEME_CORNER_RADIUS)
58
59 uniform color_dither: 1.0
60
61 uniform color: (THEME_COLOR_U_HIDDEN)
62 uniform color_hover: (THEME_COLOR_OUTSET_HOVER)
63 uniform color_active: (THEME_COLOR_OUTSET_ACTIVE)
64 uniform color_disabled: (THEME_COLOR_OUTSET_ACTIVE)
65
66 uniform border_color_1: (THEME_COLOR_U_HIDDEN)
67 uniform border_color_1_hover: (THEME_COLOR_U_HIDDEN)
68 uniform border_color_1_active: (THEME_COLOR_U_HIDDEN)
69 uniform border_color_1_disabled: (THEME_COLOR_U_HIDDEN)
70
71 uniform border_color_2: (THEME_COLOR_U_HIDDEN)
72 uniform border_color_2_hover: (THEME_COLOR_U_HIDDEN)
73 uniform border_color_2_active: (THEME_COLOR_U_HIDDEN)
74 uniform border_color_2_disabled: (THEME_COLOR_U_HIDDEN)
75
76 uniform mark_color: (THEME_COLOR_U_HIDDEN)
77 uniform mark_color_active: (THEME_COLOR_MARK_ACTIVE)
78 uniform mark_color_disabled: (THEME_COLOR_MARK_DISABLED)
79
80 fn pixel(self) -> vec4 {
81 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
82 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
83
84 let border_sz_uv = vec2(
85 self.border_size / self.rect_size.x,
86 self.border_size / self.rect_size.y
87 )
88
89 let gradient_border = vec2(
90 self.pos.x + dither,
91 self.pos.y + dither
92 )
93
94 let sz_inner_px = vec2(
95 self.rect_size.x - self.border_size * 2.,
96 self.rect_size.y - self.border_size * 2.
97 );
98
99 let scale_factor_fill = vec2(
100 self.rect_size.x / sz_inner_px.x,
101 self.rect_size.y / sz_inner_px.y
102 );
103
104 let gradient_fill = vec2(
105 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
106 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
107 )
108
109 sdf.box(
111 self.border_size,
112 self.border_size,
113 self.rect_size.x - self.border_size * 2.,
114 self.rect_size.y - self.border_size * 2.,
115 self.border_radius
116 );
117
118 sdf.fill_keep(
119 mix(
120 mix(
121 mix(
122 self.color,
123 self.color_active,
124 self.active
125 ),
126 self.color_hover,
127 self.hover
128 ),
129 self.color_disabled,
130 self.disabled
131 )
132 );
133
134 sdf.stroke(
135 mix(
136 mix(
137 mix(
138 mix(self.border_color_1, self.border_color_2, gradient_border.y),
139 mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
140 self.hover
141 ),
142 mix(self.border_color_1_active, self.border_color_2_active, gradient_border.y),
143 self.active
144 ),
145 mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
146 self.disabled
147 ), self.border_size
148 );
149
150 let sz = 3.;
152 let dx = 2.0;
153 let c = vec2(8.0, 0.5 * self.rect_size.y);
154 sdf.move_to(c.x - sz + dx * 0.5, c.y - sz + dx);
155 sdf.line_to(c.x, c.y + sz);
156 sdf.line_to(c.x + sz, c.y - sz);
157
158 sdf.stroke(
159 mix(
160 mix(
161 self.mark_color,
162 self.mark_color_active,
163 self.active
164 ),
165 self.mark_color_disabled,
166 self.disabled
167 ), 1.
168 );
169
170 return sdf.result;
171 }
172 }
173
174 animator: {
175 disabled = {
176 default: off,
177 off = {
178 from: {all: Forward {duration: 0.}}
179 apply: {
180 draw_bg: {disabled: 0.0}
181 draw_text: {disabled: 0.0}
182 }
183 }
184 on = {
185 from: {all: Forward {duration: 0.2}}
186 apply: {
187 draw_bg: {disabled: 1.0}
188 draw_text: {disabled: 1.0}
189 }
190 }
191 }
192 hover = {
193 default: off
194 off = {
195 from: {all: Snap}
196 apply: {
197 draw_bg: {hover: 0.0}
198 draw_text: {hover: 0.0}
199 }
200 }
201 on = {
202 cursor: Hand
203 from: {all: Snap}
204 apply: {
205 draw_bg: {hover: 1.0}
206 draw_text: {hover: 1.0}
207 }
208 }
209 }
210
211 active = {
212 default: off
213 off = {
214 from: {all: Snap}
215 apply: {
216 draw_bg: {active: 0.0,}
217 draw_text: {active: 0.0,}
218 }
219 }
220 on = {
221 from: {all: Snap}
222 apply: {
223 draw_bg: {active: 1.0,}
224 draw_text: {active: 1.0,}
225 }
226 }
227 }
228 }
229 indent_width: 10.0
230 }
231
232 PopupMenuItemGradientX = <PopupMenuItem> {
233 draw_bg: {
234 uniform color_1: (THEME_COLOR_U_HIDDEN)
235 uniform color_1_hover: (THEME_COLOR_OUTSET_1_HOVER)
236 uniform color_1_active: (THEME_COLOR_OUTSET_1_ACTIVE)
237 uniform color_1_disabled: (THEME_COLOR_OUTSET_1_DISABLED)
238
239 uniform color_2: (THEME_COLOR_U_HIDDEN)
240 uniform color_2_hover: (THEME_COLOR_OUTSET_2_HOVER)
241 uniform color_2_active: (THEME_COLOR_OUTSET_2_ACTIVE)
242 uniform color_2_disabled: (THEME_COLOR_OUTSET_2_DISABLED)
243
244 uniform border_color_1: (THEME_COLOR_U_HIDDEN)
245 uniform border_color_1_hover: (THEME_COLOR_U_HIDDEN)
246 uniform border_color_1_active: (THEME_COLOR_U_HIDDEN)
247 uniform border_color_1_disabled: (THEME_COLOR_U_HIDDEN)
248
249 uniform border_color_2: (THEME_COLOR_U_HIDDEN)
250 uniform border_color_2_hover: (THEME_COLOR_U_HIDDEN)
251 uniform border_color_2_active: (THEME_COLOR_U_HIDDEN)
252 uniform border_color_2_disabled: (THEME_COLOR_U_HIDDEN)
253
254 uniform mark_color: (THEME_COLOR_U_HIDDEN)
255 uniform mark_color_active: (THEME_COLOR_MARK_ACTIVE)
256 uniform mark_color_disabled: (THEME_COLOR_MARK_DISABLED)
257
258 fn pixel(self) -> vec4 {
259 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
260 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
261
262 let border_sz_uv = vec2(
263 self.border_size / self.rect_size.x,
264 self.border_size / self.rect_size.y
265 )
266
267 let gradient_border = vec2(
268 self.pos.x + dither,
269 self.pos.y + dither
270 )
271
272 let sz_inner_px = vec2(
273 self.rect_size.x - self.border_size * 2.,
274 self.rect_size.y - self.border_size * 2.
275 );
276
277 let scale_factor_fill = vec2(
278 self.rect_size.x / sz_inner_px.x,
279 self.rect_size.y / sz_inner_px.y
280 );
281
282 let gradient_fill = vec2(
283 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
284 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
285 )
286
287 sdf.box(
289 self.border_size,
290 self.border_size,
291 self.rect_size.x - self.border_size * 2.,
292 self.rect_size.y - self.border_size * 2.,
293 self.border_radius
294 );
295
296 sdf.fill_keep(
297 mix(
298 mix(
299 mix(
300 mix(self.color_1, self.color_2, self.pos.x),
301 mix(self.color_1_active, self.color_2_active, self.pos.x),
302 self.active
303 ),
304 mix(self.color_1_hover, self.color_2_hover, self.pos.x),
305 self.hover
306 ),
307 mix(self.color_1_disabled, self.color_2_disabled, self.pos.x),
308 self.disabled
309 )
310 );
311
312 sdf.stroke(
313 mix(
314 mix(
315 mix(
316 mix(self.border_color_1, self.border_color_2, self.pos.y + dither),
317 mix(self.border_color_1_hover, self.border_color_2_hover, self.pos.y + dither),
318 self.hover
319 ),
320 mix(self.border_color_1_active, self.border_color_2_active, self.pos.y + dither),
321 self.active
322 ),
323 mix(self.border_color_1_disabled, self.border_color_2_disabled, self.pos.y + dither),
324 self.disabled
325 ), self.border_size
326 );
327
328 let sz = 3.;
330 let dx = 2.0;
331 let c = vec2(8.0, 0.5 * self.rect_size.y);
332 sdf.move_to(c.x - sz + dx * 0.5, c.y - sz + dx);
333 sdf.line_to(c.x, c.y + sz);
334 sdf.line_to(c.x + sz, c.y - sz);
335
336 sdf.stroke(
337 mix(
338 mix(
339 self.mark_color,
340 self.mark_color_active,
341 self.active
342 ),
343 self.mark_color_disabled,
344 self.disabled
345 ), 1.);
346
347 return sdf.result;
348 }
349 }
350 }
351
352 PopupMenuItemGradientY = <PopupMenuItemGradientX> {
353 draw_bg: {
354 fn pixel(self) -> vec4 {
355 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
356 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
357
358 let border_sz_uv = vec2(
359 self.border_size / self.rect_size.x,
360 self.border_size / self.rect_size.y
361 )
362
363 let gradient_border = vec2(
364 self.pos.x + dither,
365 self.pos.y + dither
366 )
367
368 let sz_inner_px = vec2(
369 self.rect_size.x - self.border_size * 2.,
370 self.rect_size.y - self.border_size * 2.
371 );
372
373 let scale_factor_fill = vec2(
374 self.rect_size.x / sz_inner_px.x,
375 self.rect_size.y / sz_inner_px.y
376 );
377
378 let gradient_fill = vec2(
379 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
380 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
381 )
382
383 sdf.box(
385 self.border_size,
386 self.border_size,
387 self.rect_size.x - self.border_size * 2.,
388 self.rect_size.y - self.border_size * 2.,
389 self.border_radius
390 );
391
392 sdf.fill_keep(
393 mix(
394 mix(
395 mix(
396 mix(self.color_1, self.color_2, gradient_fill.y),
397 mix(self.color_1_active, self.color_2_active, gradient_fill.y),
398 self.active
399 ),
400 mix(self.color_1_hover, self.color_2_hover, gradient_fill.y),
401 self.hover
402 ),
403 mix(self.color_1_disabled, self.color_2_disabled, gradient_fill.y),
404 self.disabled
405 )
406 );
407
408 sdf.stroke(
409 mix(
410 mix(
411 mix(
412 mix(self.border_color_1, self.border_color_2, gradient_border.y),
413 mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
414 self.hover
415 ),
416 mix(self.border_color_1_active, self.border_color_2_active, gradient_border.y),
417 self.active
418 ),
419 mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
420 self.disabled
421 ), self.border_size
422 );
423
424 let sz = 3.;
426 let dx = 2.0;
427 let c = vec2(8.0, 0.5 * self.rect_size.y);
428 sdf.move_to(c.x - sz + dx * 0.5, c.y - sz + dx);
429 sdf.line_to(c.x, c.y + sz);
430 sdf.line_to(c.x + sz, c.y - sz);
431
432 sdf.stroke(
433 mix(
434 mix( self.mark_color, self.mark_color_active, self.active),
435 self.mark_color_disabled,
436 self.disabled
437 ), 1.
438 );
439
440 return sdf.result;
441 }
442 }
443 }
444
445 pub PopupMenu = <PopupMenuBase> {
446 width: 150., height: Fit,
447 flow: Down,
448 padding: <THEME_MSPACE_1> {}
449
450 menu_item: <PopupMenuItem> {}
451
452 draw_bg: {
453 uniform color_dither: 1.0
454 uniform color: (THEME_COLOR_FG_APP)
455
456 uniform border_radius: (THEME_CORNER_RADIUS)
457 uniform border_size: (THEME_BEVELING)
458 uniform border_color_1: (THEME_COLOR_BEVEL_OUTSET_1)
459 uniform border_color_2: (THEME_COLOR_BEVEL_OUTSET_2)
460
461 fn pixel(self) -> vec4 {
462 let sdf = Sdf2d::viewport(self.pos * self.rect_size)
463 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
464
465 let border_sz_uv = vec2(
466 self.border_size / self.rect_size.x,
467 self.border_size / self.rect_size.y
468 )
469
470 let gradient_border = vec2(
471 self.pos.x + dither,
472 self.pos.y + dither
473 )
474
475 let sz_inner_px = vec2(
476 self.rect_size.x - self.border_size * 2.,
477 self.rect_size.y - self.border_size * 2.
478 );
479
480 let scale_factor_fill = vec2(
481 self.rect_size.x / sz_inner_px.x,
482 self.rect_size.y / sz_inner_px.y
483 );
484
485 let gradient_fill = vec2(
486 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
487 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
488 )
489
490 sdf.box(
491 self.border_size,
492 self.border_size,
493 self.rect_size.x - self.border_size * 2.,
494 self.rect_size.y - self.border_size * 2.,
495 self.border_radius
496 )
497
498 sdf.fill_keep(self.color);
499
500 if self.border_size > 0.0 {
501 sdf.stroke(
502 mix(
503 self.border_color_1,
504 self.border_color_2,
505 gradient_border.y
506 ), self.border_size
507 );
508 }
509
510 return sdf.result;
511 }
512 }
513 }
514 pub PopupMenuFlat = <PopupMenu> {
515 menu_item: <PopupMenuItem> {}
516
517 draw_bg: {
518 uniform border_color_1: (THEME_COLOR_BEVEL)
519 uniform border_color_2: (THEME_COLOR_BEVEL)
520 }
521 }
522
523 pub PopupMenuFlatter = <PopupMenuFlat> {
524 draw_bg: { border_size: 0. }
525 }
526
527 pub PopupMenuGradientY = <PopupMenu> {
528 menu_item: <PopupMenuItemGradientY> {}
529
530 draw_bg: {
531 uniform color_1: (THEME_COLOR_FG_APP)
532 uniform color_2: (THEME_COLOR_FG_APP * 1.2)
533
534 fn pixel(self) -> vec4 {
535 let sdf = Sdf2d::viewport(self.pos * self.rect_size)
536 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
537
538 let border_sz_uv = vec2(
539 self.border_size / self.rect_size.x,
540 self.border_size / self.rect_size.y
541 )
542
543 let gradient_border = vec2(
544 self.pos.x + dither,
545 self.pos.y + dither
546 )
547
548 let sz_inner_px = vec2(
549 self.rect_size.x - self.border_size * 2.,
550 self.rect_size.y - self.border_size * 2.
551 );
552
553 let scale_factor_fill = vec2(
554 self.rect_size.x / sz_inner_px.x,
555 self.rect_size.y / sz_inner_px.y
556 );
557
558 let gradient_fill = vec2(
559 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
560 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
561 )
562
563 sdf.box(
564 self.border_size,
565 self.border_size,
566 self.rect_size.x - self.border_size * 2.,
567 self.rect_size.y - self.border_size * 2.,
568 self.border_radius
569 )
570
571 sdf.fill_keep(mix(self.color_1, self.color_2, gradient_fill.y));
572
573 if self.border_size > 0.0 {
574 sdf.stroke(
575 mix(
576 self.border_color_1,
577 self.border_color_2,
578 gradient_border.y
579 ), self.border_size
580 );
581 }
582
583 return sdf.result;
584 }
585 }
586 }
587
588 pub PopupMenuGradientX = <PopupMenuGradientY> {
589 menu_item: <PopupMenuItemGradientX> {}
590
591 draw_bg: {
592 fn pixel(self) -> vec4 {
593 let sdf = Sdf2d::viewport(self.pos * self.rect_size)
594 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
595
596 let border_sz_uv = vec2(
597 self.border_size / self.rect_size.x,
598 self.border_size / self.rect_size.y
599 )
600
601 let gradient_border = vec2(
602 self.pos.x + dither,
603 self.pos.y + dither
604 )
605
606 let sz_inner_px = vec2(
607 self.rect_size.x - self.border_size * 2.,
608 self.rect_size.y - self.border_size * 2.
609 );
610
611 let scale_factor_fill = vec2(
612 self.rect_size.x / sz_inner_px.x,
613 self.rect_size.y / sz_inner_px.y
614 );
615
616 let gradient_fill = vec2(
617 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
618 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
619 )
620
621 sdf.box(
622 self.border_size,
623 self.border_size,
624 self.rect_size.x - self.border_size * 2.,
625 self.rect_size.y - self.border_size * 2.,
626 self.border_radius
627 )
628
629 sdf.fill_keep(mix(self.color_1, self.color_2, gradient_fill.x));
630
631 if self.border_size > 0.0 {
632 sdf.stroke(
633 mix(
634 self.border_color_1,
635 self.border_color_2,
636 gradient_border.y
637 ), self.border_size
638 );
639 }
640
641 return sdf.result;
642 }
643 }
644 }
645
646
647}
648
649
650#[derive(Live, LiveHook, LiveRegister)]
651pub struct PopupMenuItem {
652
653 #[live] draw_bg: DrawQuad,
654 #[live] draw_text: DrawText,
655
656 #[layout] layout: Layout,
657 #[animator] animator: Animator,
658 #[walk] walk: Walk,
659
660 #[live] indent_width: f32,
661 #[live] icon_walk: Walk,
662
663 #[live] opened: f32,
664 #[live] hover: f32,
665 #[live] active: f32,
666}
667
668#[derive(Live, LiveRegister)]
669pub struct PopupMenu {
670 #[live] draw_list: DrawList2d,
671 #[live] menu_item: Option<LivePtr>,
672
673 #[live] draw_bg: DrawQuad,
674 #[layout] layout: Layout,
675 #[walk] walk: Walk,
676 #[live] items: Vec<String>,
677 #[rust] first_tap: bool,
678 #[rust] menu_items: ComponentMap<PopupMenuItemId, PopupMenuItem>,
679 #[rust] init_select_item: Option<PopupMenuItemId>,
680
681 #[rust] count: usize,
682}
683
684impl LiveHook for PopupMenu {
685 fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) {
686 if let Some(index) = nodes.child_by_name(index, live_id!(list_node).as_field()) {
687 for (_, node) in self.menu_items.iter_mut() {
688 node.apply(cx, apply, index, nodes);
689 }
690 }
691 self.draw_list.redraw(cx);
692 }
693}
694
695pub enum PopupMenuItemAction {
696 WasSweeped,
697 WasSelected,
698 MightBeSelected,
699 None
700}
701
702#[derive(Clone, DefaultNone)]
703pub enum PopupMenuAction {
704 WasSweeped(PopupMenuItemId),
705 WasSelected(PopupMenuItemId),
706 None,
707}
708
709#[derive(Clone, Debug, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
710pub struct PopupMenuItemId(pub LiveId);
711
712impl PopupMenuItem {
713
714 pub fn draw_item(
715 &mut self,
716 cx: &mut Cx2d,
717 label: &str,
718 ) {
719 self.draw_bg.begin(cx, self.walk, self.layout);
720 self.draw_text.draw_walk(cx, Walk::fit(), Align::default(), label);
721 self.draw_bg.end(cx);
722 }
723
724 pub fn handle_event_with(
725 &mut self,
726 cx: &mut Cx,
727 event: &Event,
728 sweep_area: Area,
729 dispatch_action: &mut dyn FnMut(&mut Cx, PopupMenuItemAction),
730 ) {
731 if self.animator_handle_event(cx, event).must_redraw() {
732 self.draw_bg.area().redraw(cx);
733 }
734
735 match event.hits_with_options(
736 cx,
737 self.draw_bg.area(),
738 HitOptions::new().with_sweep_area(sweep_area)
739 ) {
740 Hit::FingerHoverIn(_) => {
741 self.animator_play(cx, id!(hover.on));
742 }
743 Hit::FingerHoverOut(_) => {
744 self.animator_play(cx, id!(hover.off));
745 }
746 Hit::FingerDown(fe) if fe.is_primary_hit() => {
747 dispatch_action(cx, PopupMenuItemAction::WasSweeped);
748 self.animator_play(cx, id!(hover.on));
749 self.animator_play(cx, id!(active.on));
750 }
751 Hit::FingerUp(se) if se.is_primary_hit() => {
752 if !se.is_sweep {
753 dispatch_action(cx, PopupMenuItemAction::WasSelected);
759 }
761 else {
762 self.animator_play(cx, id!(hover.off));
763 self.animator_play(cx, id!(active.off));
764 }
765 }
766 _ => {}
767 }
768 }
769}
770
771impl PopupMenu {
772
773 pub fn menu_contains_pos(&self, cx: &mut Cx, pos: DVec2) -> bool {
774 self.draw_bg.area().clipped_rect(cx).contains(pos)
775 }
776
777 pub fn begin(&mut self, cx: &mut Cx2d) {
778 self.draw_list.begin_overlay_reuse(cx);
779
780 let size = cx.current_pass_size();
781 cx.begin_sized_turtle(size, Layout::flow_down());
782
783 self.draw_bg.begin(cx, self.walk, self.layout);
785 self.count = 0;
786 }
787
788 pub fn end(&mut self, cx: &mut Cx2d, shift_area: Area, shift: DVec2) {
789 self.draw_bg.end(cx);
798
799 cx.end_pass_sized_turtle_with_shift(shift_area, shift);
800 self.draw_list.end(cx);
802 self.menu_items.retain_visible();
803 if let Some(init_select_item) = self.init_select_item.take() {
804 self.select_item_state(cx, init_select_item);
805 }
806 }
807
808 pub fn redraw(&mut self, cx: &mut Cx) {
809 self.draw_list.redraw(cx);
810 }
811
812 pub fn draw_item(
813 &mut self,
814 cx: &mut Cx2d,
815 item_id: PopupMenuItemId,
816 label: &str,
817 ) {
818 self.count += 1;
819
820 let menu_item = self.menu_item;
821 let menu_item = self.menu_items.get_or_insert(cx, item_id, | cx | {
822 PopupMenuItem::new_from_ptr(cx, menu_item)
823 });
824 menu_item.draw_item(cx, label);
825 }
826
827 pub fn init_select_item(&mut self, which_id: PopupMenuItemId) {
828 self.init_select_item = Some(which_id);
829 self.first_tap = true;
830 }
831
832 fn select_item_state(&mut self, cx: &mut Cx, which_id: PopupMenuItemId) {
833 for (id, item) in &mut *self.menu_items {
834 if *id == which_id {
835 item.animator_cut(cx, id!(active.on));
836 item.animator_cut(cx, id!(hover.on));
837 }
838 else {
839 item.animator_cut(cx, id!(active.off));
840 item.animator_cut(cx, id!(hover.off));
841 }
842 }
843 }
844
845 pub fn handle_event_with(
846 &mut self,
847 cx: &mut Cx,
848 event: &Event,
849 sweep_area: Area,
850 dispatch_action: &mut dyn FnMut(&mut Cx, PopupMenuAction),
851 ) {
852 let mut actions = Vec::new();
853 for (item_id, node) in self.menu_items.iter_mut() {
854 node.handle_event_with(cx, event, sweep_area, &mut | _, e | actions.push((*item_id, e)));
855 }
856
857 for (node_id, action) in actions {
858 match action {
859 PopupMenuItemAction::MightBeSelected => {
860 if self.first_tap {
861 self.first_tap = false;
862 }
863 else {
864 self.select_item_state(cx, node_id);
865 dispatch_action(cx, PopupMenuAction::WasSelected(node_id));
866 }
867 }
868 PopupMenuItemAction::WasSweeped => {
869 self.select_item_state(cx, node_id);
870 dispatch_action(cx, PopupMenuAction::WasSweeped(node_id));
871 }
872 PopupMenuItemAction::WasSelected => {
873 self.select_item_state(cx, node_id);
874 dispatch_action(cx, PopupMenuAction::WasSelected(node_id));
875 }
876 _ => ()
877 }
878 }
879 }
880}
881