254 lines
3.6 KiB
Python
254 lines
3.6 KiB
Python
from __future__ import annotations
|
||
|
||
from rich.console import Console, ConsoleOptions, RenderResult
|
||
from rich.measure import Measurement
|
||
from rich.segment import Segment
|
||
from rich.style import Style, StyleType
|
||
|
||
DIGITS = " 0123456789+-^x:ABCDEF$£€()"
|
||
DIGITS3X3_BOLD = """\
|
||
|
||
|
||
|
||
┏━┓
|
||
┃ ┃
|
||
┗━┛
|
||
╺┓
|
||
┃
|
||
╺┻╸
|
||
╺━┓
|
||
┏━┛
|
||
┗━╸
|
||
╺━┓
|
||
━┫
|
||
╺━┛
|
||
╻ ╻
|
||
┗━┫
|
||
╹
|
||
┏━╸
|
||
┗━┓
|
||
╺━┛
|
||
┏━╸
|
||
┣━┓
|
||
┗━┛
|
||
╺━┓
|
||
┃
|
||
╹
|
||
┏━┓
|
||
┣━┫
|
||
┗━┛
|
||
┏━┓
|
||
┗━┫
|
||
╺━┛
|
||
|
||
╺╋╸
|
||
|
||
|
||
╺━╸
|
||
|
||
^
|
||
|
||
|
||
|
||
×
|
||
|
||
|
||
:
|
||
|
||
╭─╮
|
||
├─┤
|
||
╵ ╵
|
||
┌─╮
|
||
├─┤
|
||
└─╯
|
||
╭─╮
|
||
│
|
||
╰─╯
|
||
┌─╮
|
||
│ │
|
||
└─╯
|
||
╭─╴
|
||
├─
|
||
╰─╴
|
||
╭─╴
|
||
├─
|
||
╵
|
||
╭╫╮
|
||
╰╫╮
|
||
╰╫╯
|
||
╭─╮
|
||
╪═
|
||
┷━╸
|
||
╭─╮
|
||
╪═
|
||
╰─╯
|
||
╭╴
|
||
│
|
||
╰╴
|
||
╶╮
|
||
│
|
||
╶╯
|
||
""".splitlines()
|
||
|
||
|
||
DIGITS3X3 = """\
|
||
|
||
|
||
|
||
╭─╮
|
||
│ │
|
||
╰─╯
|
||
╶╮
|
||
│
|
||
╶┴╴
|
||
╶─╮
|
||
┌─┘
|
||
╰─╴
|
||
╶─╮
|
||
─┤
|
||
╶─╯
|
||
╷ ╷
|
||
╰─┤
|
||
╵
|
||
╭─╴
|
||
╰─╮
|
||
╶─╯
|
||
╭─╴
|
||
├─╮
|
||
╰─╯
|
||
╶─┐
|
||
│
|
||
╵
|
||
╭─╮
|
||
├─┤
|
||
╰─╯
|
||
╭─╮
|
||
╰─┤
|
||
╶─╯
|
||
|
||
╶┼╴
|
||
|
||
|
||
╶─╴
|
||
|
||
^
|
||
|
||
|
||
|
||
×
|
||
|
||
|
||
:
|
||
|
||
╭─╮
|
||
├─┤
|
||
╵ ╵
|
||
┌─╮
|
||
├─┤
|
||
└─╯
|
||
╭─╮
|
||
│
|
||
╰─╯
|
||
┌─╮
|
||
│ │
|
||
└─╯
|
||
╭─╴
|
||
├─
|
||
╰─╴
|
||
╭─╴
|
||
├─
|
||
╵
|
||
╭╫╮
|
||
╰╫╮
|
||
╰╫╯
|
||
╭─╮
|
||
╪═
|
||
┷━╸
|
||
╭─╮
|
||
╪═
|
||
╰─╯
|
||
╭╴
|
||
│
|
||
╰╴
|
||
╶╮
|
||
│
|
||
╶╯
|
||
""".splitlines()
|
||
|
||
|
||
class Digits:
|
||
"""Renders a 3X3 unicode 'font' for numerical values.
|
||
|
||
Args:
|
||
text: Text to display.
|
||
style: Style to apply to the digits.
|
||
|
||
"""
|
||
|
||
REPLACEMENTS = str.maketrans({".": "•"})
|
||
|
||
def __init__(self, text: str, style: StyleType = "") -> None:
|
||
self._text = text
|
||
self._style = style
|
||
|
||
def __rich_console__(
|
||
self, console: Console, options: ConsoleOptions
|
||
) -> RenderResult:
|
||
style = console.get_style(self._style)
|
||
yield from self.render(style)
|
||
|
||
def render(self, style: Style) -> RenderResult:
|
||
"""Render with the given style
|
||
|
||
Args:
|
||
style: Rich Style.
|
||
|
||
Returns:
|
||
Result of render.
|
||
"""
|
||
digit_pieces: list[list[str]] = [[], [], []]
|
||
row1 = digit_pieces[0].append
|
||
row2 = digit_pieces[1].append
|
||
row3 = digit_pieces[2].append
|
||
|
||
if style.bold:
|
||
digits = DIGITS3X3_BOLD
|
||
else:
|
||
digits = DIGITS3X3
|
||
|
||
for character in self._text.translate(self.REPLACEMENTS):
|
||
try:
|
||
position = DIGITS.index(character) * 3
|
||
except ValueError:
|
||
row1(" ")
|
||
row2(" ")
|
||
row3(character)
|
||
else:
|
||
row1(digits[position].ljust(3))
|
||
row2(digits[position + 1].ljust(3))
|
||
row3(digits[position + 2].ljust(3))
|
||
|
||
new_line = Segment.line()
|
||
for line in digit_pieces:
|
||
yield Segment("".join(line), style + Style(meta={"offset": (0, 0)}))
|
||
yield new_line
|
||
|
||
@classmethod
|
||
def get_width(cls, text: str) -> int:
|
||
"""Calculate the width without rendering.
|
||
|
||
Args:
|
||
text: Text which may be displayed in the `Digits` widget.
|
||
|
||
Returns:
|
||
width of the text (in cells).
|
||
"""
|
||
width = sum(3 if character in DIGITS else 1 for character in text)
|
||
return width
|
||
|
||
def __rich_measure__(
|
||
self, console: Console, options: ConsoleOptions
|
||
) -> Measurement:
|
||
width = self.get_width(self._text)
|
||
return Measurement(width, width)
|