Source code for simvx.editor.modal_dialog

"""Shared modal-dialog scaffolding for the SimVX editor.

The editor hosts a family of small modal dialogs (About, Preferences, Duplicate
Node, Rename Class, …) that all wire up the engine's modal-router contract in
exactly the same way: ``modal=True``, outside-click dismissal, a ``top_level``
overlay that does not pause the tree, the ``cancel_requested`` router signal, and
``show_modal()`` / ``close_modal()`` to drive the modal stack.

:class:`BaseModalDialog` centralises that boilerplate so each dialog only
declares its own geometry, contents, and result signals. It is a thin
:class:`~simvx.core.Panel` subclass: the panel background starts fully
transparent (so a dialog that paints itself via ``on_draw`` or an inner panel is
unaffected), and the modal router injects the dimmed backdrop.

Subclasses typically:

* set ``z_index`` (overlay stacking) and, for full-rect overlays,
  ``set_anchor_preset(AnchorPreset.FULL_RECT)``;
* call :meth:`open_modal` to show and :meth:`dismiss` to hide;
* override :meth:`_on_cancel` when an outside click / Escape should emit a
  ``cancelled`` result signal (the default just hides the dialog).
"""

from __future__ import annotations

from simvx.core import Panel

__all__ = ["BaseModalDialog"]


[docs] class BaseModalDialog(Panel): """Base class for the editor's modal-router dialogs. Centralises the modal-router setup (``modal``, ``dismiss_on_outside_click``, ``pause_tree_when_modal``, ``top_level``, the ``cancel_requested`` wiring) and the show/hide lifecycle (:meth:`open_modal` / :meth:`dismiss`). The panel background is transparent by default; subclasses paint their own card via ``on_draw`` or an inner :class:`~simvx.core.Panel`. ``pause_tree_when_modal`` defaults to ``False`` (the editor stays live behind the dialog). Set :attr:`PAUSE_TREE_WHEN_MODAL` on the subclass to opt into a dimmed, paused backdrop instead. """ #: Whether the running scene tree pauses while this dialog is open. Overlay #: dialogs that want the engine's dimmed backdrop set this to ``True``. PAUSE_TREE_WHEN_MODAL = False def __init__(self, **kwargs): super().__init__(**kwargs) # Transparent panel background: subclasses draw their own card. self.bg_colour = (0.0, 0.0, 0.0, 0.0) self.border_width = 0 self.modal = True self.dismiss_on_outside_click = True self.pause_tree_when_modal = self.PAUSE_TREE_WHEN_MODAL self.top_level = True self.visible = False self.cancel_requested.connect(self._on_router_cancel) # --------------------------------------------------------------- lifecycle
[docs] def open_modal(self) -> None: """Show the dialog and capture UI input via the modal router.""" self.show_modal()
[docs] def dismiss(self) -> None: """Hide the dialog without emitting a result signal. Idempotent.""" if not self.visible: return self.close_modal()
# ----------------------------------------------------------------- router def _on_router_cancel(self) -> None: """Router fired ``cancel_requested`` (Escape / outside click).""" if self.visible: self._on_cancel() def _on_cancel(self) -> None: """Handle Escape / outside-click / Cancel-button dismissal. Defaults to a plain :meth:`dismiss`. Override to additionally emit a ``cancelled`` result signal. """ self.dismiss()