Changed code to support older Python versions

This commit is contained in:
Malasaur 2025-12-01 23:27:09 +01:00
parent eb92d2d36f
commit 582458cdd0
5027 changed files with 794942 additions and 4 deletions

View file

@ -0,0 +1,261 @@
from typing import Any, Optional, Tuple, Union
from rich import box
from rich.color import Color
from rich.console import Group, RenderableType
from rich.style import Style
from rich.text import Text
from rich_toolkit._rich_components import Panel
from rich_toolkit.container import Container
from rich_toolkit.element import CursorOffset, Element
from rich_toolkit.form import Form
from rich_toolkit.input import Input
from rich_toolkit.menu import Menu
from rich_toolkit.progress import Progress
from .base import BaseStyle
class BorderedStyle(BaseStyle):
box = box.SQUARE
def empty_line(self) -> RenderableType:
return ""
def _box(
self,
content: RenderableType,
title: Union[str, Text, None],
is_active: bool,
border_color: Color,
after: Tuple[str, ...] = (),
) -> RenderableType:
return Group(
Panel(
content,
title=title,
title_align="left",
highlight=is_active,
width=50,
box=self.box,
border_style=Style(color=border_color),
),
*after,
)
def render_container(
self,
element: Container,
is_active: bool = False,
done: bool = False,
parent: Optional[Element] = None,
) -> RenderableType:
content = super().render_container(element, is_active, done, parent)
if isinstance(element, Form):
return self._box(content, element.title, is_active, Color.parse("white"))
return content
def render_input(
self,
element: Input,
is_active: bool = False,
done: bool = False,
parent: Optional[Element] = None,
**metadata: Any,
) -> RenderableType:
validation_message: Tuple[str, ...] = ()
if isinstance(parent, Form):
return super().render_input(element, is_active, done, parent, **metadata)
if message := self.render_validation_message(element):
validation_message = (message,)
title = self.render_input_label(
element,
is_active=is_active,
parent=parent,
)
# Determine border color based on validation state
if element.valid is False:
try:
border_color = self.console.get_style("error").color or Color.parse(
"red"
)
except Exception:
# Fallback if error style is not defined
border_color = Color.parse("red")
else:
border_color = Color.parse("white")
return self._box(
self.render_input_value(element, is_active=is_active, parent=parent),
title,
is_active,
border_color,
after=validation_message,
)
def render_menu(
self,
element: Menu,
is_active: bool = False,
done: bool = False,
parent: Optional[Element] = None,
**metadata: Any,
) -> RenderableType:
validation_message: Tuple[str, ...] = ()
menu = Text(justify="left")
selected_prefix = Text(element.current_selection_char + " ")
not_selected_prefix = Text(element.selection_char + " ")
separator = Text("\t" if element.inline else "\n")
content: list[RenderableType] = []
if done:
content.append(
Text(
element.options[element.selected]["name"],
style=self.console.get_style("result"),
)
)
else:
# Get visible range for scrolling
all_options = element.options
start, end = element.visible_options_range
visible_options = all_options[start:end]
# Check if scrolling is needed (to reserve consistent space for indicators)
needs_scrolling = element._needs_scrolling()
# Always reserve space for "more above" indicator when scrolling is enabled
if needs_scrolling:
if element.has_more_above:
menu.append(Text(element.MORE_ABOVE_INDICATOR + "\n", style="dim"))
else:
menu.append(Text(" " * len(element.MORE_ABOVE_INDICATOR) + "\n"))
for idx, option in enumerate(visible_options):
actual_idx = start + idx
if actual_idx == element.selected:
prefix = selected_prefix
style = self.console.get_style("selected")
else:
prefix = not_selected_prefix
style = self.console.get_style("text")
is_last = idx == len(visible_options) - 1
menu.append(
Text.assemble(
prefix,
option["name"],
separator if not is_last else "",
style=style,
)
)
# Always reserve space for "more below" indicator when scrolling is enabled
if needs_scrolling:
if element.has_more_below:
menu.append(Text("\n" + element.MORE_BELOW_INDICATOR, style="dim"))
else:
menu.append(Text("\n" + " " * len(element.MORE_BELOW_INDICATOR)))
if not element.options:
menu = Text("No results found", style=self.console.get_style("text"))
filter = (
[
Text.assemble(
(element.filter_prompt, self.console.get_style("text")),
(element.text, self.console.get_style("text")),
"\n",
)
]
if element.allow_filtering
else []
)
content.extend(filter)
content.append(menu)
if message := self.render_validation_message(element):
validation_message = (message,)
result = Group(*content)
return self._box(
result,
self.render_input_label(element),
is_active,
Color.parse("white"),
after=validation_message,
)
def render_progress(
self,
element: Progress,
is_active: bool = False,
done: bool = False,
parent: Optional[Element] = None,
) -> RenderableType:
content: str | Group | Text = element.current_message
title: Union[str, Text, None] = None
title = element.title
if element.logs and element._inline_logs:
lines_to_show = (
element.logs[-element.lines_to_show :]
if element.lines_to_show > 0
else element.logs
)
content = Group(
*[
self.render_element(
line,
index=index,
max_lines=element.lines_to_show,
total_lines=len(element.logs),
)
for index, line in enumerate(lines_to_show)
]
)
border_color = Color.parse("white")
if not done:
colors = self._get_animation_colors(
steps=10, animation_status="started", breathe=True
)
border_color = colors[self.animation_counter % 10]
return self._box(content, title, is_active, border_color=border_color)
def get_cursor_offset_for_element(
self, element: Element, parent: Optional[Element] = None
) -> CursorOffset:
top_offset = element.cursor_offset.top
left_offset = element.cursor_offset.left + 2
if isinstance(element, Input) and element.inline:
# we don't support inline inputs yet in border style
top_offset += 1
inline_left_offset = (len(element.label) - 1) if element.label else 0
left_offset = element.cursor_offset.left - inline_left_offset
if isinstance(parent, Form):
top_offset += 1
return CursorOffset(top=top_offset, left=left_offset)