freya/_docs/events.rs
1//! # Events
2//!
3//! Events let your UI react to user input: a click, a key press, the cursor entering an
4//! element, a scroll, a file drop, and so on.
5//!
6//! Events are attached to **elements** (`rect`, `label`, `paragraph`, ...), not to components.
7//! Components are just a way to organize and reuse UI; the listeners are on the elements that
8//! the component returns.
9//!
10//! ```rust, no_run
11//! # use freya::prelude::*;
12//! # fn app() -> impl IntoElement {
13//! rect()
14//! .width(Size::px(100.))
15//! .height(Size::px(100.))
16//! .background(Color::RED)
17//! .on_press(|_| println!("Clicked!"))
18//! # }
19//! ```
20//!
21//! ## Event handlers
22//!
23//! Most events have a method named `on_<event>` (and global variants `on_global_<event>`).
24//! See [`EventHandlersExt`](freya_core::prelude::EventHandlersExt) for the full list.
25//!
26//! Common ones:
27//!
28//! - Pointer / mouse / touch: `on_press`, `on_secondary_down`, `on_pointer_press`, `on_pointer_down`,
29//! `on_pointer_move`, `on_pointer_enter`, `on_pointer_leave`, `on_pointer_over`, `on_pointer_out`.
30//! - Keyboard: `on_key_down`, `on_key_up`.
31//! - Wheel: `on_wheel`.
32//! - Layout: `on_sized`.
33//! - Files: `on_file_drop`.
34//!
35//! Each handler receives an [`Event<D>`](freya_core::prelude::Event) where `D` is the payload
36//! (e.g [`PointerEventData`](freya_core::prelude::PointerEventData),
37//! [`KeyboardEventData`](freya_core::prelude::KeyboardEventData), etc).
38//!
39//! ## Propagation (bubbling)
40//!
41//! When an event fires on a target element it then **bubbles** up through its ancestors,
42//! invoking any matching handler along the way.
43//!
44//! ```rust, no_run
45//! # use freya::prelude::*;
46//! # fn app() -> impl IntoElement {
47//! rect()
48//! .on_press(|_| println!("outer"))
49//! .child(rect().on_press(|_| println!("inner")))
50//! # }
51//! ```
52//!
53//! Pressing the inner rect prints `inner` and then `outer`.
54//!
55//! ### Stopping propagation
56//!
57//! Call [`stop_propagation`](freya_core::prelude::Event::stop_propagation) on the event to
58//! prevent it from continuing to bubble up to ancestors. The current handler still runs to
59//! completion; only ancestor handlers for **this same event** are skipped.
60//!
61//! ```rust, no_run
62//! # use freya::prelude::*;
63//! # fn app() -> impl IntoElement {
64//! rect()
65//! .on_press(|_| println!("outer"))
66//! .child(rect().on_press(|e: Event<PressEventData>| {
67//! e.stop_propagation();
68//! println!("inner only");
69//! }))
70//! # }
71//! ```
72//!
73//! Note that not every event bubbles. Move/enter/leave events, capture events, and global
74//! events do not bubble: they target specific elements directly.
75//!
76//! ## Default behavior and `prevent_default`
77//!
78//! Some events have a **default behavior**: side effects that Freya runs after the handler
79//! unless you opt out. The most common case is that an element-level event (e.g `on_mouse_up`)
80//! also dispatches a related **global** event (e.g `on_global_pointer_press`), and that some
81//! events (e.g `on_mouse_down`) get translated into the unified
82//! [pointer events](freya_core::prelude::PointerEventData) (e.g `on_pointer_down`).
83//!
84//! Calling [`prevent_default`](freya_core::prelude::Event::prevent_default) cancels those
85//! follow-up events for this dispatch. It is **not** the same as `stop_propagation`:
86//!
87//! - `stop_propagation`: stops **this same event** from bubbling to ancestors.
88//! - `prevent_default`: cancels **other related events** that would otherwise fire as a
89//! consequence of this one.
90//!
91//! Each event declares which other events it can cancel. For example a `on_mouse_up` handler
92//! that calls `prevent_default` will additionally cancel the `on_pointer_press` and the
93//! `on_global_pointer_press` events that would have fired afterwards.
94//!
95//! ```rust, no_run
96//! # use freya::prelude::*;
97//! # fn app() -> impl IntoElement {
98//! rect()
99//! .on_global_pointer_press(|_| println!("Anywhere on screen"))
100//! .child(rect().on_mouse_up(|e: Event<MouseEventData>| {
101//! // Suppresses the global handler above for this click only.
102//! e.prevent_default();
103//! println!("Click handled here");
104//! }))
105//! # }
106//! ```
107//!
108//! A typical use case is a draggable scrollbar: while dragging, the scrollbar handles the
109//! pointer move events itself and calls `prevent_default` so that no other element behind the
110//! cursor receives hover/move events for the duration of the drag.
111//!
112//! ## Event ordering and priority
113//!
114//! When several events would dispatch in the same frame, Freya processes them in a fixed
115//! priority order:
116//!
117//! 1. **Capture** events (e.g `on_capture_global_pointer_press`, `on_capture_global_pointer_move`).
118//! 2. **Leave** events (`on_pointer_leave`, `on_pointer_out`).
119//! 3. **Enter / over** events (`on_pointer_enter`, `on_pointer_over`).
120//! 4. Everything else.
121//!
122//! Within the same priority class, events are sorted by layer and cursor position so that
123//! the topmost element under the cursor is reached first. Because of this, a handler that
124//! calls `prevent_default` on a higher-priority event can naturally cancel lower-priority
125//! events that would otherwise have fired in the same frame.
126//!
127//! Capture events are the strongest tool here: they run before anything else, and a
128//! `prevent_default` on a capture handler can cancel the regular pointer events for that
129//! tick. They are useful when you need to intercept input globally before any element sees
130//! it (for example, dismissing an overlay on the next click anywhere).
131//!
132//! ## Global events
133//!
134//! Global events fire **once per dispatch**, regardless of which element is under the cursor
135//! or focused. They are useful when you want to react to input that is not necessarily aimed
136//! at your element.
137//!
138//! - `on_global_pointer_press`
139//! - `on_global_pointer_down`
140//! - `on_global_pointer_move`
141//! - `on_global_key_down`
142//! - `on_global_key_up`
143//! - `on_global_file_hover`
144//! - `on_global_file_hover_cancelled`
145//!
146//! Global events do **not** bubble (they are dispatched directly to every listener), so
147//! `stop_propagation` has no effect on them. A non-global handler that calls
148//! `prevent_default` will, however, suppress the matching global event for that dispatch.
149//!
150//! ## Components don't have events
151//!
152//! Components are just data and a `render` method. To expose a "click" or "change" hook from
153//! a component, accept a callback as a field and forward it from the inner element.
154//!
155//! ```rust, no_run
156//! # use freya::prelude::*;
157//! #[derive(PartialEq)]
158//! struct MyButton {
159//! on_press: EventHandler<Event<PressEventData>>,
160//! }
161//!
162//! impl Component for MyButton {
163//! fn render(&self) -> impl IntoElement {
164//! rect()
165//! .padding(8.)
166//! .background(Color::BLUE)
167//! .on_press(self.on_press.clone())
168//! .child("Press me")
169//! }
170//! }
171//! ```