Skip to main content

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    /// Whether this is a press shown to a widget before the widgets inside it, see
47    /// [`PaintCx::preview_presses`](super::PaintCx::preview_presses).
48    pub(crate) preview: bool,
49    /// Whether this widget, or the widget that forwarded the event to it, holds the pointer: the
50    /// press it belongs to began on it. See [`EventCx::holds_pointer`].
51    pub(crate) holds_pointer: bool,
52}
53
54impl<Msg> EventCx<'_, Msg> {
55    /// Whether the press this pointer event belongs to began on this widget, which captured the
56    /// pointer then. A release that reaches a widget without it began somewhere else: on another
57    /// widget, or on a screen that changed since.
58    pub(crate) fn holds_pointer(&self) -> bool {
59        self.holds_pointer
60    }
61
62    /// Whether the event is a press shown to this widget before the widgets inside it, because
63    /// it asked with [`PaintCx::preview_presses`](super::PaintCx::preview_presses). Using it
64    /// keeps it from them; leaving it lets it go on as usual, to this widget too.
65    pub(crate) fn is_preview(&self) -> bool {
66        self.preview
67    }
68
69    /// The id of the widget handling the event.
70    #[must_use]
71    pub fn id(&self) -> WidgetId {
72        self.id
73    }
74
75    /// The area the widget was painted in during the last frame.
76    #[must_use]
77    pub fn area(&self) -> Rect {
78        self.rect
79    }
80
81    /// The area the focused widget was painted in during the last frame, if a widget has focus.
82    /// Lets a container place something next to the focused child, e.g. a context menu opened
83    /// from the keyboard.
84    #[must_use]
85    pub fn focused_area(&self) -> Option<Rect> {
86        self.focus_rect
87    }
88
89    /// The environment.
90    #[must_use]
91    pub fn env(&self) -> &Env {
92        self.env
93    }
94
95    /// Time since the runtime started.
96    #[must_use]
97    pub fn now(&self) -> Duration {
98        self.now
99    }
100
101    /// How many presses in a row the pointer press or release being handled belongs to: 1 for a
102    /// single click, 2 for the second press of a double click, 3 for a triple click and on, up to
103    /// 255. A press counts as the next of a series when it is the same button on the same cell
104    /// within 400 ms of the press before it; anything else starts over at 1. A release has the
105    /// count of the press it ends. Every other event (keys, pastes, moves, drags, scrolling)
106    /// counts 0.
107    ///
108    /// The runtime counts once for every widget, so a widget tells a double click from two clicks
109    /// without keeping time itself: act on the press or the release whose count is 2, or on every
110    /// even count to let a fast third and fourth press make another double click. In a
111    /// [`Harness`](crate::runtime::Harness) two clicks on one cell with no time advanced between
112    /// them are a double click.
113    #[must_use]
114    pub fn clicks(&self) -> u8 {
115        self.interaction.clicks
116    }
117
118    /// Sends a message to the application.
119    pub fn emit(&mut self, message: Msg) {
120        self.messages.push(message);
121    }
122
123    /// This widget's state of type `T`.
124    pub fn memory<T: Default + 'static>(&mut self) -> &mut T {
125        self.memory.get::<T>(self.id, self.persistent)
126    }
127
128    /// Whether this widget has keyboard focus.
129    #[must_use]
130    pub fn is_focused(&self) -> bool {
131        self.interaction.focused == Some(self.id)
132    }
133
134    /// Moves keyboard focus to this widget.
135    pub fn request_focus(&mut self) {
136        self.effects.focus = Some(self.id);
137    }
138
139    /// Moves keyboard focus to the next widget in focus order, as Tab does. Forms use it to go
140    /// to the next field on Enter.
141    pub fn focus_next(&mut self) {
142        self.focus_step(1);
143    }
144
145    /// Moves keyboard focus by `step` places in focus order, as a directional control asks.
146    pub(crate) fn focus_step(&mut self, step: isize) {
147        self.effects.focus_step = Some(step);
148    }
149
150    /// Offers `event` to a child `node` painted in `rect`, as if the child had received it: the
151    /// child keeps its own memory, and its messages and requests go out with this widget's. For
152    /// widgets that take focus as one control and let a child act, e.g. a settings row passing
153    /// Space to its switch. Returns whether the child used the event.
154    pub fn forward(&mut self, node: &Node<Msg>, rect: Rect, event: &Event) -> bool
155    where
156        Msg: 'static,
157    {
158        let mut child = EventCx {
159            id: node.id,
160            rect,
161            focus_rect: self.focus_rect,
162            env: self.env,
163            memory: &mut *self.memory,
164            interaction: self.interaction,
165            messages: &mut *self.messages,
166            effects: &mut *self.effects,
167            now: self.now,
168            persistent: self.persistent,
169            preview: self.preview,
170            holds_pointer: self.holds_pointer,
171        };
172        node.widget.event(&mut child, event)
173    }
174
175    /// While on, every key event goes to this widget first (an open dropdown), and a pointer
176    /// press on any other widget, including one inside it, first sends it
177    /// [`Event::PointerOutside`](crate::event::Event::PointerOutside) and then reaches that widget
178    /// as usual. A press on this widget itself reaches only this widget.
179    pub fn capture_keys(&mut self, on: bool) {
180        self.effects.key_capture = Some(on.then_some(self.id));
181    }
182
183    /// Keeps pointer events flowing to this widget until the button is released.
184    pub fn capture_pointer(&mut self) {
185        self.effects.pointer_capture = true;
186    }
187
188    /// Flashes this widget to confirm an activation.
189    pub fn flash(&mut self) {
190        self.effects.flash = Some(self.id);
191    }
192
193    /// Copies `text` to the system clipboard.
194    pub fn copy(&mut self, text: impl Into<String>) {
195        self.effects.copy.push(text.into());
196    }
197
198    /// Runs keymap action `action` of `scope` as if its key had been pressed, after this event.
199    /// Application actions reach [`App::action`](crate::runtime::App::action) even while a
200    /// modal layer is open, since the user asked for them explicitly (e.g. from a command
201    /// palette).
202    pub fn run_action(&mut self, scope: Scope, action: impl Into<String>) {
203        self.effects.run_action = Some((scope, action.into()));
204    }
205
206    /// While the pointer is captured, delivers a `Drag` event at the last pointer position to
207    /// this widget every `interval` until the button is released, as if the pointer moved in
208    /// place. Terminals send nothing while a button is held still; this lets a widget react
209    /// to how long it is held (hold-to-confirm, auto-repeating steppers). Call it together
210    /// with [`EventCx::capture_pointer`] on the button press.
211    pub fn repeat_pointer(&mut self, interval: Duration) {
212        self.effects.pointer_repeat = Some(interval.max(Duration::from_millis(1)));
213    }
214
215    /// Ends the repeat [`EventCx::repeat_pointer`] started for this widget before the button is
216    /// released, so a widget that needs timed drags only for a while (scrolling while a dragged
217    /// item rests against an edge) does not keep waking the loop afterwards. A repeat asked for in
218    /// the same event wins. Nothing happens when this widget has no repeat running.
219    pub fn stop_pointer_repeat(&mut self) {
220        self.effects.stop_pointer_repeat = true;
221    }
222
223    /// Runs `handle` with a context whose messages are of type `M` instead of `Msg`, and returns
224    /// its result with the messages it sent; everything else (memory, focus, captures, copies)
225    /// is this widget's. Lets a widget drive an inner widget of its own, such as the edit menu of
226    /// a text field, whose choices are the field's business rather than the application's.
227    pub(crate) fn with_messages<M, R>(&mut self, handle: impl FnOnce(&mut EventCx<'_, M>) -> R) -> (R, Vec<M>) {
228        let mut messages = Vec::new();
229        let result = {
230            let mut inner = EventCx {
231                id: self.id,
232                rect: self.rect,
233                focus_rect: self.focus_rect,
234                env: self.env,
235                memory: &mut *self.memory,
236                interaction: self.interaction,
237                messages: &mut messages,
238                effects: &mut *self.effects,
239                now: self.now,
240                persistent: self.persistent,
241                preview: self.preview,
242                holds_pointer: self.holds_pointer,
243            };
244            handle(&mut inner)
245        };
246        (result, messages)
247    }
248
249    /// Reads the clipboard in the background so `can_paste` on this context and on
250    /// [`PaintCx`](crate::widget::PaintCx) soon tells whether it has text, e.g. when an edit menu
251    /// opens.
252    pub(crate) fn probe_clipboard(&mut self) {
253        self.effects.probe_clipboard = true;
254    }
255
256    /// Whether pasting would insert text, as far as the runtime knows.
257    pub(crate) fn can_paste(&self) -> bool {
258        self.interaction.can_paste
259    }
260
261    /// Copies the runtime's mouse selection as `kind`.
262    pub(crate) fn copy_selection(&mut self, kind: CopyKind) {
263        self.effects.copy_selection = Some(kind);
264    }
265
266    /// Resolves the confirmation dialog the runtime shows for
267    /// [`Command::confirm`](crate::runtime::Command::confirm).
268    pub(crate) fn answer(&mut self, confirmed: bool) {
269        self.effects.answer = Some(confirmed);
270    }
271}