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}