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