kas_core/config/
event.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 handling configuration
7
8use crate::Action;
9use crate::cast::Cast;
10#[allow(unused)] use crate::event::Event;
11use crate::event::ModifiersState;
12use crate::geom::Vec2;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15use std::cell::Ref;
16use std::time::Duration;
17
18/// A message which may be used to update [`EventConfig`]
19#[derive(Clone, Debug)]
20#[non_exhaustive]
21pub enum EventConfigMsg {
22    HoverDelay(u32),
23    MenuDelay(u32),
24    TouchSelectDelay(u32),
25    KineticTimeout(u32),
26    KineticDecayMul(f32),
27    KineticDecaySub(f32),
28    KineticGrabSub(f32),
29    ScrollDistEm(f32),
30    PanDistThresh(f32),
31    MousePan(MousePan),
32    MouseTextPan(MousePan),
33    MouseWheelActions(bool),
34    MouseNavFocus(bool),
35    TouchNavFocus(bool),
36    /// Reset all config values to default (not saved) values
37    ResetToDefault,
38}
39
40/// Event handling configuration
41///
42/// This is serializable (using `feature = "serde"`) with the following fields:
43///
44/// > `menu_delay_ms`: `u32` (milliseconds) \
45/// > `touch_select_delay_ms`: `u32` (milliseconds) \
46/// > `kinetic_timeout_ms`: `u32` (milliseconds) \
47/// > `kinetic_decay_mul`: `f32` (unitless, applied each second) \
48/// > `kinetic_decay_sub`: `f32` (pixels per second) \
49/// > `kinetic_grab_sub`: `f32` (pixels per second) \
50/// > `pan_dist_thresh`: `f32` (pixels) \
51/// > `mouse_pan`: [`MousePan`] \
52/// > `mouse_text_pan`: [`MousePan`] \
53/// > `mouse_nav_focus`: `bool` \
54/// > `touch_nav_focus`: `bool`
55///
56/// For descriptions of configuration effects, see [`EventWindowConfig`] methods.
57#[derive(Clone, Debug, PartialEq)]
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59pub struct EventConfig {
60    #[cfg_attr(feature = "serde", serde(default = "defaults::hover_delay_ms"))]
61    pub hover_delay_ms: u32,
62
63    #[cfg_attr(feature = "serde", serde(default = "defaults::menu_delay_ms"))]
64    pub menu_delay_ms: u32,
65
66    #[cfg_attr(feature = "serde", serde(default = "defaults::touch_select_delay_ms"))]
67    pub touch_select_delay_ms: u32,
68
69    #[cfg_attr(feature = "serde", serde(default = "defaults::kinetic_timeout_ms"))]
70    pub kinetic_timeout_ms: u32,
71
72    #[cfg_attr(feature = "serde", serde(default = "defaults::kinetic_decay_mul"))]
73    pub kinetic_decay_mul: f32,
74
75    #[cfg_attr(feature = "serde", serde(default = "defaults::kinetic_decay_sub"))]
76    pub kinetic_decay_sub: f32,
77
78    #[cfg_attr(feature = "serde", serde(default = "defaults::kinetic_grab_sub"))]
79    pub kinetic_grab_sub: f32,
80
81    #[cfg_attr(feature = "serde", serde(default = "defaults::scroll_dist_em"))]
82    pub scroll_dist_em: f32,
83
84    #[cfg_attr(feature = "serde", serde(default = "defaults::pan_dist_thresh"))]
85    pub pan_dist_thresh: f32,
86
87    #[cfg_attr(feature = "serde", serde(default = "defaults::mouse_pan"))]
88    pub mouse_pan: MousePan,
89    #[cfg_attr(feature = "serde", serde(default = "defaults::mouse_text_pan"))]
90    pub mouse_text_pan: MousePan,
91
92    #[cfg_attr(feature = "serde", serde(default = "defaults::mouse_wheel_actions"))]
93    pub mouse_wheel_actions: bool,
94
95    #[cfg_attr(feature = "serde", serde(default = "defaults::mouse_nav_focus"))]
96    pub mouse_nav_focus: bool,
97    #[cfg_attr(feature = "serde", serde(default = "defaults::touch_nav_focus"))]
98    pub touch_nav_focus: bool,
99}
100
101impl Default for EventConfig {
102    fn default() -> Self {
103        EventConfig {
104            hover_delay_ms: defaults::hover_delay_ms(),
105            menu_delay_ms: defaults::menu_delay_ms(),
106            touch_select_delay_ms: defaults::touch_select_delay_ms(),
107            kinetic_timeout_ms: defaults::kinetic_timeout_ms(),
108            kinetic_decay_mul: defaults::kinetic_decay_mul(),
109            kinetic_decay_sub: defaults::kinetic_decay_sub(),
110            kinetic_grab_sub: defaults::kinetic_grab_sub(),
111            scroll_dist_em: defaults::scroll_dist_em(),
112            pan_dist_thresh: defaults::pan_dist_thresh(),
113            mouse_pan: defaults::mouse_pan(),
114            mouse_text_pan: defaults::mouse_text_pan(),
115            mouse_wheel_actions: defaults::mouse_wheel_actions(),
116            mouse_nav_focus: defaults::mouse_nav_focus(),
117            touch_nav_focus: defaults::touch_nav_focus(),
118        }
119    }
120}
121
122impl EventConfig {
123    pub(super) fn change_config(&mut self, msg: EventConfigMsg) -> Action {
124        match msg {
125            EventConfigMsg::HoverDelay(v) => self.hover_delay_ms = v,
126            EventConfigMsg::MenuDelay(v) => self.menu_delay_ms = v,
127            EventConfigMsg::TouchSelectDelay(v) => self.touch_select_delay_ms = v,
128            EventConfigMsg::KineticTimeout(v) => self.kinetic_timeout_ms = v,
129            EventConfigMsg::KineticDecayMul(v) => self.kinetic_decay_mul = v,
130            EventConfigMsg::KineticDecaySub(v) => self.kinetic_decay_sub = v,
131            EventConfigMsg::KineticGrabSub(v) => self.kinetic_grab_sub = v,
132            EventConfigMsg::ScrollDistEm(v) => self.scroll_dist_em = v,
133            EventConfigMsg::PanDistThresh(v) => self.pan_dist_thresh = v,
134            EventConfigMsg::MousePan(v) => self.mouse_pan = v,
135            EventConfigMsg::MouseTextPan(v) => self.mouse_text_pan = v,
136            EventConfigMsg::MouseWheelActions(v) => self.mouse_wheel_actions = v,
137            EventConfigMsg::MouseNavFocus(v) => self.mouse_nav_focus = v,
138            EventConfigMsg::TouchNavFocus(v) => self.touch_nav_focus = v,
139            EventConfigMsg::ResetToDefault => *self = EventConfig::default(),
140        }
141
142        Action::EVENT_CONFIG
143    }
144}
145
146/// Accessor to event configuration
147///
148/// This is a helper to read event configuration, adapted for the current
149/// application and window scale.
150#[derive(Clone, Debug)]
151pub struct EventWindowConfig<'a>(pub(super) &'a super::WindowConfig);
152
153impl<'a> EventWindowConfig<'a> {
154    /// Access base (unscaled) event [`EventConfig`]
155    #[inline]
156    pub fn base(&self) -> Ref<'_, EventConfig> {
157        Ref::map(self.0.config.borrow(), |c| &c.event)
158    }
159
160    /// Delay before mouse hover action (show tooltip)
161    #[inline]
162    pub fn hover_delay(&self) -> Duration {
163        Duration::from_millis(self.base().hover_delay_ms.cast())
164    }
165
166    /// Delay before opening/closing menus on mouse over
167    #[inline]
168    pub fn menu_delay(&self) -> Duration {
169        Duration::from_millis(self.base().menu_delay_ms.cast())
170    }
171
172    /// Delay before switching from panning to (text) selection mode
173    #[inline]
174    pub fn touch_select_delay(&self) -> Duration {
175        Duration::from_millis(self.base().touch_select_delay_ms.cast())
176    }
177
178    /// Controls activation of kinetic scrolling
179    ///
180    /// This is the maximum time between the last press-movement and final
181    /// release to activate kinetic scrolling mode. The last few `PressMove`
182    /// events within this time window are used to calculate the initial speed.
183    #[inline]
184    pub fn kinetic_timeout(&self) -> Duration {
185        Duration::from_millis(self.base().kinetic_timeout_ms.cast())
186    }
187
188    /// Kinetic scrolling decay: `(mul, sub)`
189    ///
190    /// The `mul` factor describes exponential decay: effectively, velocity is
191    /// multiplied by `mul` every second. This is the dominant decay factor at
192    /// high speeds; `mul = 1.0` implies no decay while `mul = 0.0` implies an
193    /// instant stop.
194    ///
195    /// The `sub` factor describes linear decay: effectively, speed is reduced
196    /// by `sub` every second. This is the dominant decay factor at low speeds.
197    /// Units are pixels/second (output is adjusted for the window's scale factor).
198    #[inline]
199    pub fn kinetic_decay(&self) -> (f32, f32) {
200        (self.base().kinetic_decay_mul, self.0.kinetic_decay_sub)
201    }
202
203    /// Kinetic scrolling decay while grabbed: `sub`
204    ///
205    /// When a kinetic-scrolling element is grabbed while in motion, speed
206    /// relative to the grab velocity is reduced by this value each second.
207    /// This is applied in addition to the usual velocity decay (see
208    /// [`Self::kinetic_decay`]).
209    #[inline]
210    pub fn kinetic_grab_sub(&self) -> f32 {
211        self.base().kinetic_grab_sub
212    }
213
214    /// Get distance in pixels to scroll due to mouse wheel
215    ///
216    /// Calculates scroll distance from `(horiz, vert)` lines.
217    pub fn scroll_distance(&self, lines: (f32, f32)) -> Vec2 {
218        let x = self.0.scroll_dist * lines.0;
219        let y = self.0.scroll_dist * lines.1;
220        Vec2(x, y)
221    }
222
223    /// Drag distance threshold before panning (scrolling) starts
224    ///
225    /// When the distance moved is greater than this threshold, panning should
226    /// start; otherwise the system should wait for the text-selection timer.
227    /// We currently recommend the L-inf distance metric (max of abs of values).
228    ///
229    /// Units are pixels (output is adjusted for the window's scale factor).
230    #[inline]
231    pub fn pan_dist_thresh(&self) -> f32 {
232        self.0.pan_dist_thresh
233    }
234
235    /// Whether the mouse wheel may trigger actions such as switching to the next item in a list
236    #[inline]
237    pub fn mouse_wheel_actions(&self) -> bool {
238        self.base().mouse_wheel_actions
239    }
240
241    /// When to pan general widgets (unhandled events) with the mouse
242    #[inline]
243    pub fn mouse_pan(&self) -> MousePan {
244        self.base().mouse_pan
245    }
246
247    /// When to pan text fields with the mouse
248    #[inline]
249    pub fn mouse_text_pan(&self) -> MousePan {
250        self.base().mouse_text_pan
251    }
252
253    /// Whether mouse clicks set keyboard navigation focus
254    #[inline]
255    pub fn mouse_nav_focus(&self) -> bool {
256        self.0.nav_focus && self.base().mouse_nav_focus
257    }
258
259    /// Whether touchscreen events set keyboard navigation focus
260    #[inline]
261    pub fn touch_nav_focus(&self) -> bool {
262        self.0.nav_focus && self.base().touch_nav_focus
263    }
264}
265
266/// When mouse-panning is enabled (click+drag to scroll)
267///
268/// For *text* objects, this may conflict with text selection, hence it is
269/// recommended to require a modifier or disable this feature.
270///
271/// For non-text cases, this does not conflict with other event handlers since
272/// panning is only possible when events are otherwise unused, thus `Always` is
273/// acceptable (equivalent to touch scrolling).
274#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
275#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
276#[repr(u8)]
277pub enum MousePan {
278    /// Disable
279    Never,
280    /// Only enable when the Alt key is held
281    WithAlt,
282    /// Only enable when the Ctrl key is held
283    WithCtrl,
284    /// Always enabled
285    Always,
286}
287
288impl MousePan {
289    /// Is this enabled with the current modifiers?
290    pub fn is_enabled_with(self, modifiers: ModifiersState) -> bool {
291        match self {
292            MousePan::Never => false,
293            MousePan::WithAlt => modifiers.alt_key(),
294            MousePan::WithCtrl => modifiers.control_key(),
295            MousePan::Always => true,
296        }
297    }
298}
299
300mod defaults {
301    use super::MousePan;
302
303    pub fn hover_delay_ms() -> u32 {
304        1000
305    }
306    pub fn menu_delay_ms() -> u32 {
307        250
308    }
309    pub fn touch_select_delay_ms() -> u32 {
310        1000
311    }
312    pub fn kinetic_timeout_ms() -> u32 {
313        50
314    }
315    pub fn kinetic_decay_mul() -> f32 {
316        0.625
317    }
318    pub fn kinetic_decay_sub() -> f32 {
319        200.0
320    }
321    pub fn kinetic_grab_sub() -> f32 {
322        10000.0
323    }
324    pub fn scroll_dist_em() -> f32 {
325        4.5
326    }
327    pub fn pan_dist_thresh() -> f32 {
328        5.0
329    }
330    pub fn mouse_wheel_actions() -> bool {
331        true
332    }
333    pub fn mouse_pan() -> MousePan {
334        MousePan::Always
335    }
336    pub fn mouse_text_pan() -> MousePan {
337        #[cfg(target_os = "windows")]
338        {
339            MousePan::WithAlt
340        }
341        #[cfg(not(target_os = "windows"))]
342        {
343            MousePan::WithCtrl
344        }
345    }
346    pub fn mouse_nav_focus() -> bool {
347        true
348    }
349    pub fn touch_nav_focus() -> bool {
350        true
351    }
352}