Skip to main content

vizia_core/modifiers/
actions.rs

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
9/// An event used to modify the modal properties of a view, such as an attached tooltip.
10pub enum ModalEvent {
11    /// Show the attached tooltip.
12    ShowTooltip,
13    /// Hide the attached tooltip.
14    HideTooltip,
15    /// Show the attached menu.
16    ShowMenu,
17    /// Hide the attached menu.
18    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::MouseDown(MouseButton::Right) => {
43                self.menu_visible.set(true);
44            }
45            WindowEvent::MouseOver => {
46                if !self.tooltip_visible.get().0 {
47                    self.tooltip_visible.set((true, true));
48                }
49            }
50            WindowEvent::MouseOut => self.tooltip_visible.set((false, true)),
51            WindowEvent::FocusIn => {
52                if !self.tooltip_visible.get().0 {
53                    self.tooltip_visible.set((true, false));
54                }
55            }
56            WindowEvent::FocusOut => self.tooltip_visible.set((false, false)),
57            WindowEvent::FocusVisibility(vis) if !(*vis) => {
58                self.tooltip_visible.set((false, false))
59            }
60            WindowEvent::KeyDown(code, _) if *code == Code::Escape => {
61                self.tooltip_visible.set((false, false));
62                self.menu_visible.set(false);
63            }
64            WindowEvent::PressDown { mouse: _ } => self.tooltip_visible.set((false, true)),
65            _ => {}
66        });
67    }
68}
69
70pub(crate) struct ActionsModel {
71    pub(crate) on_press: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
72    pub(crate) on_press_down: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
73    pub(crate) on_double_click: Option<Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>>,
74    pub(crate) on_hover: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
75    pub(crate) on_hover_out: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
76    pub(crate) on_over: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
77    pub(crate) on_over_out: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
78    pub(crate) on_mouse_move: Option<Box<dyn Fn(&mut EventContext, f32, f32) + Send + Sync>>,
79    pub(crate) on_mouse_down: Option<Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>>,
80    pub(crate) on_mouse_up: Option<Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>>,
81    pub(crate) on_focus_in: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
82    pub(crate) on_focus_out: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
83    pub(crate) on_geo_changed: Option<Box<dyn Fn(&mut EventContext, GeoChanged) + Send + Sync>>,
84}
85
86impl ActionsModel {
87    pub(crate) fn new() -> Self {
88        Self {
89            on_press: None,
90            on_press_down: None,
91            on_double_click: None,
92            on_hover: None,
93            on_hover_out: None,
94            on_over: None,
95            on_over_out: None,
96            on_mouse_move: None,
97            on_mouse_down: None,
98            on_mouse_up: None,
99            on_focus_in: None,
100            on_focus_out: None,
101            on_geo_changed: 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
164        event.map(|window_event, meta| match window_event {
165            WindowEvent::Press { mouse } => {
166                let over = if *mouse { cx.hovered() } else { cx.focused() };
167                if cx.current() != over && !over.is_descendant_of(cx.tree, cx.current()) {
168                    return;
169                }
170
171                if !cx.is_disabled() && cx.current == meta.target {
172                    // Only grab keyboard focus if the view has opted in via
173                    // the FOCUSABLE ability. Without this gate, any view with
174                    // an `on_press` callback steals focus on click even when
175                    // `.focusable(false)` is set, and every subsequent
176                    // Space / Enter keypress then re-dispatches
177                    // `WindowEvent::Press` straight back to the clicked view
178                    // and re-runs its action — a confusing non-feature for
179                    // chrome buttons that are mouse-only by design.
180                    let focusable = cx
181                        .style
182                        .abilities
183                        .get(cx.current())
184                        .is_some_and(|abilities| abilities.contains(Abilities::FOCUSABLE));
185                    if focusable {
186                        cx.focus();
187                    }
188                    if let Some(action) = &self.on_press {
189                        (action)(cx);
190                    }
191                }
192            }
193
194            WindowEvent::PressDown { mouse } => {
195                let over = if *mouse { cx.hovered() } else { cx.focused() };
196                if cx.current() != over && !over.is_descendant_of(cx.tree, cx.current()) {
197                    return;
198                }
199                if !cx.is_disabled() && cx.current == meta.target {
200                    if let Some(action) = &self.on_press_down {
201                        (action)(cx);
202                    }
203                }
204            }
205
206            WindowEvent::MouseDoubleClick(button) => {
207                if meta.target == cx.current && !cx.is_disabled() {
208                    if let Some(action) = &self.on_double_click {
209                        (action)(cx, *button);
210                    }
211                }
212            }
213
214            WindowEvent::MouseEnter => {
215                if meta.target == cx.current() {
216                    if let Some(action) = &self.on_hover {
217                        (action)(cx);
218                    }
219                }
220            }
221
222            WindowEvent::MouseLeave => {
223                if meta.target == cx.current() {
224                    if let Some(action) = &self.on_hover_out {
225                        (action)(cx);
226                    }
227                }
228            }
229
230            WindowEvent::MouseOver => {
231                if let Some(action) = &self.on_over {
232                    (action)(cx);
233                }
234            }
235
236            WindowEvent::MouseOut => {
237                // if meta.target == cx.current() {
238                if let Some(action) = &self.on_over_out {
239                    (action)(cx);
240                }
241                // }
242            }
243
244            WindowEvent::MouseMove(x, y) => {
245                if let Some(action) = &self.on_mouse_move {
246                    (action)(cx, *x, *y);
247                }
248            }
249
250            WindowEvent::MouseDown(mouse_button) => {
251                if let Some(action) = &self.on_mouse_down {
252                    (action)(cx, *mouse_button);
253                }
254            }
255
256            WindowEvent::MouseUp(mouse_button) => {
257                if let Some(action) = &self.on_mouse_up {
258                    (action)(cx, *mouse_button);
259                }
260            }
261
262            WindowEvent::FocusIn => {
263                if let Some(action) = &self.on_focus_in {
264                    (action)(cx);
265                }
266            }
267
268            WindowEvent::FocusOut => {
269                if let Some(action) = &self.on_focus_out {
270                    (action)(cx);
271                }
272            }
273
274            WindowEvent::GeometryChanged(geo) => {
275                if meta.target == cx.current() {
276                    if let Some(action) = &self.on_geo_changed {
277                        (action)(cx, *geo);
278                    }
279                }
280            }
281
282            _ => {}
283        });
284    }
285}
286
287pub(crate) enum ActionsEvent {
288    OnPress(Box<dyn Fn(&mut EventContext) + Send + Sync>),
289    OnPressDown(Box<dyn Fn(&mut EventContext) + Send + Sync>),
290    OnDoubleClick(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
291    OnHover(Box<dyn Fn(&mut EventContext) + Send + Sync>),
292    OnHoverOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
293    OnOver(Box<dyn Fn(&mut EventContext) + Send + Sync>),
294    OnOverOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
295    OnMouseMove(Box<dyn Fn(&mut EventContext, f32, f32) + Send + Sync>),
296    OnMouseDown(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
297    OnMouseUp(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
298    OnFocusIn(Box<dyn Fn(&mut EventContext) + Send + Sync>),
299    OnFocusOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
300    OnGeoChanged(Box<dyn Fn(&mut EventContext, GeoChanged) + Send + Sync>),
301}
302
303/// Modifiers which add an action callback to a view.
304pub trait ActionModifiers<V> {
305    /// Adds a callback which is performed when the the view receives the [`Press`](crate::prelude::WindowEvent::Press) event.
306    /// By default a view receives the [`Press`](crate::prelude::WindowEvent::Press) event when the left mouse button is pressed and then released on the view,
307    /// or when the space or enter keys are pressed and then released while the view is focused.
308    ///
309    /// # Example
310    /// ```rust
311    /// # use vizia_core::prelude::*;
312    /// # let mut cx = &mut Context::default();
313    /// Element::new(cx).on_press(|_| debug!("View was pressed!"));
314    /// ```
315    fn on_press<F>(self, action: F) -> Self
316    where
317        F: 'static + Fn(&mut EventContext) + Send + Sync;
318
319    /// Adds a callback which is performed when the the view receives the [`PressDown`](crate::prelude::WindowEvent::PressDown) event.
320    // By default a view receives the [`PressDown`](crate::prelude::WindowEvent::PressDown) event when the left mouse button is pressed on the view,
321    /// or when the space or enter keys are pressed while the view is focused.
322    ///
323    /// # Example
324    /// ```rust
325    /// # use vizia_core::prelude::*;
326    /// # let mut cx = &mut Context::default();
327    /// Element::new(cx).on_press_down(|_| debug!("View was pressed down!"));
328    /// ```
329    fn on_press_down<F>(self, action: F) -> Self
330    where
331        F: 'static + Fn(&mut EventContext) + Send + Sync;
332
333    /// Adds a callback which is performed when the the view receives the [`MouseDoubleClick`](crate::prelude::WindowEvent::MouseDoubleClick) event.
334    ///
335    /// # Example
336    /// ```rust
337    /// # use vizia_core::prelude::*;
338    /// # let mut cx = &mut Context::default();
339    /// Element::new(cx).on_double_click(|_, _button| debug!("View was double clicked on!"));
340    /// ```
341    fn on_double_click<F>(self, action: F) -> Self
342    where
343        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
344
345    /// Adds a callback which is performed when the mouse pointer moves over a view.
346    /// This callback is not triggered when the mouse pointer moves over an overlapping child of the view.
347    ///
348    /// # Example
349    /// ```rust
350    /// # use vizia_core::prelude::*;
351    /// # let mut cx = &mut Context::default();
352    /// Element::new(cx).on_hover(|_| debug!("Mouse cursor entered the view!"));
353    /// ```
354    fn on_hover<F>(self, action: F) -> Self
355    where
356        F: 'static + Fn(&mut EventContext) + Send + Sync;
357
358    /// Adds a callback which is performed when the mouse pointer moves away from a view.
359    /// This callback is not triggered when the mouse pointer moves away from an overlapping child of the view.
360    ///
361    /// # Example
362    /// ```rust
363    /// # use vizia_core::prelude::*;
364    /// # let mut cx = &mut Context::default();
365    /// Element::new(cx).on_hover_out(|_| debug!("Mouse cursor left the view!"));
366    /// ```
367    fn on_hover_out<F>(self, action: F) -> Self
368    where
369        F: 'static + Fn(&mut EventContext) + Send + Sync;
370
371    /// Adds a callback which is performed when the mouse pointer moves over the bounds of a view,
372    /// including any overlapping children.
373    ///
374    /// # Example
375    /// ```rust
376    /// # use vizia_core::prelude::*;
377    /// # let mut cx = &mut Context::default();
378    /// Element::new(cx).on_over(|_| debug!("Mouse cursor entered the view bounds!"));
379    /// ```
380    fn on_over<F>(self, action: F) -> Self
381    where
382        F: 'static + Fn(&mut EventContext) + Send + Sync;
383
384    /// Adds a callback which is performed when the mouse pointer moves away from the bounds of a view,
385    /// including any overlapping children.
386    ///
387    /// # Example
388    /// ```rust
389    /// # use vizia_core::prelude::*;
390    /// # let mut cx = &mut Context::default();
391    /// Element::new(cx).on_over_out(|_| debug!("Mouse cursor left the view bounds!"));
392    /// ```
393    fn on_over_out<F>(self, action: F) -> Self
394    where
395        F: 'static + Fn(&mut EventContext) + Send + Sync;
396
397    /// Adds a callback which is performed when the mouse pointer moves within the bounds of a view.
398    ///
399    /// # Example
400    /// ```rust
401    /// # use vizia_core::prelude::*;
402    /// # let mut cx = &mut Context::default();
403    /// Element::new(cx).on_mouse_move(|_, x, y| debug!("Cursor moving: {} {}", x, y));
404    /// ```
405    fn on_mouse_move<F>(self, action: F) -> Self
406    where
407        F: 'static + Fn(&mut EventContext, f32, f32) + Send + Sync;
408
409    /// Adds a callback which is performed when a mouse button is pressed on the view.
410    /// Unlike the `on_press` callback, this callback is triggered for all mouse buttons and not for any keyboard keys.
411    ///
412    /// # Example
413    /// ```rust
414    /// # use vizia_core::prelude::*;
415    /// # let mut cx = &mut Context::default();
416    /// Element::new(cx).on_mouse_down(|_, button| debug!("Mouse button, {:?}, was pressed!", button));
417    /// ```
418    fn on_mouse_down<F>(self, action: F) -> Self
419    where
420        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
421
422    /// Adds a callback which is performed when a mouse button is released on the view.
423    /// Unlike the `on_release` callback, this callback is triggered for all mouse buttons and not for any keyboard keys.
424    ///
425    /// # Example
426    /// ```rust
427    /// # use vizia_core::prelude::*;
428    /// # let mut cx = &mut Context::default();
429    /// Element::new(cx).on_mouse_up(|_, button| debug!("Mouse button, {:?}, was released!", button));
430    /// ```
431    fn on_mouse_up<F>(self, action: F) -> Self
432    where
433        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
434
435    /// Adds a callback which is performed when the view gains keyboard focus.
436    ///
437    /// # Example
438    /// ```rust
439    /// # use vizia_core::prelude::*;
440    /// # let mut cx = &mut Context::default();
441    /// Element::new(cx).on_focus_in(|_| debug!("View gained keyboard focus!"));
442    /// ```
443    fn on_focus_in<F>(self, action: F) -> Self
444    where
445        F: 'static + Fn(&mut EventContext) + Send + Sync;
446
447    /// Adds a callback which is performed when the view loses keyboard focus.
448    ///
449    /// # Example
450    /// ```rust
451    /// # use vizia_core::prelude::*;
452    /// # let mut cx = &mut Context::default();
453    /// Element::new(cx).on_focus_out(|_| debug!("View lost keyboard focus!"));
454    /// ```
455    fn on_focus_out<F>(self, action: F) -> Self
456    where
457        F: 'static + Fn(&mut EventContext) + Send + Sync;
458
459    /// Adds a callback which is performed when the the view changes size or position after layout.
460    ///
461    /// # Example
462    /// ```rust
463    /// # use vizia_core::prelude::*;
464    /// # let mut cx = &mut Context::default();
465    /// Element::new(cx).on_geo_changed(|_, _| debug!("View geometry changed!"));
466    /// ```
467    fn on_geo_changed<F>(self, action: F) -> Self
468    where
469        F: 'static + Fn(&mut EventContext, GeoChanged) + Send + Sync;
470
471    /// Adds a popup tooltip to the view.
472    fn tooltip<C: Fn(&mut Context) -> Handle<'_, Tooltip> + 'static>(self, content: C) -> Self;
473
474    /// Adds a popup menu to the view.
475    fn menu<C: Fn(&mut Context) -> Handle<'_, Popover> + 'static>(self, content: C) -> Self;
476}
477
478// If the entity doesn't have an `ActionsModel` then add one to the entity
479fn build_action_model(cx: &mut Context, entity: Entity) {
480    if cx.models.get(&entity).and_then(|models| models.get(&TypeId::of::<ActionsModel>())).is_none()
481    {
482        cx.with_current(entity, |cx| {
483            ActionsModel::new().build(cx);
484        });
485    }
486}
487
488fn build_modal_model(cx: &mut Context, entity: Entity) {
489    if cx.models.get(&entity).and_then(|models| models.get(&TypeId::of::<ModalModel>())).is_none() {
490        cx.with_current(entity, |cx| {
491            ModalModel {
492                tooltip_visible: Signal::new((false, true)),
493                menu_visible: Signal::new(false),
494            }
495            .build(cx);
496        });
497    }
498}
499
500impl<V: View> ActionModifiers<V> for Handle<'_, V> {
501    fn tooltip<C: Fn(&mut Context) -> Handle<'_, Tooltip> + 'static>(self, content: C) -> Self {
502        let entity = self.entity();
503
504        build_modal_model(self.cx, entity);
505
506        self.cx.with_current(entity, move |cx| {
507            let tooltip_visible = cx.data::<ModalModel>().tooltip_visible;
508            Binding::new(cx, tooltip_visible, move |cx| {
509                let tooltip_visible = tooltip_visible.get();
510                let tooltip_delay = cx.environment().tooltip_delay;
511                if tooltip_visible.0 {
512                    (content)(cx)
513                        .on_build(|cx| {
514                            if tooltip_visible.1 {
515                                cx.play_animation(
516                                    "tooltip_fade",
517                                    Duration::from_millis(100),
518                                    tooltip_delay,
519                                )
520                            }
521                        })
522                        .id(format!("{entity}"));
523                }
524            });
525        });
526
527        self.described_by(format!("{entity}"))
528    }
529
530    fn menu<C: Fn(&mut Context) -> Handle<'_, Popover> + 'static>(self, content: C) -> Self {
531        let entity = self.entity();
532
533        build_modal_model(self.cx, entity);
534
535        self.cx.with_current(entity, |cx| {
536            let menu_visible = cx.data::<ModalModel>().menu_visible;
537            Binding::new(cx, menu_visible, move |cx| {
538                let is_visible = menu_visible.get();
539                if is_visible {
540                    (content)(cx).on_build(|cx| {
541                        cx.add_listener(move |_: &mut Popover, cx, event| {
542                            event.map(|window_event, _: &mut crate::events::EventMeta| {
543                                match window_event {
544                                    WindowEvent::MouseDown(_) => {
545                                        //if flag && meta.origin != cx.current() {
546                                        // Check if the mouse was pressed outside of any descendants
547                                        if !cx.hovered.is_descendant_of(cx.tree, cx.current) {
548                                            cx.emit(ModalEvent::HideMenu);
549                                        }
550                                        //}
551                                    }
552
553                                    _ => {}
554                                }
555                            });
556                        });
557                    });
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}