Files
tidal-dl-ng-webui/env/lib/python3.11/site-packages/rich_toolkit/styles/base.py
2024-12-27 22:31:23 +09:00

126 lines
3.7 KiB
Python

from abc import ABC, abstractmethod
from typing import Any, Generator, Iterable, List, Type, TypeVar
from typing_extensions import Literal
from rich.color import Color
from rich.console import (
Console,
ConsoleOptions,
ConsoleRenderable,
RenderableType,
RenderResult,
)
from rich.segment import Segment
from rich.text import Text
from rich_toolkit.utils.colors import lighten
ConsoleRenderableClass = TypeVar(
"ConsoleRenderableClass", bound=Type[ConsoleRenderable]
)
ANIMATION_STATUS = Literal["started", "stopped", "error", "no_animation"]
class BaseStyle(ABC):
result_color: Color
decoration_size: int
def __init__(self) -> None:
self.padding = 2
self.cursor_offset = 0
self._animation_counter = 0
self.decoration_size = 2
def empty_line(self) -> Text:
return Text(" ")
def with_decoration(
self,
*renderables: RenderableType,
animation_status: ANIMATION_STATUS = "no_animation",
**metadata: Any,
) -> ConsoleRenderable:
class WithDecoration:
@staticmethod
def __rich_console__(
console: Console, options: ConsoleOptions
) -> RenderResult:
for content in renderables:
# our styles are potentially adding some paddings on the left
# and right sides, so we need to adjust the max_width to
# make sure that rich takes that into account
options = options.update(
max_width=console.width - self.decoration_size
)
lines = console.render_lines(content, options, pad=False)
for line in Segment.split_lines(
self.decorate(
lines=lines,
console=console,
animation_status=animation_status,
**metadata,
)
):
yield from line
yield Segment.line()
return WithDecoration()
def decorate_class(
self, klass: ConsoleRenderableClass, **metadata: Any
) -> ConsoleRenderableClass:
style = self
class Decorated(klass): # type: ignore[valid-type,misc]
def __rich_console__(
self, console: Console, options: ConsoleOptions
) -> RenderResult:
lines = Segment.split_lines(super().__rich_console__(console, options)) # type: ignore
yield from style.decorate(lines=lines, console=console, **metadata)
return Decorated # type: ignore
@abstractmethod
def decorate(
self,
console: Console,
lines: Iterable[List[Segment]],
animation_status: ANIMATION_STATUS = "no_animation",
**kwargs: Any,
) -> Generator[Segment, None, None]:
raise NotImplementedError()
def _get_animation_colors(
self,
console: Console,
steps: int = 5,
animation_status: ANIMATION_STATUS = "started",
**metadata: Any,
) -> List[Color]:
animated = animation_status == "started"
if animation_status == "error":
base_color = console.get_style("error").color
if base_color is None:
base_color = Color.parse("red")
else:
base_color = console.get_style("progress").bgcolor
if not base_color:
base_color = Color.parse("white")
if animated and base_color.triplet is not None:
return [lighten(base_color, 0.1 * i) for i in range(0, steps)]
return [base_color] * steps