ai-station/.venv/lib/python3.12/site-packages/textual/widgets/_content_switcher.py

133 lines
4.2 KiB
Python

"""Provides a widget for switching between the display of its immediate children."""
from __future__ import annotations
from typing import Optional
from textual.await_complete import AwaitComplete
from textual.containers import Container
from textual.css.query import NoMatches
from textual.events import Mount
from textual.reactive import reactive
from textual.widget import Widget
class ContentSwitcher(Container):
"""A widget for switching between different children.
Note:
All child widgets that are to be switched between need a unique ID.
Children that have no ID will be hidden and ignored.
"""
DEFAULT_CSS = """
ContentSwitcher {
height: auto;
}
"""
current: reactive[str | None] = reactive[Optional[str]](None, init=False)
"""The ID of the currently-displayed widget.
If set to `None` then no widget is visible.
Note:
If set to an unknown ID, this will result in
[`NoMatches`][textual.css.query.NoMatches] being raised.
"""
def __init__(
self,
*children: Widget,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
initial: str | None = None,
) -> None:
"""Initialise the content switching widget.
Args:
*children: The widgets to switch between.
name: The name of the content switcher.
id: The ID of the content switcher in the DOM.
classes: The CSS classes of the content switcher.
disabled: Whether the content switcher is disabled or not.
initial: The ID of the initial widget to show, ``None`` or empty string for the first tab.
Note:
If `initial` is not supplied no children will be shown to start with.
"""
super().__init__(
*children,
name=name,
id=id,
classes=classes,
disabled=disabled,
)
self._initial = initial
def _on_mount(self, _: Mount) -> None:
"""Perform the initial setup of the widget once the DOM is ready."""
initial = self._initial
with self.app.batch_update():
for child in self.children:
child.display = bool(initial) and child.id == initial
self._reactive_current = initial
@property
def visible_content(self) -> Widget | None:
"""A reference to the currently-visible widget.
`None` if nothing is visible.
"""
return self.get_child_by_id(self.current) if self.current is not None else None
def watch_current(self, old: str | None, new: str | None) -> None:
"""React to the current visible child choice being changed.
Args:
old: The old widget ID (or `None` if there was no widget).
new: The new widget ID (or `None` if nothing should be shown).
"""
with self.app.batch_update():
if old:
try:
self.get_child_by_id(old).display = False
except NoMatches:
pass
if new:
self.get_child_by_id(new).display = True
def add_content(
self, widget: Widget, *, id: str | None = None, set_current: bool = False
) -> AwaitComplete:
"""Add new content to the `ContentSwitcher`.
Args:
widget: A Widget to add.
id: ID for the widget, or `None` if the widget already has an ID.
set_current: Set the new widget as current (which will cause it to display).
Returns:
An awaitable to wait for the new content to be mounted.
"""
if id is not None and widget.id != id:
widget.id = id
if not widget.id:
raise ValueError(
"Widget must have an ID (or set id parameter when calling add_content)"
)
async def _add_content() -> None:
"""Add new widget and potentially change the current widget."""
widget.display = False
with self.app.batch_update():
await self.mount(widget)
if set_current:
self.current = widget.id
return AwaitComplete(_add_content())