ASCII-ize scrollbar in --ascii-only mode

This commit is contained in:
Isaiah Odhner 2023-09-05 19:24:52 -04:00
parent e2c53e48a0
commit 7c4947df12
2 changed files with 100 additions and 0 deletions

View File

@ -28,6 +28,7 @@ from textual.containers import Container, Vertical, Horizontal
from textual.geometry import Offset, Region, Size from textual.geometry import Offset, Region, Size
from textual.css._style_properties import BorderDefinition from textual.css._style_properties import BorderDefinition
from textual.reactive import var, reactive from textual.reactive import var, reactive
from textual.scrollbar import ScrollBar, ScrollBarRender
from textual.strip import Strip from textual.strip import Strip
from textual.dom import DOMNode from textual.dom import DOMNode
from textual.widget import Widget from textual.widget import Widget
@ -51,6 +52,7 @@ from .graphics_primitives import (
) )
from .menus import MenuBar, Menu, MenuItem, Separator from .menus import MenuBar, Menu, MenuItem, Separator
from .windows import Window, DialogWindow, CharacterSelectorDialogWindow, MessageBox, get_warning_icon, get_question_icon, get_paint_icon from .windows import Window, DialogWindow, CharacterSelectorDialogWindow, MessageBox, get_warning_icon, get_question_icon, get_paint_icon
from .scrollbars import ASCIIScrollBarRender
from .file_dialogs import SaveAsDialogWindow, OpenDialogWindow from .file_dialogs import SaveAsDialogWindow, OpenDialogWindow
from .edit_colors import EditColorsDialogWindow from .edit_colors import EditColorsDialogWindow
from .localization.i18n import get as _, load_language, remove_hotkey from .localization.i18n import get as _, load_language, remove_hotkey
@ -5253,6 +5255,9 @@ if args.ascii_only:
RadioButton.BUTTON_LEFT = "[" RadioButton.BUTTON_LEFT = "["
RadioButton.BUTTON_RIGHT = "]" RadioButton.BUTTON_RIGHT = "]"
ScrollBar.renderer = ASCIIScrollBarRender
# header_icon_markup = "[on white][blue]\\\\[/][red]|[/][yellow]/[/][/]" # header_icon_markup = "[on white][blue]\\\\[/][red]|[/][yellow]/[/][/]"
# header_icon_markup = "[black]..,[/]\n[blue]\\\\[/][on white][red]|[/][yellow]/[/][/]\n[black on rgb(192,192,192)]\\[_][/]" # header_icon_markup = "[black]..,[/]\n[blue]\\\\[/][on white][red]|[/][yellow]/[/][/]\n[black on rgb(192,192,192)]\\[_][/]"
# trying different geometries for the page going behind the cup of brushes: # trying different geometries for the page going behind the cup of brushes:

View File

@ -0,0 +1,95 @@
from math import ceil
from rich.color import Color
from rich.segment import Segment, Segments
from rich.style import Style
from textual.scrollbar import ScrollBarRender
class ASCIIScrollBarRender(ScrollBarRender):
"""A scrollbar renderer that uses ASCII characters."""
@classmethod
def render_bar(
cls,
size: int = 25,
virtual_size: float = 50,
window_size: float = 20,
position: float = 0,
thickness: int = 1,
vertical: bool = True,
back_color: Color = Color.parse("#555555"),
bar_color: Color = Color.parse("bright_magenta"),
) -> Segments:
if vertical:
# bars = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", " "]
# bars = ["_", "=", "#", " "]
bars = [" "]
else:
# bars = ["▉", "▊", "▋", "▌", "▍", "▎", "▏", " "]
# bars = ["#", " "]
bars = [" "]
back = back_color
bar = bar_color
len_bars = len(bars)
width_thickness = thickness if vertical else 1
_Segment = Segment
_Style = Style
blank = " " * width_thickness
foreground_meta = {"@mouse.up": "release", "@mouse.down": "grab"}
if window_size and size and virtual_size and size != virtual_size:
bar_ratio = virtual_size / size
thumb_size = max(1, window_size / bar_ratio)
position_ratio = position / (virtual_size - window_size)
position = (size - thumb_size) * position_ratio
start = int(position * len_bars)
end = start + ceil(thumb_size * len_bars)
start_index, start_bar = divmod(max(0, start), len_bars)
end_index, end_bar = divmod(max(0, end), len_bars)
upper = {"@mouse.up": "scroll_up"}
lower = {"@mouse.up": "scroll_down"}
upper_back_segment = Segment(blank, _Style(bgcolor=back, meta=upper))
lower_back_segment = Segment(blank, _Style(bgcolor=back, meta=lower))
segments = [upper_back_segment] * int(size)
segments[end_index:] = [lower_back_segment] * (size - end_index)
segments[start_index:end_index] = [
_Segment(blank, _Style(bgcolor=bar, meta=foreground_meta))
] * (end_index - start_index)
# Apply the smaller bar characters to head and tail of scrollbar for more "granularity"
if start_index < len(segments):
bar_character = bars[len_bars - 1 - start_bar]
if bar_character != " ":
segments[start_index] = _Segment(
bar_character * width_thickness,
_Style(bgcolor=back, color=bar, meta=foreground_meta)
if vertical
else _Style(bgcolor=bar, color=back, meta=foreground_meta),
)
if end_index < len(segments):
bar_character = bars[len_bars - 1 - end_bar]
if bar_character != " ":
segments[end_index] = _Segment(
bar_character * width_thickness,
_Style(bgcolor=bar, color=back, meta=foreground_meta)
if vertical
else _Style(bgcolor=back, color=bar, meta=foreground_meta),
)
else:
style = _Style(bgcolor=back)
segments = [_Segment(blank, style=style)] * int(size)
if vertical:
return Segments(segments, new_lines=True)
else:
return Segments((segments + [_Segment.line()]) * thickness, new_lines=False)