simvx.core.ui.core

Core UI types: Control, Theme, Colour, FocusMode, Anchor, SizeFlags, DragData.

Module Contents

Classes

Control

Base class for all UI elements.

Data

API

simvx.core.ui.core.log

‘getLogger(…)’

simvx.core.ui.core.__all__

[‘Control’, ‘Theme’, ‘ThemeColour’, ‘ThemeSize’, ‘ThemeStyleBox’, ‘Colour’, ‘FocusMode’, ‘Anchor’, ‘…

class simvx.core.ui.core.Control(**kwargs)[source]

Bases: simvx.core.nodes_2d.node2d.Node2D

Base class for all UI elements.

Supports anchors, margins, sizing, focus, drag-and-drop, and input handling. Widgets draw themselves using the renderer passed to draw().

Example: hint = Label(“Hint”, anchor=Anchor.BOTTOM_LEFT, inset=16) panel = Panel(anchor=Anchor.TOP_RIGHT, size=(220, 120), inset=10)

Initialization

size_x

‘Property(…)’

size_y

‘Property(…)’

min_size_x

‘Property(…)’

min_size_y

‘Property(…)’

anchor_left

‘Property(…)’

anchor_top

‘Property(…)’

anchor_right

‘Property(…)’

anchor_bottom

‘Property(…)’

margin_left

‘Property(…)’

margin_top

‘Property(…)’

margin_right

‘Property(…)’

margin_bottom

‘Property(…)’

anchor

‘Property(…)’

inset

‘Property(…)’

dismiss_on_outside_click

‘Property(…)’

clip_contents

‘Property(…)’

focus_mode

‘Property(…)’

tooltip

‘Property(…)’

pointer_filter

‘Property(…)’

cursor_shape

‘Property(…)’

pointer_types: frozenset[simvx.core.input.enums.PointerType] | None

None

touch_target: bool

False

property wants_text_input: bool[source]

Whether this control takes typing while focused.

size_flags_horizontal

‘Property(…)’

size_flags_vertical

‘Property(…)’

stretch_ratio

‘Property(…)’

max_size

‘Property(…)’

property size: simvx.core.math.types.Vec2[source]

Size backed by size_x/size_y Properties (inspector-synced).

property min_size: simvx.core.math.types.Vec2[source]

Minimum size, backed by min_size_x/min_size_y Properties (inspector-synced).

A view, like :attr:size: assign a whole vector to change it. Editing the returned vector in place changes nothing.

content_signature() → object[source]

What :meth:get_minimum_size measures, beyond the font metrics.

Assigning new content resizes a control only where the control says so through :meth:_content_changed, as Label does. A metrics re-fit therefore has to distinguish content it may re-measure from content that has changed underneath it since, because re-measuring the latter would apply a resize nobody asked for, at whatever moment the metrics happened to move. Subclasses whose minimum size depends on their own state return it here.

The default says “nothing that can change”, which is the safe answer: it makes every re-fit re-measure, as it would without this at all.

content_uses(chars: str) → bool[source]

Whether new metrics for chars can change this control’s minimum size.

A control answering False is left alone when only those characters moved, which is what keeps a browser rasterising glyphs a few at a time from re-fitting an entire UI on every frame it does so. Subclasses that size themselves to a known string answer from it.

The default says any character might matter, which is the safe answer.

autosize_to_content() → None[source]

Size this control to fit its content, and keep it fitted.

How wide a string is depends on the font it is drawn with, and a renderer can resolve that font after the scene has been built. Controls sized this way are measured again when that happens, so a button never ends up narrower than the label inside it.

A size set from anywhere else wins: a container that lays this control out, or a game that assigns size, keeps its size, and only the layout is told to run again.

accepts_pointers

‘Property(…)’

property theme: simvx.core.ui.types.Theme | None[source]
property mouse_over: bool[source]
property disabled: bool[source]
get_theme() → simvx.core.ui.types.Theme[source]

Get effective theme (own -> parent -> default).

property viewport_size: simvx.core.math.types.Vec2[source]

The size of the render target this control draws into, in UI pixels.

Attr:

Node.viewport_size, divided by the tree’s ui_scale: a control draws in UI pixels, and a render target that is 1600 pixels wide is 800 of them at ui_scale == 2.0. At 1.0 the two are the same number. A draw that read it depends on ui_scale as well as on the target, and is re-captured when either changes.

property viewport_insets: tuple[float, float, float, float][source]
Attr:

Node.viewport_insets in UI pixels, divided by the tree’s ui_scale as :attr:viewport_size is.

queue_redraw()[source]

Mark this control as needing a redraw. Propagates upward to parent.

Extends :meth:Drawable2D.queue_redraw additively: it sets the item-pipeline render-dirty bit (read by the item RenderItemCache) AND keeps the _draw_dirty/_DrawRecorder cache path (read by the Draw2D _draw_recursive walk the editor play-mode game viewport + web runtime still use).

size_floor() → tuple[float, float][source]

The smallest this control may be, whatever is asking: its declared :attr:min_size.

This is the one statement of that floor: every sizing path reads it rather than restating the rule, because a control is sized down three different routes and a floor written into one of them is absent from the other two.

  • meth:

    get_minimum_size measures content against it, in the base and in every override, so a container laying out children respects it.

  • meth:

    _combined_minimum_size remembers the measurement it bounds.

  • meth:

    get_rect clamps to it, which is the path a control placed with anchors and margins takes.

A finger is not a reason to grow it: a touch target keeps the size its author chose and only its hit area grows (:meth:is_hit).

A tuple rather than a Vec2: :meth:get_rect reads this for every control on every layout pass, and it has no use for an array.

get_minimum_size() → simvx.core.math.types.Vec2[source]

Minimum size needed to display content. Subclasses override.

An override measures its content and returns the larger of that and

Meth:

size_floor on each axis. The base control has no content, so the floor is the whole answer.

add_child(node: simvx.core.node.T) → simvx.core.node.T[source]

Add a child, and forget the measurements it just changed (see :meth:Node.add_child).

get_rect() → tuple[float, float, float, float][source]

Get (x, y, width, height) in parent space.

get_global_rect() → tuple[float, float, float, float][source]

Get (x, y, width, height) in screen space.

The origin is :attr:world_position: the anchor/margin rect is part of the 2D transform chain (:meth:_layout_offset), so there is one walk up the tree and not two, and a control’s drawn position and its reported position cannot disagree.

The extent still comes from :meth:get_rect, which is why _rect_frame stays a field of its own rather than merging into the transform epoch: the two are stamped at different moments, and one field would let a fresh transform certify a stale (width, height).

Cached per layout frame, and auto-expires when the epoch advances.

One class diverges on purpose. While registered as an overlay, SplashScreen and AttributionWatermark (_FullscreenOverlay) override both this and :meth:get_rect to cover the whole render target wherever they are parented, so for them this does NOT equal world_position. The overlay collector draws them from their rect and never reads their world position.

property focus_rect: tuple[float, float, float, float] | None[source]

Where this control is on screen, as directional focus reads it, or None when it is off screen.

The same rect the eye and the pointer use, so the control a player sees above another is the one a press of Up moves to. A control that occupies a different area than it draws in overrides this.

is_point_inside(point) → bool[source]

Check if point (screen coords) is inside this control, moved, rotated and scaled as it is drawn.

is_hit(event: simvx.core.input.events.PointerEvent) → bool[source]

Whether event’s pointer lands on this control.

Its drawn rect, and for a finger on a :attr:touch_target that rect grown evenly about its centre to at least the theme’s min_hit_size on each axis, so a 30-unit button is pressed by a thumb landing a few units off it while it stays 30 units on screen. A mouse and a pen are precise and hit the drawn rect alone. A widget deciding whether a press is its own asks this, which is the same question the hit test asked when it chose the widget.

classmethod __init_subclass__(**kwargs)[source]

Keep placement working under a subclass that redeclares anchor to change its default.

anchor = Property(Anchor.FULL_RECT) in a class body is how a full-screen dialog says where it sits. A redeclared descriptor starts from scratch, so the change hook and the None-accepting coerce are given back to it here rather than asking every such class to restate them.

activate() → bool[source]

Act on this control as a completed click would; return whether it acted.

This is what the ui_accept key (Enter by default) does to the focus owner. The router calls it only after the focused widget has declined the key, so a widget that wants Enter for itself keeps it by returning True – a text field submits, a code editor inserts a newline, and neither is activated.

A plain Control has no activation semantics and returns False. Button fires pressed, CheckBox toggles and RadioButton selects, which is what Godot’s ui_accept and Unity’s ISubmitHandler do for the same widgets. Everything else is deliberately inert.

It is public so activation is one call rather than a synthesised event: button.activate() is exactly what the keyboard does.

The call itself is unconditional: it does not consult disabled or visible, because the caller is the policy. Both engine paths into it apply their own – a click never reaches a disabled control, and the router activates only a focusable owner – so an application calling this directly is saying “do it now”, the way emitting pressed would.

focus_next_control()[source]

Move focus to the next control in tab order.

An explicit focus_next link wins; otherwise the tab order is walked from this control’s position in the tree, wrapping at the end. Hidden and disabled controls are skipped. When this control sits inside an open capturing overlay the walk is confined to that overlay; a control outside one is unaffected by it.

focus_previous_control()[source]

Move focus to the previous control in tab order (see :meth:focus_next_control).

set_drag_preview(control: simvx.core.ui.core.Control)[source]

Set a visual preview control for the current drag operation.

property is_overlay_open: bool[source]

True while this control is registered as an open overlay.

show_overlay(modality: str = 'light', *, dim: bool | None = None, dismiss: bool | None = None, inert: bool | None = None, owner: simvx.core.ui.core.Control | None = None, initial_focus: simvx.core.ui.core.Control | None = None) → None[source]

Register this control as an on-top overlay until :meth:close_overlay.

modality is one of "none" / "light" / "blocking" (a preset bundle of the flags below). dim / dismiss / inert (None = preset default) override individual flags. owner is the logical chain owner for whole-chain dismissal + input scope (e.g. a MenuBar for its dropdown + submenu overlays); defaults to self. initial_focus is the descendant to focus when the overlay is capturing (None = the first focusable descendant). Positioning is the widget’s own job: position first, then call show_overlay to register.

close_overlay() → None[source]

Pop this control (and its open chain) from the overlay registry. Idempotent.

Mirrors :meth:show_overlay, which sets visible = True: closing hides the control again so a persistent (non-freed) overlay – a reused title/menu screen – stops drawing over the scene once dismissed. A consumer that frees its overlay on close is unaffected (the hide is harmless); one that reuses it gets visible = True back on the next show_overlay.

position

‘_SpatialVecProperty(…)’

rotation

‘Property(…)’

scale

‘_SpatialVecProperty(…)’

top_level

‘Property(…)’

z_index

‘Property(…)’

z_as_relative

‘Property(…)’

render_layer

‘Property(…)’

set_render_layer(index: int, enabled: bool = True) → None
is_on_render_layer(index: int) → bool
property absolute_z_index: int
property rotation_degrees: float
property world_position: simvx.core.math.types.Vec2
property world_rotation: float
property world_scale: simvx.core.math.types.Vec2
property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
to_global(point) → simvx.core.math.types.Vec2
to_local(point) → simvx.core.math.types.Vec2
transform_points(points: list[simvx.core.math.types.Vec2]) → list[simvx.core.math.types.Vec2]
wrap_screen(margin: float = 20)
post_processed

‘Property(…)’

property transform_render_dirty: bool
strict_errors: ClassVar[bool]

True

dev_checks: ClassVar[bool]

None

dev_warnings: ClassVar[bool]

None

freeze_check_interval: ClassVar[float]

1.0

dynamic: bool

False

visible

‘Property(…)’

update_mode

‘Property(…)’

drag_threshold

‘Property(…)’

focus_entered

‘Signal(…)’

focus_exited

‘Signal(…)’

__properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]

None

property name: str
property visible_in_tree: bool
property input_player: simvx.core.input.players.InputPlayer
property input_context: simvx.core.input.contexts.InputContext | None
reset_error() → None
remove_child(node: simvx.core.node.Node) → None
reparent(new_parent: simvx.core.node.Node)
node_at(path, default=_NO_DEFAULT)
find(target, *, direct: bool = False)
find_all(target, *, direct: bool = False)
expect(target, *, direct: bool = False)
ancestor(target)
walk(*, include_self: bool = True) → collections.abc.Iterator[simvx.core.node.Node]
property path: str
property is_scene_root: bool
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) → bool
on_ready() → None
on_enter_tree() → None
on_exit_tree() → None
on_update(dt: float) → None
on_fixed_update(dt: float) → None
on_draw(canvas) → None
on_draw_3d(gizmos) → None
on_picked(event: simvx.core.input.events.PickEvent) → bool | None
on_gui_input(event: simvx.core.input.events.GuiInputEvent) → bool | None
start_coroutine(gen: simvx.core.descriptors.Coroutine) → simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
property focused: bool
drag_threshold_for(pointer_type: simvx.core.input.enums.PointerType | str) → float
grab_focus() → None
release_focus() → None
capture_pointer(pointer_id: int | None = None) → None
release_pointer(pointer_id: int | None = None) → None
property render_dirty: bool
clear_children()
destroy()
property destroying: bool
property destroyed: bool
call_deferred(method: collections.abc.Callable[..., Any], *args: Any) → None
property app
property tree: simvx.core.scene_tree.SceneTree
property physics
property physics_2d
__getitem__(key: str)
classmethod get_properties() → dict[str, simvx.core.descriptors.Property]
__repr__()