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

312 lines
8.5 KiB
Python

"""
Container widgets for quick styling.
With the exception of `Center` and `Middle` containers will fill all of the space in the parent widget.
"""
from __future__ import annotations
from typing import ClassVar
from textual.binding import Binding, BindingType
from textual.layout import Layout
from textual.layouts.grid import GridLayout
from textual.reactive import reactive
from textual.widget import Widget
class Container(Widget):
"""Simple container widget, with vertical layout."""
DEFAULT_CSS = """
Container {
width: 1fr;
height: 1fr;
layout: vertical;
overflow: hidden hidden;
}
"""
class ScrollableContainer(Widget, can_focus=True):
"""A scrollable container with vertical layout, and auto scrollbars on both axis."""
# We don't typically want to maximize scrollable containers,
# since the user can easily navigate the contents
ALLOW_MAXIMIZE = False
DEFAULT_CSS = """
ScrollableContainer {
width: 1fr;
height: 1fr;
layout: vertical;
overflow: auto auto;
}
"""
BINDINGS: ClassVar[list[BindingType]] = [
Binding("up", "scroll_up", "Scroll Up", show=False),
Binding("down", "scroll_down", "Scroll Down", show=False),
Binding("left", "scroll_left", "Scroll Left", show=False),
Binding("right", "scroll_right", "Scroll Right", show=False),
Binding("home", "scroll_home", "Scroll Home", show=False),
Binding("end", "scroll_end", "Scroll End", show=False),
Binding("pageup", "page_up", "Page Up", show=False),
Binding("pagedown", "page_down", "Page Down", show=False),
Binding("ctrl+pageup", "page_left", "Page Left", show=False),
Binding("ctrl+pagedown", "page_right", "Page Right", show=False),
]
"""Keyboard bindings for scrollable containers.
| Key(s) | Description |
| :- | :- |
| up | Scroll up, if vertical scrolling is available. |
| down | Scroll down, if vertical scrolling is available. |
| left | Scroll left, if horizontal scrolling is available. |
| right | Scroll right, if horizontal scrolling is available. |
| home | Scroll to the home position, if scrolling is available. |
| end | Scroll to the end position, if scrolling is available. |
| pageup | Scroll up one page, if vertical scrolling is available. |
| pagedown | Scroll down one page, if vertical scrolling is available. |
| ctrl+pageup | Scroll left one page, if horizontal scrolling is available. |
| ctrl+pagedown | Scroll right one page, if horizontal scrolling is available. |
"""
def __init__(
self,
*children: Widget,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
can_focus: bool | None = None,
can_focus_children: bool | None = None,
can_maximize: bool | None = None,
) -> None:
"""
Construct a scrollable container.
Args:
*children: Child widgets.
name: The name of the widget.
id: The ID of the widget in the DOM.
classes: The CSS classes for the widget.
disabled: Whether the widget is disabled or not.
can_focus: Can this container be focused?
can_focus_children: Can this container's children be focused?
can_maximized: Allow this container to maximize? `None` to use default logic.,
"""
super().__init__(
*children,
name=name,
id=id,
classes=classes,
disabled=disabled,
)
if can_focus is not None:
self.can_focus = can_focus
if can_focus_children is not None:
self.can_focus_children = can_focus_children
self.can_maximize = can_maximize
@property
def allow_maximize(self) -> bool:
if self.can_maximize is None:
return super().allow_maximize
return self.can_maximize
class Vertical(Widget):
"""An expanding container with vertical layout and no scrollbars."""
DEFAULT_CSS = """
Vertical {
width: 1fr;
height: 1fr;
layout: vertical;
overflow: hidden hidden;
}
"""
class VerticalGroup(Widget):
"""A non-expanding container with vertical layout and no scrollbars."""
DEFAULT_CSS = """
VerticalGroup {
width: 1fr;
height: auto;
layout: vertical;
overflow: hidden hidden;
}
"""
class VerticalScroll(ScrollableContainer):
"""A container with vertical layout and an automatic scrollbar on the Y axis."""
DEFAULT_CSS = """
VerticalScroll {
layout: vertical;
overflow-x: hidden;
overflow-y: auto;
}
"""
class Horizontal(Widget):
"""An expanding container with horizontal layout and no scrollbars."""
DEFAULT_CSS = """
Horizontal {
width: 1fr;
height: 1fr;
layout: horizontal;
overflow: hidden hidden;
}
"""
class HorizontalGroup(Widget):
"""A non-expanding container with horizontal layout and no scrollbars."""
DEFAULT_CSS = """
HorizontalGroup {
width: 1fr;
height: auto;
layout: horizontal;
overflow: hidden hidden;
}
"""
class HorizontalScroll(ScrollableContainer):
"""A container with horizontal layout and an automatic scrollbar on the X axis."""
DEFAULT_CSS = """
HorizontalScroll {
layout: horizontal;
overflow-y: hidden;
overflow-x: auto;
}
"""
class Center(Widget):
"""A container which aligns children on the X axis."""
DEFAULT_CSS = """
Center {
align-horizontal: center;
width: 1fr;
height: auto;
}
"""
class Right(Widget):
"""A container which aligns children on the X axis."""
DEFAULT_CSS = """
Right {
align-horizontal: right;
width: 1fr;
height: auto;
}
"""
class Middle(Widget):
"""A container which aligns children on the Y axis."""
DEFAULT_CSS = """
Middle {
align-vertical: middle;
width: auto;
height: 1fr;
}
"""
class CenterMiddle(Widget):
"""A container which aligns its children on both axis."""
DEFAULT_CSS = """
CenterMiddle {
align: center middle;
width: 1fr;
height: 1fr;
}
"""
class Grid(Widget):
"""A container with grid layout."""
DEFAULT_CSS = """
Grid {
width: 1fr;
height: 1fr;
layout: grid;
}
"""
class ItemGrid(Widget):
"""A container with grid layout and automatic columns."""
DEFAULT_CSS = """
ItemGrid {
width: 1fr;
height: auto;
layout: grid;
}
"""
stretch_height: reactive[bool] = reactive(True)
min_column_width: reactive[int | None] = reactive(None, layout=True)
max_column_width: reactive[int | None] = reactive(None, layout=True)
regular: reactive[bool] = reactive(False)
def __init__(
self,
*children: Widget,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
min_column_width: int | None = None,
max_column_width: int | None = None,
stretch_height: bool = True,
regular: bool = False,
) -> None:
"""
Construct a ItemGrid.
Args:
*children: Child widgets.
name: The name of the widget.
id: The ID of the widget in the DOM.
classes: The CSS classes for the widget.
disabled: Whether the widget is disabled or not.
stretch_height: Expand the height of widgets to the row height.
min_column_width: The smallest permitted column width.
regular: All rows should have the same number of items.
"""
super().__init__(
*children, name=name, id=id, classes=classes, disabled=disabled
)
self.set_reactive(ItemGrid.stretch_height, stretch_height)
self.set_reactive(ItemGrid.min_column_width, min_column_width)
self.set_reactive(ItemGrid.max_column_width, max_column_width)
self.set_reactive(ItemGrid.regular, regular)
def pre_layout(self, layout: Layout) -> None:
if isinstance(layout, GridLayout):
layout.stretch_height = self.stretch_height
layout.min_column_width = self.min_column_width
layout.max_column_width = self.max_column_width
layout.regular = self.regular