simvx.core.ui.marks

One canonical answer to: which visible controls are interactive, and where are they on screen.

A single pure-data pass over a scene tree that returns a :class:ControlMark per visible

class:

~simvx.core.ui.core.Control, carrying its screen-space rect (from get_global_rect) and the interaction flags derived from the very predicates the engine uses at runtime:

  • clickable -> mouse_filter truthy and the rect is non-degenerate (matches UIInputManager._find_control_at_point: a control receives clicks iff it is visible, mouse_filter is truthy, and the point lands inside its rect).

  • focusable -> focus_mode != FocusMode.NONE (matches the focus/tab predicates).

No new member is added to Control: every flag is derived from existing state so there is exactly one source of truth. This module is backend-agnostic (no renderer, no Vulkan, no simvx.ai): the two render adapters that consume it are the in-game DebugOverlay (Draw2D) and the AI set-of-marks pixel overlay.

The tree must be laid out for the current frame before calling this: get_global_rect is per-frame cached keyed on Control._current_frame (bumped by SceneTree.on_update), so a pre-layout call returns stale or zero rects.

Module Contents

Classes

ControlMark

A visible control plus its screen rect and runtime-derived interaction flags.

Functions

enumerate_interactive_controls

Enumerate visible Controls under root as screen-space :class:ControlMark records.

Data

API

simvx.core.ui.marks.__all__

[‘ControlMark’, ‘enumerate_interactive_controls’]

class simvx.core.ui.marks.ControlMark[source]

A visible control plus its screen rect and runtime-derived interaction flags.

control: simvx.core.ui.core.Control

None

rect: tuple[float, float, float, float]

None

path: str

None

type_name: str

None

text: str

None

clickable: bool

None

focusable: bool

None

disabled: bool

None

focused: bool

None

mouse_over: bool

None

property interactive: bool[source]

True when this control can receive mouse clicks or keyboard focus.

simvx.core.ui.marks.enumerate_interactive_controls(root: simvx.core.node.Node, *, interactive_only: bool = False) list[simvx.core.ui.marks.ControlMark][source]

Enumerate visible Controls under root as screen-space :class:ControlMark records.

Single DFS pass over root.walk(include_self=True). A node is included when it is a Control that is visible in the hierarchy and has a non-degenerate rect; degenerate rects (w <= 0 or h <= 0) are skipped silently because that is normal for a 0-sized or anchor/margin-collapsed decorative node (get_rect already warns only on negative size).

With interactive_only=True, records that are neither clickable nor focusable are dropped, leaving only what the mouse and an agent can act on. Note a control is clickable whenever mouse_filter is truthy (the engine default), so decorative containers drop out only when the author opts them out of hit-testing (mouse_filter = False), exactly matching what the runtime hit-test would route a click to.