Source code for simvx.editor.panels.anchor_preset_widget

"""Inspector anchor-preset widget — toggle button that reveals a 3x3 preset grid."""

from collections.abc import Callable

from simvx.core.descriptors import Signal
from simvx.core.math.types import Vec2
from simvx.core.ui.containers import GridContainer, HBoxContainer, VBoxContainer
from simvx.core.ui.core import Control
from simvx.core.ui.enums import AnchorPreset
from simvx.core.ui.widgets import Button

__all__ = ["AnchorPresetButton", "PRESET_LABELS"]

PRESET_LABELS: list[tuple[AnchorPreset, str]] = [
    (AnchorPreset.TOP_LEFT,      "\u2196"),  # ↖
    (AnchorPreset.CENTER_TOP,    "\u2191"),  # ↑
    (AnchorPreset.TOP_RIGHT,     "\u2197"),  # ↗
    (AnchorPreset.CENTER_LEFT,   "\u2190"),  # ←
    (AnchorPreset.CENTER,        "\u25cf"),  # ●
    (AnchorPreset.CENTER_RIGHT,  "\u2192"),  # →
    (AnchorPreset.BOTTOM_LEFT,   "\u2199"),  # ↙
    (AnchorPreset.CENTER_BOTTOM, "\u2193"),  # ↓
    (AnchorPreset.BOTTOM_RIGHT,  "\u2198"),  # ↘
]

_WIDE_PRESETS: list[tuple[AnchorPreset, str]] = [
    (AnchorPreset.LEFT_WIDE,   "Left"),
    (AnchorPreset.RIGHT_WIDE,  "Right"),
    (AnchorPreset.TOP_WIDE,    "Top"),
    (AnchorPreset.BOTTOM_WIDE, "Bot"),
]

[docs] class AnchorPresetButton(VBoxContainer): """Compact grid of anchor-preset selectors shown inline in the Inspector. Renders as a small header label plus a 3×3 directional grid and a row of wide/full variants. Clicking any preset calls ``target_control.set_anchor_preset(preset)`` and emits ``preset_changed``. Inline grid (not a modal popup) so the surrounding PropertyRow sizes correctly and so the affordance stays discoverable. """ preset_changed = Signal() # emits AnchorPreset def __init__(self, target_control: Control | None = None, on_applied: Callable[[AnchorPreset], None] | None = None, **kwargs): super().__init__(**kwargs) self.target_control = target_control self._on_applied = on_applied self._preset_buttons: dict[AnchorPreset, Button] = {} self.separation = 3 grid = GridContainer(columns=3) grid.separation = 2 for preset, label in PRESET_LABELS: btn = Button(label, on_press=lambda p=preset: self._apply_preset(p)) btn.size = Vec2(30, 26) self._preset_buttons[preset] = btn grid.add_child(btn) self.add_child(grid) wide_row = HBoxContainer() wide_row.separation = 2 for preset, label in _WIDE_PRESETS: btn = Button(label, on_press=lambda p=preset: self._apply_preset(p)) btn.size = Vec2(44, 20) self._preset_buttons[preset] = btn wide_row.add_child(btn) self.add_child(wide_row) full = Button("Full Rect", on_press=lambda: self._apply_preset(AnchorPreset.FULL_RECT)) full.size = Vec2(100, 20) self._preset_buttons[AnchorPreset.FULL_RECT] = full self.add_child(full) # Height = 3 rows of 3x3 (~26) + separation + wide row (~20) + sep + full (~20) self.size = Vec2(120, 26 * 3 + 2 * 2 + self.separation + 20 + self.separation + 20) def _apply_preset(self, preset: AnchorPreset) -> None: if self.target_control is not None: self.target_control.set_anchor_preset(preset) self.preset_changed.emit(preset) if self._on_applied is not None: self._on_applied(preset)
[docs] def get_minimum_size(self) -> Vec2: return Vec2(120, 26 * 3 + 2 * 2 + self.separation + 20 + self.separation + 20)