Skip to main content

teksilo_core/
focus.rs

1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4/// How focus was acquired — used for `:focus-visible` behavior.
5/// Only show focus ring when focus was gained via keyboard, not pointer click.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum FocusOrigin {
8    /// Focus gained via Tab/Shift-Tab keyboard navigation.
9    Keyboard,
10    /// Focus gained via pointer click.
11    Pointer,
12    /// Focus set programmatically by the application.
13    Programmatic,
14}
15
16/// Policy for a focus **traversal scope**, declared via the `FocusScope`
17/// wrapper widget. Controls what Tab / Shift+Tab does when it reaches the
18/// scope's ends.
19///
20/// A scope groups + scopes the `tab_index` numbering of its descendants:
21/// two sibling scopes that both number their children `1, 2, 3` never
22/// interleave — each scope is an independent, ordered unit within its
23/// parent. This is Teksilo's analogue of Flutter `FocusTraversalGroup` /
24/// WPF `KeyboardNavigation.TabNavigation`.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum TraversalScopePolicy {
27    /// Tab flows *out* of the scope at its ends into the enclosing scope's
28    /// next member. The scope groups `tab_index` numbering without trapping
29    /// focus — use for logical regions in a continuous Tab order (e.g. dock
30    /// panels, where each panel numbers its own controls without colliding
31    /// with sibling panels).
32    Continue,
33    /// Tab *wraps* within the scope and never exits via keyboard navigation.
34    /// Use for modal dialogs — the one surface whose pattern (ARIA's Dialog
35    /// (Modal)) actually calls for containing focus.
36    ///
37    /// **Not for popovers or menus.** Those implement Disclosure and Menu,
38    /// which mandate the opposite: Tab is an exit gesture there, and the
39    /// framework already answers it by dismissing the overlay focus leaves
40    /// rather than by trapping focus inside it. Wrapping such an overlay in a
41    /// `Cycle` scope defeats that — focus can no longer leave, so the
42    /// dismissal never fires and the panel becomes keyboard-inescapable except
43    /// via Escape.
44    Cycle,
45}