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}