kas_core/event/cx/
mod.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Event context state
7
8use linear_map::{LinearMap, set::LinearSet};
9use smallvec::SmallVec;
10use std::any::TypeId;
11use std::collections::{HashMap, VecDeque};
12use std::future::Future;
13use std::ops::{Deref, DerefMut};
14use std::pin::Pin;
15use std::time::Instant;
16
17use super::*;
18use crate::cast::{Cast, Conv};
19use crate::config::{ConfigMsg, WindowConfig};
20use crate::draw::DrawShared;
21use crate::geom::{Offset, Rect, Vec2};
22use crate::messages::Erased;
23use crate::runner::{Platform, RunnerT, WindowDataErased};
24use crate::theme::{SizeCx, ThemeSize};
25use crate::window::{PopupDescriptor, WindowId};
26use crate::{Action, HasId, Id, Node};
27use key::PendingSelFocus;
28use nav::PendingNavFocus;
29
30#[cfg(feature = "accesskit")] mod accessibility;
31mod key;
32mod nav;
33mod press;
34mod send;
35mod timer;
36mod window;
37
38pub use nav::NavAdvance;
39pub use press::{GrabBuilder, GrabMode, Press, PressSource, PressStart};
40pub(crate) use press::{Mouse, Touch};
41pub use timer::TimerHandle;
42
43struct PopupState {
44    id: WindowId,
45    desc: PopupDescriptor,
46    old_nav_focus: Option<Id>,
47    is_sized: bool,
48}
49
50/// Event context state
51///
52/// This struct encapsulates window-specific event-handling state and handling.
53/// Most operations are only available via a [`EventCx`] handle, though some
54/// are available on this struct.
55///
56/// Besides event handling, this struct also configures widgets.
57///
58/// Some methods are intended only for usage by graphics and platform backends
59/// and are hidden from generated documentation unless the `internal_doc`
60/// feature is enabled. Event handling assumes [winit].
61///
62/// [winit]: https://github.com/rust-windowing/winit
63//
64// Note that the most frequent usage of fields is to check highlighting states
65// for each widget during drawing. Most fields contain only a few values, hence
66// `SmallVec` is used to keep contents in local memory.
67pub struct EventState {
68    pub(crate) window_id: WindowId,
69    pub(crate) config: WindowConfig,
70    platform: Platform,
71    disabled: Vec<Id>,
72    window_has_focus: bool,
73    #[cfg(feature = "accesskit")]
74    accesskit_is_enabled: bool,
75    modifiers: ModifiersState,
76    /// Key (and IME) focus is on same widget as sel_focus; otherwise its value is ignored
77    key_focus: bool,
78    ime: Option<ImePurpose>,
79    old_ime_target: Option<Id>,
80    /// Rect is cursor area in sel_focus's coordinate space if size != ZERO
81    ime_cursor_area: Rect,
82    last_ime_rect: Rect,
83    sel_focus: Option<Id>,
84    nav_focus: Option<Id>,
85    nav_fallback: Option<Id>,
86    key_depress: LinearMap<PhysicalKey, Id>,
87    mouse: Mouse,
88    touch: Touch,
89    access_keys: HashMap<Key, Id>,
90    popups: SmallVec<[PopupState; 16]>,
91    popup_removed: SmallVec<[(Id, WindowId); 16]>,
92    time_updates: Vec<(Instant, Id, TimerHandle)>,
93    frame_updates: LinearSet<(Id, TimerHandle)>,
94    need_frame_update: bool,
95    // Set of messages awaiting sending
96    send_queue: VecDeque<(Id, Erased)>,
97    // Set of futures of messages together with id of sending widget
98    fut_messages: Vec<(Id, Pin<Box<dyn Future<Output = Erased>>>)>,
99    pub(super) pending_send_targets: Vec<(TypeId, Id)>,
100    // Widget requiring update
101    pending_update: Option<Id>,
102    // Optional new target for selection focus. bool is true if this also gains key focus.
103    pending_sel_focus: Option<PendingSelFocus>,
104    pending_nav_focus: PendingNavFocus,
105    pub(crate) action: Action,
106}
107
108impl EventState {
109    /// Construct per-window event state
110    #[inline]
111    pub(crate) fn new(window_id: WindowId, config: WindowConfig, platform: Platform) -> Self {
112        EventState {
113            window_id,
114            config,
115            platform,
116            disabled: vec![],
117            window_has_focus: false,
118            #[cfg(feature = "accesskit")]
119            accesskit_is_enabled: false,
120            modifiers: ModifiersState::empty(),
121            key_focus: false,
122            ime: None,
123            old_ime_target: None,
124            ime_cursor_area: Rect::ZERO,
125            last_ime_rect: Rect::ZERO,
126            sel_focus: None,
127            nav_focus: None,
128            nav_fallback: None,
129            key_depress: Default::default(),
130            mouse: Default::default(),
131            touch: Default::default(),
132            access_keys: Default::default(),
133            popups: Default::default(),
134            popup_removed: Default::default(),
135            time_updates: vec![],
136            frame_updates: Default::default(),
137            need_frame_update: false,
138            send_queue: Default::default(),
139            pending_send_targets: vec![],
140            fut_messages: vec![],
141            pending_update: None,
142            pending_sel_focus: None,
143            pending_nav_focus: PendingNavFocus::None,
144            action: Action::empty(),
145        }
146    }
147
148    /// Update scale factor
149    pub(crate) fn update_config(&mut self, scale_factor: f32) {
150        self.config.update(scale_factor);
151    }
152
153    /// Configure a widget tree
154    ///
155    /// This should be called by the toolkit on the widget tree when the window
156    /// is created (before or after resizing).
157    ///
158    /// This method calls [`ConfigCx::configure`] in order to assign
159    /// [`Id`] identifiers and call widgets' [`Events::configure`]
160    /// method. Additionally, it updates the [`EventState`] to account for
161    /// renamed and removed widgets.
162    pub(crate) fn full_configure(&mut self, sizer: &dyn ThemeSize, node: Node) {
163        let id = Id::ROOT.make_child(self.window_id.get().cast());
164
165        log::debug!(target: "kas_core::event", "full_configure of Window{id}");
166
167        // These are recreated during configure:
168        self.nav_fallback = None;
169
170        ConfigCx::new(sizer, self).configure(node, id);
171        self.action |= Action::REGION_MOVED;
172    }
173
174    /// Construct a [`EventCx`] referring to this state
175    ///
176    /// Invokes the given closure on this [`EventCx`].
177    #[inline]
178    pub(crate) fn with<'a, F: FnOnce(&mut EventCx)>(
179        &'a mut self,
180        runner: &'a mut dyn RunnerT,
181        window: &'a dyn WindowDataErased,
182        f: F,
183    ) {
184        let mut cx = EventCx {
185            state: self,
186            runner,
187            window,
188            target_is_disabled: false,
189            last_child: None,
190            scroll: Scroll::None,
191        };
192        f(&mut cx);
193    }
194
195    /// Clear all focus and grabs on `target`
196    fn cancel_event_focus(&mut self, target: &Id) {
197        self.clear_sel_socus_on(target);
198        self.clear_nav_focus_on(target);
199        self.mouse.cancel_event_focus(target);
200        self.touch.cancel_event_focus(target);
201    }
202
203    /// Check whether a widget is disabled
204    ///
205    /// A widget is disabled if any ancestor is.
206    #[inline]
207    pub fn is_disabled(&self, w_id: &Id) -> bool {
208        // TODO(opt): we should be able to use binary search here
209        for id in &self.disabled {
210            if id.is_ancestor_of(w_id) {
211                return true;
212            }
213        }
214        false
215    }
216
217    /// Access event-handling configuration
218    #[inline]
219    pub fn config(&self) -> &WindowConfig {
220        &self.config
221    }
222
223    /// Is mouse panning enabled?
224    #[inline]
225    pub fn config_enable_pan(&self, source: PressSource) -> bool {
226        source.is_touch()
227            || source.is_primary()
228                && self
229                    .config
230                    .event()
231                    .mouse_pan()
232                    .is_enabled_with(self.modifiers())
233    }
234
235    /// Is mouse text panning enabled?
236    #[inline]
237    pub fn config_enable_mouse_text_pan(&self) -> bool {
238        self.config
239            .event()
240            .mouse_text_pan()
241            .is_enabled_with(self.modifiers())
242    }
243
244    /// Test pan threshold against config, adjusted for scale factor
245    ///
246    /// Returns true when `dist` is large enough to switch to pan mode.
247    #[inline]
248    pub fn config_test_pan_thresh(&self, dist: Offset) -> bool {
249        Vec2::conv(dist).abs().max_comp() >= self.config.event().pan_dist_thresh()
250    }
251
252    /// Update event configuration
253    #[inline]
254    pub fn change_config(&mut self, msg: ConfigMsg) {
255        self.action |= self.config.change_config(msg);
256    }
257
258    /// Set/unset a widget as disabled
259    ///
260    /// Disabled status applies to all descendants and blocks reception of
261    /// events ([`Unused`] is returned automatically when the
262    /// recipient or any ancestor is disabled).
263    ///
264    /// Disabling a widget clears navigation, selection and key focus when the
265    /// target is disabled, and also cancels press/pan grabs.
266    pub fn set_disabled(&mut self, target: Id, disable: bool) {
267        if disable {
268            self.cancel_event_focus(&target);
269        }
270
271        for (i, id) in self.disabled.iter().enumerate() {
272            if target == id {
273                if !disable {
274                    self.redraw(target);
275                    self.disabled.remove(i);
276                }
277                return;
278            }
279        }
280        if disable {
281            self.action(&target, Action::REDRAW);
282            self.disabled.push(target);
283        }
284    }
285
286    /// Notify that a widget must be redrawn
287    ///
288    /// This is equivalent to calling [`Self::action`] with [`Action::REDRAW`].
289    #[inline]
290    pub fn redraw(&mut self, id: impl HasId) {
291        self.action(id, Action::REDRAW);
292    }
293
294    /// Notify that a widget must be resized
295    ///
296    /// This is equivalent to calling [`Self::action`] with [`Action::RESIZE`].
297    #[inline]
298    pub fn resize(&mut self, id: impl HasId) {
299        self.action(id, Action::RESIZE);
300    }
301
302    /// Notify that widgets under self may have moved
303    #[inline]
304    pub fn region_moved(&mut self) {
305        // Do not take id: this always applies to the whole window
306        self.action |= Action::REGION_MOVED;
307    }
308
309    /// Terminate the GUI
310    #[inline]
311    pub fn exit(&mut self) {
312        self.send(Id::ROOT, Command::Exit);
313    }
314
315    /// Notify that an [`Action`] should happen
316    ///
317    /// This causes the given action to happen after event handling.
318    ///
319    /// Whenever a widget is added, removed or replaced, a reconfigure action is
320    /// required. Should a widget's size requirements change, these will only
321    /// affect the UI after a reconfigure action.
322    #[inline]
323    pub fn action(&mut self, id: impl HasId, action: Action) {
324        fn inner(cx: &mut EventState, id: Id, mut action: Action) {
325            if action.contains(Action::UPDATE) {
326                cx.request_update(id);
327                action.remove(Action::UPDATE);
328            }
329
330            // TODO(opt): handle sub-tree SCROLLED. This is probably equivalent to using `_replay` without a message but with `scroll = Scroll::Scrolled`.
331            // TODO(opt): handle sub-tree SET_RECT and RESIZE.
332            // NOTE: our draw system is incompatible with partial redraws, and
333            // in any case redrawing is extremely fast.
334
335            cx.action |= action;
336        }
337        inner(self, id.has_id(), action)
338    }
339
340    /// Pass an [action](Self::action) given some `id`
341    #[inline]
342    pub(crate) fn opt_action(&mut self, id: Option<Id>, action: Action) {
343        if let Some(id) = id {
344            self.action(id, action);
345        }
346    }
347
348    /// Notify that an [`Action`] should happen for the whole window
349    ///
350    /// Using [`Self::action`] with a widget `id` instead of this method is
351    /// potentially more efficient (supports future optimisations), but not
352    /// always possible.
353    #[inline]
354    pub fn window_action(&mut self, action: Action) {
355        self.action |= action;
356    }
357
358    /// Request update to widget `id`
359    ///
360    /// This will call [`Events::update`] on `id`.
361    pub fn request_update(&mut self, id: Id) {
362        self.pending_update = if let Some(id2) = self.pending_update.take() {
363            Some(id.common_ancestor(&id2))
364        } else {
365            Some(id)
366        };
367    }
368}
369
370/// Event handling context
371///
372/// `EventCx` and [`EventState`] (available via [`Deref`]) support various
373/// event management and event-handling state querying operations.
374#[must_use]
375pub struct EventCx<'a> {
376    state: &'a mut EventState,
377    runner: &'a mut dyn RunnerT,
378    window: &'a dyn WindowDataErased,
379    pub(crate) target_is_disabled: bool,
380    last_child: Option<usize>,
381    scroll: Scroll,
382}
383
384impl<'a> Deref for EventCx<'a> {
385    type Target = EventState;
386    fn deref(&self) -> &Self::Target {
387        self.state
388    }
389}
390impl<'a> DerefMut for EventCx<'a> {
391    fn deref_mut(&mut self) -> &mut Self::Target {
392        self.state
393    }
394}
395
396impl<'a> EventCx<'a> {
397    /// Configure a widget
398    ///
399    /// This is a shortcut to [`ConfigCx::configure`].
400    #[inline]
401    pub fn configure(&mut self, mut widget: Node<'_>, id: Id) {
402        widget._configure(&mut self.config_cx(), id);
403    }
404
405    /// Update a widget
406    ///
407    /// [`Events::update`] will be called recursively on each child and finally
408    /// `self`. If a widget stores state which it passes to children as input
409    /// data, it should call this (or [`ConfigCx::update`]) after mutating the state.
410    #[inline]
411    pub fn update(&mut self, mut widget: Node<'_>) {
412        widget._update(&mut self.config_cx());
413    }
414
415    /// Get a [`SizeCx`]
416    ///
417    /// Warning: sizes are calculated using the window's current scale factor.
418    /// This may change, even without user action, since some platforms
419    /// always initialize windows with scale factor 1.
420    /// See also notes on [`Events::configure`].
421    pub fn size_cx(&self) -> SizeCx<'_> {
422        SizeCx::new(self.window.theme_size())
423    }
424
425    /// Get a [`ConfigCx`]
426    pub fn config_cx(&mut self) -> ConfigCx<'_> {
427        let size = self.window.theme_size();
428        ConfigCx::new(size, self.state)
429    }
430
431    /// Get a [`DrawShared`]
432    pub fn draw_shared(&mut self) -> &mut dyn DrawShared {
433        self.runner.draw_shared()
434    }
435}