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 any node, on two independent axes:
Can it hold focus?
focus_mode.takes_focus(anything butNONE) – :func:is_focusable.Is it in the tab order?
focus_mode.in_tab_order(ALLorHOVER) – :func:is_tab_stop.
Both axes additionally require the node to be enabled and effectively visible (through the whole
ancestor chain, visible_in_tree): a node the player cannot see or use is not one the keyboard
can reach. A CLICK node can therefore hold focus without ever being a Tab destination, which is
the web platform’s tabindex="-1".
The search is over the tree’s index of nodes whose focus_mode takes focus, kept as they enter,
leave and change mode, so Tab and a D-pad never walk the sprites and the particles between the
buttons.
- func:
next_focusis the single traversal routine. Everything that moves focus by keyboard goes through it: the UI router’s Tab handling,Control.focus_next_controland 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 clickedButton) still hands over to its neighbour rather than restarting at the first control.
Module Contents¶
Functions¶
True when |
|
True when |
|
The Tab order inside |
|
First tab stop inside |
|
The control Tab (or Shift+Tab, with |
|
The control a press of direction should focus inside |
|
The traversal scope for |
Data¶
API¶
- simvx.core.ui.focus.__all__¶
[‘first_tab_stop’, ‘focus_neighbour’, ‘is_focusable’, ‘is_tab_stop’, ‘next_focus’, ‘tab_stops’]
- simvx.core.ui.focus.is_focusable(node: simvx.core.node.Node) bool[source]¶
True when
nodemay hold keyboard focus: visible, enabled, and afocus_modethat takes it.
- simvx.core.ui.focus.is_tab_stop(node: simvx.core.node.Node) bool[source]¶
True when
nodeis a Tab destination: visible, enabled,focus_modeALLorHOVER.
- simvx.core.ui.focus.tab_stops(scope_root: simvx.core.node.Node) list[simvx.core.node.Node][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.node.Node | None[source]¶
First tab stop inside
scope_rootin pre-order, ignoringskip.
- simvx.core.ui.focus.next_focus(scope_root: simvx.core.node.Node, current: simvx.core.node.Node | None, *, reverse: bool = False) simvx.core.node.Node | None[source]¶
The control Tab (or Shift+Tab, with
reverse) should focus next insidescope_root.currentis the focus owner, orNone. An explicitfocus_next/focus_previouslink on the owner wins when it points at a focusable control inside the scope; a link to a hidden, disabled orNONE-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 underreverse. ReturnsNonewhen the scope holds no tab stop at all.
- simvx.core.ui.focus.focus_neighbour(scope_root: simvx.core.node.Node, current: simvx.core.node.Node | None, direction: simvx.core.ui.enums.FocusDirection | str) simvx.core.node.Node | None[source]¶
The control a press of direction should focus inside
scope_root.An explicit
focus_up/focus_down/focus_left/focus_rightlink on the owner wins when it points at a focusable control inside the scope; a link to a hidden, disabled orNONE-mode control falls through to the search rather than focusing something unreachable, exactly as- Func:
next_focustreats its own links.
Otherwise the search is geometric, over the same destinations Tab reaches (:func:
is_tab_stop), using each control’sfocus_rect. A candidate counts only if its centre lies in direction at all; among those, the nearest wins, with a sideways offset charged against it and charged much more heavily when the two rects do not overlap on the perpendicular axis, so Down prefers the control directly below to a nearer one off to the side.With no focus owner the first tab stop is elected, which is what lets a controller player enter a menu that nothing has focused yet. Nothing in that direction returns
Nonerather than wrapping: a wrap makes a directional press jump across the screen, which reads as a bug.
- simvx.core.ui.focus.scope_root_for(control: simvx.core.node.Node) 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.