1use crate::prelude::*;
2use std::any::TypeId;
3
4#[derive(Lens)]
5pub(crate) struct ModalModel {
6 pub tooltip_visible: (bool, bool),
7 pub menu_visible: bool,
8}
9
10pub enum ModalEvent {
12 ShowTooltip,
14 HideTooltip,
16 ShowMenu,
18 HideMenu,
20}
21
22impl Model for ModalModel {
23 fn event(&mut self, _cx: &mut EventContext, event: &mut Event) {
24 event.map(|modal_event, _| match modal_event {
25 ModalEvent::ShowTooltip => {
26 self.tooltip_visible = (true, true);
27 }
28
29 ModalEvent::HideTooltip => {
30 self.tooltip_visible = (false, true);
31 }
32
33 ModalEvent::ShowMenu => {
34 self.menu_visible = true;
35 }
36
37 ModalEvent::HideMenu => {
38 self.menu_visible = false;
39 }
40 });
41
42 event.map(|window_event, _| match window_event {
43 WindowEvent::MouseOver => {
44 if !self.tooltip_visible.0 {
45 self.tooltip_visible = (true, true);
46 }
47 }
48 WindowEvent::MouseOut => self.tooltip_visible = (false, true),
49 WindowEvent::FocusIn => {
50 if !self.tooltip_visible.0 {
51 self.tooltip_visible = (true, false);
52 }
53 }
54 WindowEvent::FocusOut => self.tooltip_visible = (false, false),
55 WindowEvent::FocusVisibility(vis) if !(*vis) => self.tooltip_visible = (false, false),
56 WindowEvent::KeyDown(code, _) if *code == Code::Escape => {
57 self.tooltip_visible = (false, false)
58 }
59 WindowEvent::PressDown { mouse: _ } => self.tooltip_visible = (false, true),
60 _ => {}
61 });
62 }
63}
64
65pub(crate) struct ActionsModel {
66 pub(crate) on_press: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
67 pub(crate) on_press_down: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
68 pub(crate) on_double_click: Option<Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>>,
69 pub(crate) on_hover: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
70 pub(crate) on_hover_out: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
71 pub(crate) on_over: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
72 pub(crate) on_over_out: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
73 pub(crate) on_mouse_move: Option<Box<dyn Fn(&mut EventContext, f32, f32) + Send + Sync>>,
74 pub(crate) on_mouse_down: Option<Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>>,
75 pub(crate) on_mouse_up: Option<Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>>,
76 pub(crate) on_focus_in: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
77 pub(crate) on_focus_out: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
78 pub(crate) on_geo_changed: Option<Box<dyn Fn(&mut EventContext, GeoChanged) + Send + Sync>>,
79 pub(crate) on_drag_start: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
80 pub(crate) on_drop: Option<Box<dyn Fn(&mut EventContext, DropData) + Send + Sync>>,
81}
82
83impl ActionsModel {
84 pub(crate) fn new() -> Self {
85 Self {
86 on_press: None,
87 on_press_down: None,
88 on_double_click: None,
89 on_hover: None,
90 on_hover_out: None,
91 on_over: None,
92 on_over_out: None,
93 on_mouse_move: None,
94 on_mouse_down: None,
95 on_mouse_up: None,
96 on_focus_in: None,
97 on_focus_out: None,
98 on_geo_changed: None,
99 on_drag_start: None,
100 on_drop: None,
101 }
102 }
103}
104
105impl Model for ActionsModel {
106 fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
107 event.take(|actions_event, _| match actions_event {
108 ActionsEvent::OnPress(on_press) => {
109 self.on_press = Some(on_press);
110 }
111
112 ActionsEvent::OnPressDown(on_press_down) => {
113 self.on_press_down = Some(on_press_down);
114 }
115
116 ActionsEvent::OnDoubleClick(on_double_click) => {
117 self.on_double_click = Some(on_double_click);
118 }
119
120 ActionsEvent::OnHover(on_hover) => {
121 self.on_hover = Some(on_hover);
122 }
123
124 ActionsEvent::OnHoverOut(on_hover_out) => {
125 self.on_hover_out = Some(on_hover_out);
126 }
127
128 ActionsEvent::OnOver(on_over) => {
129 self.on_over = Some(on_over);
130 }
131
132 ActionsEvent::OnOverOut(on_over_out) => {
133 self.on_over_out = Some(on_over_out);
134 }
135
136 ActionsEvent::OnMouseMove(on_move) => {
137 self.on_mouse_move = Some(on_move);
138 }
139
140 ActionsEvent::OnMouseDown(on_mouse_down) => {
141 self.on_mouse_down = Some(on_mouse_down);
142 }
143
144 ActionsEvent::OnMouseUp(on_mouse_up) => {
145 self.on_mouse_up = Some(on_mouse_up);
146 }
147
148 ActionsEvent::OnFocusIn(on_focus_in) => {
149 self.on_focus_in = Some(on_focus_in);
150 }
151
152 ActionsEvent::OnFocusOut(on_focus_out) => {
153 self.on_focus_out = Some(on_focus_out);
154 }
155
156 ActionsEvent::OnGeoChanged(on_geo_changed) => {
157 self.on_geo_changed = Some(on_geo_changed);
158 cx.cache.set_bounds(cx.current, BoundingBox::default());
159 cx.needs_relayout();
160 }
161
162 ActionsEvent::OnDragStart(on_drag_start) => {
163 self.on_drag_start = Some(on_drag_start);
164 }
165
166 ActionsEvent::OnDrop(on_drop) => {
167 self.on_drop = Some(on_drop);
168 }
169 });
170
171 event.map(|window_event, meta| match window_event {
172 WindowEvent::Press { mouse } => {
173 let over = if *mouse { cx.hovered() } else { cx.focused() };
174 if cx.current() != over && !over.is_descendant_of(cx.tree, cx.current()) {
175 return;
176 }
177
178 if !cx.is_disabled() && cx.current == meta.target {
179 if let Some(action) = &self.on_press {
180 (action)(cx);
181 }
182 }
183 }
184
185 WindowEvent::PressDown { mouse } => {
186 let over = if *mouse { cx.hovered() } else { cx.focused() };
187 if cx.current() != over && !over.is_descendant_of(cx.tree, cx.current()) {
188 return;
189 }
190 if !cx.is_disabled() && cx.current == meta.target {
191 if let Some(action) = &self.on_press_down {
192 (action)(cx);
193 }
194 }
195 }
196
197 WindowEvent::MouseDoubleClick(button) => {
198 if meta.target == cx.current && !cx.is_disabled() {
199 if let Some(action) = &self.on_double_click {
200 (action)(cx, *button);
201 }
202 }
203 }
204
205 WindowEvent::MouseEnter => {
206 if meta.target == cx.current() {
207 if let Some(action) = &self.on_hover {
208 (action)(cx);
209 }
210 }
211 }
212
213 WindowEvent::MouseLeave => {
214 if meta.target == cx.current() {
215 if let Some(action) = &self.on_hover_out {
216 (action)(cx);
217 }
218 }
219 }
220
221 WindowEvent::MouseOver => {
222 if let Some(action) = &self.on_over {
223 (action)(cx);
224 }
225 }
226
227 WindowEvent::MouseOut => {
228 if let Some(action) = &self.on_over_out {
230 (action)(cx);
231 }
232
233 if cx.mouse.left.state == MouseButtonState::Pressed
234 && cx.mouse.left.pressed == cx.current()
235 && cx.is_draggable()
236 {
237 if let Some(action) = &self.on_drag_start {
238 (action)(cx);
239 }
240 }
241 }
243
244 WindowEvent::MouseMove(x, y) => {
245 if let Some(action) = &self.on_mouse_move {
246 (action)(cx, *x, *y);
247 }
248 if cx.mouse.left.state == MouseButtonState::Released {
249 if let Some(drop_data) = cx.drop_data.take() {
250 if let Some(action) = &self.on_drop {
251 (action)(cx, drop_data);
252 }
253 }
254 }
255 }
256
257 WindowEvent::MouseDown(mouse_button) => {
258 if let Some(action) = &self.on_mouse_down {
259 (action)(cx, *mouse_button);
260 }
261 }
262
263 WindowEvent::MouseUp(mouse_button) => {
264 if let Some(action) = &self.on_mouse_up {
265 (action)(cx, *mouse_button);
266 }
267 if let Some(drop_data) = cx.drop_data.take() {
268 if let Some(action) = &self.on_drop {
269 (action)(cx, drop_data);
270 }
271 }
272 }
273
274 WindowEvent::FocusIn => {
275 if let Some(action) = &self.on_focus_in {
276 (action)(cx);
277 }
278 }
279
280 WindowEvent::FocusOut => {
281 if let Some(action) = &self.on_focus_out {
282 (action)(cx);
283 }
284 }
285
286 WindowEvent::GeometryChanged(geo) => {
287 if meta.target == cx.current() {
288 if let Some(action) = &self.on_geo_changed {
289 (action)(cx, *geo);
290 }
291 }
292 }
293
294 _ => {}
295 });
296 }
297}
298
299pub(crate) enum ActionsEvent {
300 OnPress(Box<dyn Fn(&mut EventContext) + Send + Sync>),
301 OnPressDown(Box<dyn Fn(&mut EventContext) + Send + Sync>),
302 OnDoubleClick(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
303 OnHover(Box<dyn Fn(&mut EventContext) + Send + Sync>),
304 OnHoverOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
305 OnOver(Box<dyn Fn(&mut EventContext) + Send + Sync>),
306 OnOverOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
307 OnMouseMove(Box<dyn Fn(&mut EventContext, f32, f32) + Send + Sync>),
308 OnMouseDown(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
309 OnMouseUp(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
310 OnFocusIn(Box<dyn Fn(&mut EventContext) + Send + Sync>),
311 OnFocusOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
312 OnGeoChanged(Box<dyn Fn(&mut EventContext, GeoChanged) + Send + Sync>),
313 OnDragStart(Box<dyn Fn(&mut EventContext) + Send + Sync>),
314 OnDrop(Box<dyn Fn(&mut EventContext, DropData) + Send + Sync>),
315}
316
317pub trait ActionModifiers<V> {
319 fn on_press<F>(self, action: F) -> Self
330 where
331 F: 'static + Fn(&mut EventContext) + Send + Sync;
332
333 fn on_press_down<F>(self, action: F) -> Self
344 where
345 F: 'static + Fn(&mut EventContext) + Send + Sync;
346
347 fn on_double_click<F>(self, action: F) -> Self
356 where
357 F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
358
359 fn on_hover<F>(self, action: F) -> Self
369 where
370 F: 'static + Fn(&mut EventContext) + Send + Sync;
371
372 fn on_hover_out<F>(self, action: F) -> Self
382 where
383 F: 'static + Fn(&mut EventContext) + Send + Sync;
384
385 fn on_over<F>(self, action: F) -> Self
395 where
396 F: 'static + Fn(&mut EventContext) + Send + Sync;
397
398 fn on_over_out<F>(self, action: F) -> Self
408 where
409 F: 'static + Fn(&mut EventContext) + Send + Sync;
410
411 fn on_mouse_move<F>(self, action: F) -> Self
420 where
421 F: 'static + Fn(&mut EventContext, f32, f32) + Send + Sync;
422
423 fn on_mouse_down<F>(self, action: F) -> Self
433 where
434 F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
435
436 fn on_mouse_up<F>(self, action: F) -> Self
446 where
447 F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
448
449 fn on_focus_in<F>(self, action: F) -> Self
458 where
459 F: 'static + Fn(&mut EventContext) + Send + Sync;
460
461 fn on_focus_out<F>(self, action: F) -> Self
470 where
471 F: 'static + Fn(&mut EventContext) + Send + Sync;
472
473 fn on_geo_changed<F>(self, action: F) -> Self
482 where
483 F: 'static + Fn(&mut EventContext, GeoChanged) + Send + Sync;
484
485 fn tooltip<C: Fn(&mut Context) -> Handle<'_, Tooltip> + 'static>(self, content: C) -> Self;
487
488 fn menu<C: FnOnce(&mut Context) -> Handle<'_, T>, T: View>(self, content: C) -> Self;
490
491 fn on_drag<F>(self, action: F) -> Self
493 where
494 F: 'static + Fn(&mut EventContext) + Send + Sync;
495
496 fn on_drop<F>(self, action: F) -> Self
498 where
499 F: 'static + Fn(&mut EventContext, DropData) + Send + Sync;
500}
501
502fn build_action_model(cx: &mut Context, entity: Entity) {
504 if cx.models.get(&entity).and_then(|models| models.get(&TypeId::of::<ActionsModel>())).is_none()
505 {
506 cx.with_current(entity, |cx| {
507 ActionsModel::new().build(cx);
508 });
509 }
510}
511
512fn build_modal_model(cx: &mut Context, entity: Entity) {
513 if cx.models.get(&entity).and_then(|models| models.get(&TypeId::of::<ModalModel>())).is_none() {
514 cx.with_current(entity, |cx| {
515 ModalModel { tooltip_visible: (false, true), menu_visible: false }.build(cx);
516 });
517 }
518}
519
520impl<V: View> ActionModifiers<V> for Handle<'_, V> {
521 fn tooltip<C: Fn(&mut Context) -> Handle<'_, Tooltip> + 'static>(self, content: C) -> Self {
522 let entity = self.entity();
523
524 build_modal_model(self.cx, entity);
525
526 self.cx.with_current(entity, move |cx| {
527 Binding::new(cx, ModalModel::tooltip_visible, move |cx, tooltip_visible| {
528 let tooltip_visible = tooltip_visible.get(cx);
529 if tooltip_visible.0 {
530 (content)(cx).on_build(|cx| {
531 if tooltip_visible.1 {
532 cx.play_animation(
533 "tooltip_fade",
534 Duration::from_millis(100),
535 Duration::from_millis(500),
536 )
537 }
538 });
539 }
540 });
541 });
542
543 self
544 }
545
546 fn menu<C: FnOnce(&mut Context) -> Handle<'_, T>, T: View>(self, content: C) -> Self {
547 let entity = self.entity();
548
549 build_modal_model(self.cx, entity);
550
551 self.cx.with_current(entity, |cx| {
552 (content)(cx).bind(ModalModel::menu_visible, |mut handle, vis| {
553 let is_visible = vis.get(&handle);
554 handle = handle.toggle_class("vis", is_visible);
555
556 if is_visible {
557 handle.context().emit(WindowEvent::GeometryChanged(GeoChanged::empty()));
558 }
559 });
560 });
561
562 self
563 }
564
565 fn on_press<F>(mut self, action: F) -> Self
566 where
567 F: 'static + Fn(&mut EventContext) + Send + Sync,
568 {
569 self = self.hoverable(true);
570
571 if let Some(view) = self
572 .cx
573 .views
574 .get_mut(&self.entity)
575 .and_then(|view_handler| view_handler.downcast_mut::<Button>())
576 {
577 view.action = Some(Box::new(action));
578 return self;
579 }
580
581 build_action_model(self.cx, self.entity);
582
583 self.cx.emit_custom(
584 Event::new(ActionsEvent::OnPress(Box::new(action)))
585 .target(self.entity)
586 .origin(self.entity),
587 );
588
589 self
590 }
591
592 fn on_press_down<F>(self, action: F) -> Self
593 where
594 F: 'static + Fn(&mut EventContext) + Send + Sync,
595 {
596 build_action_model(self.cx, self.entity);
597
598 self.cx.emit_custom(
599 Event::new(ActionsEvent::OnPressDown(Box::new(action)))
600 .target(self.entity)
601 .origin(self.entity),
602 );
603
604 self
605 }
606
607 fn on_double_click<F>(self, action: F) -> Self
608 where
609 F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync,
610 {
611 build_action_model(self.cx, self.entity);
612
613 self.cx.emit_custom(
614 Event::new(ActionsEvent::OnDoubleClick(Box::new(action)))
615 .target(self.entity)
616 .origin(self.entity),
617 );
618
619 self
620 }
621
622 fn on_hover<F>(self, action: F) -> Self
623 where
624 F: 'static + Fn(&mut EventContext) + Send + Sync,
625 {
626 build_action_model(self.cx, self.entity);
627
628 self.cx.emit_custom(
629 Event::new(ActionsEvent::OnHover(Box::new(action)))
630 .target(self.entity)
631 .origin(self.entity),
632 );
633
634 self
635 }
636
637 fn on_hover_out<F>(self, action: F) -> Self
638 where
639 F: 'static + Fn(&mut EventContext) + Send + Sync,
640 {
641 build_action_model(self.cx, self.entity);
642
643 self.cx.emit_custom(
644 Event::new(ActionsEvent::OnHoverOut(Box::new(action)))
645 .target(self.entity)
646 .origin(self.entity),
647 );
648
649 self
650 }
651
652 fn on_over<F>(self, action: F) -> Self
653 where
654 F: 'static + Fn(&mut EventContext) + Send + Sync,
655 {
656 build_action_model(self.cx, self.entity);
657
658 self.cx.emit_custom(
659 Event::new(ActionsEvent::OnOver(Box::new(action)))
660 .target(self.entity)
661 .origin(self.entity),
662 );
663
664 self
665 }
666
667 fn on_over_out<F>(self, action: F) -> Self
668 where
669 F: 'static + Fn(&mut EventContext) + Send + Sync,
670 {
671 build_action_model(self.cx, self.entity);
672
673 self.cx.emit_custom(
674 Event::new(ActionsEvent::OnOverOut(Box::new(action)))
675 .target(self.entity)
676 .origin(self.entity),
677 );
678
679 self
680 }
681
682 fn on_mouse_move<F>(self, action: F) -> Self
683 where
684 F: 'static + Fn(&mut EventContext, f32, f32) + Send + Sync,
685 {
686 build_action_model(self.cx, self.entity);
687
688 self.cx.emit_custom(
689 Event::new(ActionsEvent::OnMouseMove(Box::new(action)))
690 .target(self.entity)
691 .origin(self.entity),
692 );
693
694 self
695 }
696
697 fn on_mouse_down<F>(self, action: F) -> Self
698 where
699 F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync,
700 {
701 build_action_model(self.cx, self.entity);
702
703 self.cx.emit_custom(
704 Event::new(ActionsEvent::OnMouseDown(Box::new(action)))
705 .target(self.entity)
706 .origin(self.entity),
707 );
708
709 self
710 }
711
712 fn on_mouse_up<F>(self, action: F) -> Self
713 where
714 F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync,
715 {
716 build_action_model(self.cx, self.entity);
717
718 self.cx.emit_custom(
719 Event::new(ActionsEvent::OnMouseUp(Box::new(action)))
720 .target(self.entity)
721 .origin(self.entity),
722 );
723
724 self
725 }
726
727 fn on_focus_in<F>(self, action: F) -> Self
728 where
729 F: 'static + Fn(&mut EventContext) + Send + Sync,
730 {
731 build_action_model(self.cx, self.entity);
732
733 self.cx.emit_custom(
734 Event::new(ActionsEvent::OnFocusIn(Box::new(action)))
735 .target(self.entity)
736 .origin(self.entity),
737 );
738
739 self
740 }
741
742 fn on_focus_out<F>(self, action: F) -> Self
743 where
744 F: 'static + Fn(&mut EventContext) + Send + Sync,
745 {
746 build_action_model(self.cx, self.entity);
747
748 self.cx.emit_custom(
749 Event::new(ActionsEvent::OnFocusOut(Box::new(action)))
750 .target(self.entity)
751 .origin(self.entity),
752 );
753
754 self
755 }
756
757 fn on_geo_changed<F>(self, action: F) -> Self
758 where
759 F: 'static + Fn(&mut EventContext, GeoChanged) + Send + Sync,
760 {
761 build_action_model(self.cx, self.entity);
762
763 self.cx.emit_custom(
764 Event::new(ActionsEvent::OnGeoChanged(Box::new(action)))
765 .target(self.entity)
766 .origin(self.entity),
767 );
768
769 self
770 }
771
772 fn on_drag<F>(self, action: F) -> Self
773 where
774 F: 'static + Fn(&mut EventContext) + Send + Sync,
775 {
776 build_action_model(self.cx, self.entity);
777
778 if let Some(abilities) = self.cx.style.abilities.get_mut(self.entity) {
779 abilities.set(Abilities::DRAGGABLE, true);
780 }
781
782 self.cx.emit_custom(
783 Event::new(ActionsEvent::OnDragStart(Box::new(action)))
784 .target(self.entity)
785 .origin(self.entity),
786 );
787
788 self
789 }
790
791 fn on_drop<F>(self, action: F) -> Self
792 where
793 F: 'static + Fn(&mut EventContext, DropData) + Send + Sync,
794 {
795 build_action_model(self.cx, self.entity);
796
797 self.cx.emit_custom(
798 Event::new(ActionsEvent::OnDrop(Box::new(action)))
799 .target(self.entity)
800 .origin(self.entity),
801 );
802
803 self
804 }
805}