ailloli_ui_runtime/input/
focus.rs

1//! Keyboard-focus policy and focus-transition helpers.
2
3use ailloli_ui_core::ElementId;
4
5/// Whether an element may become the keyboard-focus owner.
6///
7/// # Examples
8///
9/// ```
10/// use ailloli_ui_runtime::input::FocusPolicy;
11/// assert_eq!(FocusPolicy::default(), FocusPolicy::NotFocusable);
12/// ```
13#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
14pub enum FocusPolicy {
15    /// Pointer and keyboard navigation skip the element; this is the default.
16    #[default]
17    NotFocusable,
18    /// The input router may assign keyboard focus to the element.
19    Focusable,
20}
21
22/// Controls whether a pointer gesture that only activated/focused the host may
23/// also activate a widget.
24///
25/// Policies are resolved from the hit-tested child towards its ancestors. If
26/// no widget chooses an explicit policy, the input router uses the safe
27/// [`ActivationPolicy::SuppressOnFocusOnly`] root fallback.
28///
29/// # Examples
30///
31/// ```
32/// use ailloli_ui_runtime::input::ActivationPolicy;
33/// assert_eq!(ActivationPolicy::default(), ActivationPolicy::Inherit);
34/// ```
35#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
36pub enum ActivationPolicy {
37    /// Defer the decision to the closest ancestor with an explicit policy.
38    #[default]
39    Inherit,
40    /// Preserve focus handling but suppress action activation.
41    SuppressOnFocusOnly,
42    /// Deliver the gesture normally, for example to place a text caret.
43    AllowOnFocusOnly,
44}
45
46/// Semantic keyboard-input role of a retained widget.
47///
48/// The role guides focus, IME, and cursor behavior; it does not itself edit
49/// text or make an element focusable.
50///
51/// # Examples
52///
53/// ```
54/// use ailloli_ui_runtime::input::InputRole;
55/// assert_eq!(InputRole::default(), InputRole::None);
56/// assert_ne!(InputRole::TextSingleLine, InputRole::TextMultiLine);
57/// ```
58#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
59pub enum InputRole {
60    /// No text-input semantics; this is the default.
61    #[default]
62    None,
63    /// Single-line editable text semantics.
64    TextSingleLine,
65    /// Multi-line editable text semantics.
66    TextMultiLine,
67}
68
69/// Cursor role requested while a pointer hovers an element.
70///
71/// `Inherit` delegates to an ancestor; every other value is explicit. Mapping
72/// these provider-neutral roles to platform cursor assets is a host concern.
73///
74/// # Examples
75///
76/// ```
77/// use ailloli_ui_runtime::input::HoverCursorRole;
78/// assert_eq!(HoverCursorRole::default(), HoverCursorRole::Inherit);
79/// assert_ne!(HoverCursorRole::Pointer, HoverCursorRole::Text);
80/// ```
81#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
82pub enum HoverCursorRole {
83    /// Delegate to the closest ancestor with an explicit role; this is default.
84    #[default]
85    Inherit,
86    /// Platform default arrow cursor.
87    Default,
88    /// Link or action pointer cursor.
89    Pointer,
90    /// Text-selection I-beam cursor.
91    Text,
92    /// Horizontal resize cursor.
93    ResizeX,
94    /// Vertical resize cursor.
95    ResizeY,
96}
97
98/// Stores the single keyboard-focus owner for one input router.
99///
100/// This type does not validate whether the ID exists or is focusable; routing
101/// code is responsible for clearing stale IDs and dispatching focus events.
102///
103/// # Examples
104///
105/// ```
106/// use ailloli_ui_core::ElementId;
107/// use ailloli_ui_runtime::input::FocusManager;
108/// let mut focus = FocusManager::default();
109/// focus.set_focused(Some(ElementId(7)));
110/// assert_eq!(focus.focused(), Some(ElementId(7)));
111/// ```
112#[derive(Debug, Default, Clone)]
113pub struct FocusManager {
114    /// Strict current focus owner, without implicit liveness validation.
115    focused: Option<ElementId>,
116}
117
118/// Provides the operations defined for FocusManager.
119impl FocusManager {
120    /// Returns the current focus owner, or `None` when nothing is focused.
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// use ailloli_ui_runtime::input::FocusManager;
126    /// assert_eq!(FocusManager::default().focused(), None);
127    /// ```
128    pub fn focused(&self) -> Option<ElementId> {
129        self.focused
130    }
131
132    /// Replaces the focus owner without dispatching events or invalidating paint.
133    ///
134    /// Passing `None` clears focus. Unknown IDs are stored unchanged.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use ailloli_ui_core::ElementId;
140    /// use ailloli_ui_runtime::input::FocusManager;
141    /// let mut focus = FocusManager::default();
142    /// focus.set_focused(Some(ElementId(2)));
143    /// focus.set_focused(None);
144    /// assert_eq!(focus.focused(), None);
145    /// ```
146    pub fn set_focused(&mut self, id: Option<ElementId>) {
147        self.focused = id;
148    }
149}