simvx.core.ui.focus

The focus model: one authority for who can hold focus and who is in the tab order.

focus_mode is that authority, on two independent axes:

  • Can it hold focus? focus_mode != FocusMode.NONE – :func:is_focusable.

  • Is it in the tab order? focus_mode == FocusMode.ALL – :func:is_tab_stop.

Both axes additionally require the control to be enabled and effectively visible (tested through the whole ancestor chain via Node._visible_in_hierarchy, not just the control’s own flag): a control the player cannot see or use is not one the keyboard can reach. A CLICK control can therefore hold focus without ever being a Tab destination, which is the web platform’s tabindex="-1".

func:

next_focus is the single traversal routine. Everything that moves focus by keyboard goes through it: the UI router’s Tab handling, Control.focus_next_control and its reverse. It walks the scope in pre-order and resolves the successor from the focus owner’s POSITION in that walk, so an owner that is not itself a tab stop (a clicked Button) still hands over to its neighbour rather than restarting at the first control.

Module Contents

Functions

is_focusable

True when control may hold keyboard focus (visible, enabled, focus_mode != NONE).

is_tab_stop

True when control is a Tab destination (visible, enabled, focus_mode == ALL).

tab_stops

The Tab order inside scope_root: its tab stops in pre-order.

first_tab_stop

First tab stop inside scope_root in pre-order, ignoring skip.

next_focus

The control Tab (or Shift+Tab, with reverse) should focus next inside scope_root.

scope_root_for

The traversal scope for control: the capturing overlay chain it sits in, else the root.

Data

API

simvx.core.ui.focus.__all__

[‘first_tab_stop’, ‘is_focusable’, ‘is_tab_stop’, ‘next_focus’, ‘tab_stops’]

simvx.core.ui.focus.is_focusable(control: simvx.core.ui.core.Control) bool[source]

True when control may hold keyboard focus (visible, enabled, focus_mode != NONE).

simvx.core.ui.focus.is_tab_stop(control: simvx.core.ui.core.Control) bool[source]

True when control is a Tab destination (visible, enabled, focus_mode == ALL).

simvx.core.ui.focus.tab_stops(scope_root: simvx.core.node.Node) list[simvx.core.ui.core.Control][source]

The Tab order inside scope_root: its tab stops in pre-order.

simvx.core.ui.focus.first_tab_stop(scope_root: simvx.core.node.Node, *, skip: Any = None) simvx.core.ui.core.Control | None[source]

First tab stop inside scope_root in pre-order, ignoring skip.

simvx.core.ui.focus.next_focus(scope_root: simvx.core.node.Node, current: simvx.core.ui.core.Control | None, *, reverse: bool = False) simvx.core.ui.core.Control | None[source]

The control Tab (or Shift+Tab, with reverse) should focus next inside scope_root.

current is the focus owner, or None. An explicit focus_next / focus_previous link on the owner wins when it points at a focusable control inside the scope; a link to a hidden, disabled or NONE-mode control falls through to the walk rather than focusing something unreachable.

Otherwise the scope is walked in pre-order and the search starts from the owner’s position in that walk, wrapping at the ends. An owner absent from the walk (a different scope, or None) starts at the first tab stop, or the last one under reverse. Returns None when the scope holds no tab stop at all.

simvx.core.ui.focus.scope_root_for(control: simvx.core.ui.core.Control) simvx.core.node.Node[source]

The traversal scope for control: the capturing overlay chain it sits in, else the root.

Keyboard traversal never leaves an open capturing overlay, so a dialog’s Tab order is the dialog. Outside one (or for a control beneath one), the scope is the whole tree.