111 lines
3.3 KiB
Python
111 lines
3.3 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, cast
|
|
|
|
from rich.align import Align, AlignMethod
|
|
|
|
if TYPE_CHECKING:
|
|
from textual.app import RenderResult
|
|
|
|
from textual.geometry import Size
|
|
from textual.renderables.digits import Digits as DigitsRenderable
|
|
from textual.selection import Selection
|
|
from textual.widget import Widget
|
|
|
|
|
|
class Digits(Widget):
|
|
"""A widget to display numerical values using a 3x3 grid of unicode characters."""
|
|
|
|
DEFAULT_CSS = """
|
|
Digits {
|
|
width: 1fr;
|
|
height: auto;
|
|
text-align: left;
|
|
box-sizing: border-box;
|
|
}
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
value: str = "",
|
|
*,
|
|
name: str | None = None,
|
|
id: str | None = None,
|
|
classes: str | None = None,
|
|
disabled: bool = False,
|
|
) -> None:
|
|
"""Initialize a Digits widget.
|
|
|
|
Args:
|
|
value: Value to display in widget.
|
|
name: The name of the widget.
|
|
id: The ID of the widget in the DOM.
|
|
classes: The CSS classes of the widget.
|
|
disabled: Whether the widget is disabled or not.
|
|
|
|
"""
|
|
if not isinstance(value, str):
|
|
raise TypeError("value must be a str")
|
|
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
|
|
self._value = value
|
|
|
|
@property
|
|
def value(self) -> str:
|
|
"""The current value displayed in the Digits."""
|
|
return self._value
|
|
|
|
def get_selection(self, selection: Selection) -> str | None:
|
|
return self._value
|
|
|
|
def update(self, value: str) -> None:
|
|
"""Update the Digits with a new value.
|
|
|
|
Args:
|
|
value: New value to display.
|
|
|
|
Raises:
|
|
TypeError: If the value isn't a `str`.
|
|
"""
|
|
if not isinstance(value, str):
|
|
raise TypeError("value must be a str")
|
|
layout_required = len(value) != len(self._value) or (
|
|
DigitsRenderable.get_width(self._value) != DigitsRenderable.get_width(value)
|
|
)
|
|
self._value = value
|
|
self.refresh(layout=layout_required)
|
|
|
|
def render(self) -> RenderResult:
|
|
"""Render digits."""
|
|
rich_style = self.rich_style
|
|
if self.text_selection:
|
|
rich_style += self.selection_style
|
|
digits = DigitsRenderable(self._value, rich_style)
|
|
text_align = self.styles.text_align
|
|
align = "left" if text_align not in {"left", "center", "right"} else text_align
|
|
return Align(digits, cast(AlignMethod, align), rich_style)
|
|
|
|
def get_content_width(self, container: Size, viewport: Size) -> int:
|
|
"""Called by textual to get the width of the content area.
|
|
|
|
Args:
|
|
container: Size of the container (immediate parent) widget.
|
|
viewport: Size of the viewport.
|
|
|
|
Returns:
|
|
The optimal width of the content.
|
|
"""
|
|
return DigitsRenderable.get_width(self._value)
|
|
|
|
def get_content_height(self, container: Size, viewport: Size, width: int) -> int:
|
|
"""Called by Textual to get the height of the content area.
|
|
|
|
Args:
|
|
container: Size of the container (immediate parent) widget.
|
|
viewport: Size of the viewport.
|
|
width: Width of renderable.
|
|
|
|
Returns:
|
|
The height of the content.
|
|
"""
|
|
return 3 # Always 3 lines
|