qframe/widget/context/event.rs
1//! The event handling context.
2
3use std::time::Duration;
4
5use super::frame::Interaction;
6use crate::env::Env;
7use crate::event::Event;
8use crate::geometry::Rect;
9use crate::keymap::Scope;
10use crate::runtime::CopyKind;
11use crate::widget::memory::Memory;
12use crate::widget::{Node, WidgetId};
13
14/// Requests widgets make of the runtime while handling an event.
15#[derive(Debug, Default)]
16pub(crate) struct Effects {
17 pub(crate) focus: Option<WidgetId>,
18 pub(crate) key_capture: Option<Option<WidgetId>>,
19 pub(crate) pointer_capture: bool,
20 pub(crate) flash: Option<WidgetId>,
21 pub(crate) copy: Vec<String>,
22 pub(crate) run_action: Option<(Scope, String)>,
23 pub(crate) pointer_repeat: Option<Duration>,
24 /// End this widget's pointer repeat, see [`EventCx::stop_pointer_repeat`].
25 pub(crate) stop_pointer_repeat: bool,
26 pub(crate) answer: Option<bool>,
27 pub(crate) focus_step: Option<isize>,
28 /// Read the clipboard to learn whether pasting is possible, see [`EventCx::probe_clipboard`].
29 pub(crate) probe_clipboard: bool,
30 /// Copy the mouse selection, see [`EventCx::copy_selection`].
31 pub(crate) copy_selection: Option<CopyKind>,
32}
33
34/// Event handling context.
35pub struct EventCx<'a, Msg> {
36 pub(crate) id: WidgetId,
37 pub(crate) rect: Rect,
38 pub(crate) focus_rect: Option<Rect>,
39 pub(crate) env: &'a Env,
40 pub(crate) memory: &'a mut Memory,
41 pub(crate) interaction: &'a Interaction,
42 pub(crate) messages: &'a mut Vec<Msg>,
43 pub(crate) effects: &'a mut Effects,
44 pub(crate) now: Duration,
45 pub(crate) persistent: bool,
46}
47
48impl<Msg> EventCx<'_, Msg> {
49 /// The id of the widget handling the event.
50 #[must_use]
51 pub fn id(&self) -> WidgetId {
52 self.id
53 }
54
55 /// The area the widget was painted in during the last frame.
56 #[must_use]
57 pub fn area(&self) -> Rect {
58 self.rect
59 }
60
61 /// The area the focused widget was painted in during the last frame, if a widget has focus.
62 /// Lets a container place something next to the focused child, e.g. a context menu opened
63 /// from the keyboard.
64 #[must_use]
65 pub fn focused_area(&self) -> Option<Rect> {
66 self.focus_rect
67 }
68
69 /// The environment.
70 #[must_use]
71 pub fn env(&self) -> &Env {
72 self.env
73 }
74
75 /// Time since the runtime started.
76 #[must_use]
77 pub fn now(&self) -> Duration {
78 self.now
79 }
80
81 /// Sends a message to the application.
82 pub fn emit(&mut self, message: Msg) {
83 self.messages.push(message);
84 }
85
86 /// This widget's state of type `T`.
87 pub fn memory<T: Default + 'static>(&mut self) -> &mut T {
88 self.memory.get::<T>(self.id, self.persistent)
89 }
90
91 /// Whether this widget has keyboard focus.
92 #[must_use]
93 pub fn is_focused(&self) -> bool {
94 self.interaction.focused == Some(self.id)
95 }
96
97 /// Moves keyboard focus to this widget.
98 pub fn request_focus(&mut self) {
99 self.effects.focus = Some(self.id);
100 }
101
102 /// Moves keyboard focus to the next widget in focus order, as Tab does. Forms use it to go
103 /// to the next field on Enter.
104 pub fn focus_next(&mut self) {
105 self.effects.focus_step = Some(1);
106 }
107
108 /// Offers `event` to a child `node` painted in `rect`, as if the child had received it: the
109 /// child keeps its own memory, and its messages and requests go out with this widget's. For
110 /// widgets that take focus as one control and let a child act, e.g. a settings row passing
111 /// Space to its switch. Returns whether the child used the event.
112 pub fn forward(&mut self, node: &Node<Msg>, rect: Rect, event: &Event) -> bool
113 where
114 Msg: 'static,
115 {
116 let mut child = EventCx {
117 id: node.id,
118 rect,
119 focus_rect: self.focus_rect,
120 env: self.env,
121 memory: &mut *self.memory,
122 interaction: self.interaction,
123 messages: &mut *self.messages,
124 effects: &mut *self.effects,
125 now: self.now,
126 persistent: self.persistent,
127 };
128 node.widget.event(&mut child, event)
129 }
130
131 /// While on, every key event goes to this widget first (an open dropdown), and a pointer
132 /// press on any other widget, including one inside it, first sends it
133 /// [`Event::PointerOutside`](crate::event::Event::PointerOutside) and then reaches that widget
134 /// as usual. A press on this widget itself reaches only this widget.
135 pub fn capture_keys(&mut self, on: bool) {
136 self.effects.key_capture = Some(on.then_some(self.id));
137 }
138
139 /// Keeps pointer events flowing to this widget until the button is released.
140 pub fn capture_pointer(&mut self) {
141 self.effects.pointer_capture = true;
142 }
143
144 /// Flashes this widget to confirm an activation.
145 pub fn flash(&mut self) {
146 self.effects.flash = Some(self.id);
147 }
148
149 /// Copies `text` to the system clipboard.
150 pub fn copy(&mut self, text: impl Into<String>) {
151 self.effects.copy.push(text.into());
152 }
153
154 /// Runs keymap action `action` of `scope` as if its key had been pressed, after this event.
155 /// Application actions reach [`App::action`](crate::runtime::App::action) even while a
156 /// modal layer is open, since the user asked for them explicitly (e.g. from a command
157 /// palette).
158 pub fn run_action(&mut self, scope: Scope, action: impl Into<String>) {
159 self.effects.run_action = Some((scope, action.into()));
160 }
161
162 /// While the pointer is captured, delivers a `Drag` event at the last pointer position to
163 /// this widget every `interval` until the button is released, as if the pointer moved in
164 /// place. Terminals send nothing while a button is held still; this lets a widget react
165 /// to how long it is held (hold-to-confirm, auto-repeating steppers). Call it together
166 /// with [`EventCx::capture_pointer`] on the button press.
167 pub fn repeat_pointer(&mut self, interval: Duration) {
168 self.effects.pointer_repeat = Some(interval.max(Duration::from_millis(1)));
169 }
170
171 /// Ends the repeat [`EventCx::repeat_pointer`] started for this widget before the button is
172 /// released, so a widget that needs timed drags only for a while (scrolling while a dragged
173 /// item rests against an edge) does not keep waking the loop afterwards. A repeat asked for in
174 /// the same event wins. Nothing happens when this widget has no repeat running.
175 pub fn stop_pointer_repeat(&mut self) {
176 self.effects.stop_pointer_repeat = true;
177 }
178
179 /// Runs `handle` with a context whose messages are of type `M` instead of `Msg`, and returns
180 /// its result with the messages it sent; everything else (memory, focus, captures, copies)
181 /// is this widget's. Lets a widget drive an inner widget of its own, such as the edit menu of
182 /// a text field, whose choices are the field's business rather than the application's.
183 pub(crate) fn with_messages<M, R>(&mut self, handle: impl FnOnce(&mut EventCx<'_, M>) -> R) -> (R, Vec<M>) {
184 let mut messages = Vec::new();
185 let result = {
186 let mut inner = EventCx {
187 id: self.id,
188 rect: self.rect,
189 focus_rect: self.focus_rect,
190 env: self.env,
191 memory: &mut *self.memory,
192 interaction: self.interaction,
193 messages: &mut messages,
194 effects: &mut *self.effects,
195 now: self.now,
196 persistent: self.persistent,
197 };
198 handle(&mut inner)
199 };
200 (result, messages)
201 }
202
203 /// Reads the clipboard in the background so `can_paste` on this context and on
204 /// [`PaintCx`](crate::widget::PaintCx) soon tells whether it has text, e.g. when an edit menu
205 /// opens.
206 pub(crate) fn probe_clipboard(&mut self) {
207 self.effects.probe_clipboard = true;
208 }
209
210 /// Whether pasting would insert text, as far as the runtime knows.
211 pub(crate) fn can_paste(&self) -> bool {
212 self.interaction.can_paste
213 }
214
215 /// Copies the runtime's mouse selection as `kind`.
216 pub(crate) fn copy_selection(&mut self, kind: CopyKind) {
217 self.effects.copy_selection = Some(kind);
218 }
219
220 /// Resolves the confirmation dialog the runtime shows for
221 /// [`Command::confirm`](crate::runtime::Command::confirm).
222 pub(crate) fn answer(&mut self, confirmed: bool) {
223 self.effects.answer = Some(confirmed);
224 }
225}