tessera_ui/focus_state.rs
1//! # Focus State Management
2//!
3//! This module provides focus state management for the Tessera UI framework.
4//!
5//! ## Overview
6//!
7//! Focus is a common requirement in UI frameworks. As a functional UI framework,
8//! `tessera` lacks a stable method for locating specific components by reference.
9//! Treating focus as an independent, shared state aligns better with `tessera`'s
10//! design philosophy and provides greater flexibility.
11//!
12//! ## Design Philosophy
13//!
14//! The focus system is designed around the following principles:
15//! - **Decentralized**: Each component can create its own [`Focus`] instance
16//! - **Thread-safe**: Focus state is managed using atomic operations and locks
17//! - **Automatic cleanup**: Focus is automatically cleared when a [`Focus`] instance is dropped
18//! - **Unique identification**: Each focus instance has a unique UUID to prevent conflicts
19//!
20//! ## Usage
21//!
22//! ```
23//! use tessera_ui::Focus;
24//!
25//! // Create a new focus instance
26//! let focus = Focus::new();
27//!
28//! // Check if this focus is currently active
29//! if focus.is_focused() {
30//! // Handle focused state
31//! }
32//!
33//! // Request focus for this component
34//! focus.request_focus();
35//!
36//! // Clear focus when no longer needed
37//! focus.unfocus();
38//! ```
39//!
40//! ## Thread Safety
41//!
42//! The focus state is managed through a global static variable protected by
43//! read-write locks, making it safe to use across multiple threads. This is
44//! essential for Tessera's parallelized design.
45
46use std::sync::OnceLock;
47
48use parking_lot::{RwLock, RwLockReadGuard};
49use uuid::Uuid;
50
51/// Global focus state storage.
52///
53/// This static variable holds the shared focus state across the entire application.
54/// It's initialized lazily on first access and protected by a read-write lock
55/// for thread-safe access.
56static FOCUS_STATE: OnceLock<RwLock<FocusState>> = OnceLock::new();
57
58/// Internal focus state representation.
59///
60/// This structure holds the current focus state, tracking which component
61/// (if any) currently has focus through its unique identifier.
62#[derive(Default)]
63struct FocusState {
64 /// The UUID of the currently focused component, or `None` if no component has focus.
65 focused: Option<Uuid>,
66}
67
68/// Acquires a read lock on the global focus state.
69///
70/// This function provides thread-safe read access to the focus state.
71/// Multiple readers can access the state simultaneously, but writers
72/// will be blocked until all readers are finished.
73///
74/// # Returns
75///
76/// A read guard that provides access to the focus state. The guard
77/// automatically releases the lock when dropped.
78fn read_focus_state() -> RwLockReadGuard<'static, FocusState> {
79 FOCUS_STATE
80 .get_or_init(|| RwLock::new(FocusState::default()))
81 .read()
82}
83
84/// Acquires a write lock on the global focus state.
85///
86/// This function provides thread-safe write access to the focus state.
87/// Only one writer can access the state at a time, and all readers
88/// will be blocked until the writer is finished.
89///
90/// # Returns
91///
92/// A write guard that provides mutable access to the focus state.
93/// The guard automatically releases the lock when dropped.
94fn write_focus_state() -> parking_lot::RwLockWriteGuard<'static, FocusState> {
95 FOCUS_STATE
96 .get_or_init(|| RwLock::new(FocusState::default()))
97 .write()
98}
99
100/// A focus handle that represents a focusable component.
101///
102/// Each `Focus` instance has a unique identifier and can be used to manage
103/// focus state for a specific component. The focus system ensures that only
104/// one component can have focus at a time across the entire application.
105///
106/// # Examples
107///
108/// ```
109/// use tessera_ui::Focus;
110///
111/// // Create a focus instance for a component
112/// let button_focus = Focus::new();
113///
114/// // Request focus for this component
115/// button_focus.request_focus();
116///
117/// // Check if this component currently has focus
118/// if button_focus.is_focused() {
119/// println!("Button is focused!");
120/// }
121///
122/// // Focus is automatically cleared when the instance is dropped
123/// drop(button_focus);
124/// ```
125pub struct Focus {
126 /// Unique identifier for this focus instance.
127 ///
128 /// This UUID is used to distinguish between different focus instances
129 /// and ensure that focus operations are applied to the correct component.
130 id: Uuid,
131}
132
133impl Default for Focus {
134 /// Creates a new focus instance with a unique identifier.
135 ///
136 /// This is equivalent to calling [`Focus::new()`].
137 fn default() -> Self {
138 Self::new()
139 }
140}
141
142impl Focus {
143 /// Creates a new focus instance with a unique identifier.
144 ///
145 /// Each focus instance is assigned a unique UUID that distinguishes it
146 /// from all other focus instances in the application. This ensures that
147 /// focus operations are applied to the correct component.
148 ///
149 /// # Examples
150 ///
151 /// ```
152 /// use tessera_ui::Focus;
153 ///
154 /// let focus1 = Focus::new();
155 /// let focus2 = Focus::new();
156 ///
157 /// // There can only be one focused component at a time
158 ///
159 /// focus1.request_focus();
160 /// assert!(focus1.is_focused());
161 /// assert!(!focus2.is_focused());
162 ///
163 /// focus2.request_focus();
164 /// assert!(!focus1.is_focused());
165 /// assert!(focus2.is_focused());
166 /// ```
167 pub fn new() -> Self {
168 Focus { id: Uuid::new_v4() }
169 }
170
171 /// Checks if this focus instance currently has focus.
172 ///
173 /// Returns `true` if this specific focus instance is the currently
174 /// active focus in the global focus state, `false` otherwise.
175 ///
176 /// # Examples
177 ///
178 /// ```
179 /// use tessera_ui::Focus;
180 ///
181 /// let focus = Focus::new();
182 ///
183 /// // Initially, no focus is active
184 /// assert!(!focus.is_focused());
185 ///
186 /// // After requesting focus, this instance should be focused
187 /// focus.request_focus();
188 /// assert!(focus.is_focused());
189 /// ```
190 ///
191 /// # Thread Safety
192 ///
193 /// This method is thread-safe and can be called from any thread.
194 /// It acquires a read lock on the global focus state.
195 pub fn is_focused(&self) -> bool {
196 let focus_state = read_focus_state();
197 focus_state.focused == Some(self.id)
198 }
199
200 /// Requests focus for this component.
201 ///
202 /// This method sets the global focus state to this focus instance,
203 /// potentially removing focus from any previously focused component.
204 /// Only one component can have focus at a time.
205 ///
206 /// # Examples
207 ///
208 /// ```
209 /// use tessera_ui::Focus;
210 ///
211 /// let focus1 = Focus::new();
212 /// let focus2 = Focus::new();
213 ///
214 /// focus1.request_focus();
215 /// assert!(focus1.is_focused());
216 /// assert!(!focus2.is_focused());
217 ///
218 /// // Requesting focus for focus2 removes it from focus1
219 /// focus2.request_focus();
220 /// assert!(!focus1.is_focused());
221 /// assert!(focus2.is_focused());
222 /// ```
223 ///
224 /// # Thread Safety
225 ///
226 /// This method is thread-safe and can be called from any thread.
227 /// It acquires a write lock on the global focus state.
228 pub fn request_focus(&self) {
229 let mut focus_state = write_focus_state();
230 focus_state.focused = Some(self.id);
231 }
232
233 /// Clears focus if this instance currently has it.
234 ///
235 /// This method removes focus from the global state, but only if this
236 /// specific focus instance is the one that currently has focus.
237 /// If another component has focus, this method has no effect.
238 ///
239 /// # Examples
240 ///
241 /// ```
242 /// use tessera_ui::Focus;
243 ///
244 /// let focus1 = Focus::new();
245 /// let focus2 = Focus::new();
246 ///
247 /// focus1.request_focus();
248 /// assert!(focus1.is_focused());
249 ///
250 /// // Clear focus from focus1
251 /// focus1.unfocus();
252 /// assert!(!focus1.is_focused());
253 ///
254 /// // If focus2 has focus, focus1.unfocus() won't affect it
255 /// focus2.request_focus();
256 /// focus1.unfocus(); // No effect
257 /// assert!(focus2.is_focused());
258 /// ```
259 ///
260 /// # Thread Safety
261 ///
262 /// This method is thread-safe and can be called from any thread.
263 /// It acquires a write lock on the global focus state.
264 pub fn unfocus(&self) {
265 let mut focus_state = write_focus_state();
266 if focus_state.focused == Some(self.id) {
267 focus_state.focused = None;
268 }
269 }
270}
271
272impl Drop for Focus {
273 /// Automatically clears focus when the `Focus` instance is dropped.
274 ///
275 /// This ensures that focus is properly cleaned up when a component
276 /// is destroyed or goes out of scope. If this focus instance currently
277 /// has focus, it will be cleared from the global focus state.
278 ///
279 /// # Examples
280 ///
281 /// ```
282 /// use tessera_ui::Focus;
283 ///
284 /// {
285 /// let focus = Focus::new();
286 /// focus.request_focus();
287 /// assert!(focus.is_focused());
288 /// } // focus is dropped here, automatically clearing focus
289 ///
290 /// // Focus is now cleared globally
291 /// let another_focus = Focus::new();
292 /// assert!(!another_focus.is_focused());
293 /// ```
294 ///
295 /// # Thread Safety
296 ///
297 /// This method is thread-safe and will properly handle cleanup
298 /// even if called from different threads.
299 fn drop(&mut self) {
300 self.unfocus(); // Ensure focus is cleared when the Focus instance is dropped
301 }
302}