kannadaLettersClassification
/
env
/lib
/python3.12
/site-packages
/prompt_toolkit
/widgets
/base.py
| """ | |
| Collection of reusable components for building full screen applications. | |
| All of these widgets implement the ``__pt_container__`` method, which makes | |
| them usable in any situation where we are expecting a `prompt_toolkit` | |
| container object. | |
| .. warning:: | |
| At this point, the API for these widgets is considered unstable, and can | |
| potentially change between minor releases (we try not too, but no | |
| guarantees are made yet). The public API in | |
| `prompt_toolkit.shortcuts.dialogs` on the other hand is considered stable. | |
| """ | |
| from __future__ import annotations | |
| from functools import partial | |
| from typing import Callable, Generic, Sequence, TypeVar | |
| from prompt_toolkit.application.current import get_app | |
| from prompt_toolkit.auto_suggest import AutoSuggest, DynamicAutoSuggest | |
| from prompt_toolkit.buffer import Buffer, BufferAcceptHandler | |
| from prompt_toolkit.completion import Completer, DynamicCompleter | |
| from prompt_toolkit.document import Document | |
| from prompt_toolkit.filters import ( | |
| Condition, | |
| FilterOrBool, | |
| has_focus, | |
| is_done, | |
| is_true, | |
| to_filter, | |
| ) | |
| from prompt_toolkit.formatted_text import ( | |
| AnyFormattedText, | |
| StyleAndTextTuples, | |
| Template, | |
| to_formatted_text, | |
| ) | |
| from prompt_toolkit.formatted_text.utils import fragment_list_to_text | |
| from prompt_toolkit.history import History | |
| from prompt_toolkit.key_binding.key_bindings import KeyBindings | |
| from prompt_toolkit.key_binding.key_processor import KeyPressEvent | |
| from prompt_toolkit.keys import Keys | |
| from prompt_toolkit.layout.containers import ( | |
| AnyContainer, | |
| ConditionalContainer, | |
| Container, | |
| DynamicContainer, | |
| Float, | |
| FloatContainer, | |
| HSplit, | |
| VSplit, | |
| Window, | |
| WindowAlign, | |
| ) | |
| from prompt_toolkit.layout.controls import ( | |
| BufferControl, | |
| FormattedTextControl, | |
| GetLinePrefixCallable, | |
| ) | |
| from prompt_toolkit.layout.dimension import AnyDimension | |
| from prompt_toolkit.layout.dimension import Dimension as D | |
| from prompt_toolkit.layout.margins import ( | |
| ConditionalMargin, | |
| NumberedMargin, | |
| ScrollbarMargin, | |
| ) | |
| from prompt_toolkit.layout.processors import ( | |
| AppendAutoSuggestion, | |
| BeforeInput, | |
| ConditionalProcessor, | |
| PasswordProcessor, | |
| Processor, | |
| ) | |
| from prompt_toolkit.lexers import DynamicLexer, Lexer | |
| from prompt_toolkit.mouse_events import MouseEvent, MouseEventType | |
| from prompt_toolkit.utils import get_cwidth | |
| from prompt_toolkit.validation import DynamicValidator, Validator | |
| from .toolbars import SearchToolbar | |
| __all__ = [ | |
| "TextArea", | |
| "Label", | |
| "Button", | |
| "Frame", | |
| "Shadow", | |
| "Box", | |
| "VerticalLine", | |
| "HorizontalLine", | |
| "RadioList", | |
| "CheckboxList", | |
| "Checkbox", # backward compatibility | |
| "ProgressBar", | |
| ] | |
| E = KeyPressEvent | |
| class Border: | |
| "Box drawing characters. (Thin)" | |
| HORIZONTAL = "\u2500" | |
| VERTICAL = "\u2502" | |
| TOP_LEFT = "\u250c" | |
| TOP_RIGHT = "\u2510" | |
| BOTTOM_LEFT = "\u2514" | |
| BOTTOM_RIGHT = "\u2518" | |
| class TextArea: | |
| """ | |
| A simple input field. | |
| This is a higher level abstraction on top of several other classes with | |
| sane defaults. | |
| This widget does have the most common options, but it does not intend to | |
| cover every single use case. For more configurations options, you can | |
| always build a text area manually, using a | |
| :class:`~prompt_toolkit.buffer.Buffer`, | |
| :class:`~prompt_toolkit.layout.BufferControl` and | |
| :class:`~prompt_toolkit.layout.Window`. | |
| Buffer attributes: | |
| :param text: The initial text. | |
| :param multiline: If True, allow multiline input. | |
| :param completer: :class:`~prompt_toolkit.completion.Completer` instance | |
| for auto completion. | |
| :param complete_while_typing: Boolean. | |
| :param accept_handler: Called when `Enter` is pressed (This should be a | |
| callable that takes a buffer as input). | |
| :param history: :class:`~prompt_toolkit.history.History` instance. | |
| :param auto_suggest: :class:`~prompt_toolkit.auto_suggest.AutoSuggest` | |
| instance for input suggestions. | |
| BufferControl attributes: | |
| :param password: When `True`, display using asterisks. | |
| :param focusable: When `True`, allow this widget to receive the focus. | |
| :param focus_on_click: When `True`, focus after mouse click. | |
| :param input_processors: `None` or a list of | |
| :class:`~prompt_toolkit.layout.Processor` objects. | |
| :param validator: `None` or a :class:`~prompt_toolkit.validation.Validator` | |
| object. | |
| Window attributes: | |
| :param lexer: :class:`~prompt_toolkit.lexers.Lexer` instance for syntax | |
| highlighting. | |
| :param wrap_lines: When `True`, don't scroll horizontally, but wrap lines. | |
| :param width: Window width. (:class:`~prompt_toolkit.layout.Dimension` object.) | |
| :param height: Window height. (:class:`~prompt_toolkit.layout.Dimension` object.) | |
| :param scrollbar: When `True`, display a scroll bar. | |
| :param style: A style string. | |
| :param dont_extend_width: When `True`, don't take up more width then the | |
| preferred width reported by the control. | |
| :param dont_extend_height: When `True`, don't take up more width then the | |
| preferred height reported by the control. | |
| :param get_line_prefix: None or a callable that returns formatted text to | |
| be inserted before a line. It takes a line number (int) and a | |
| wrap_count and returns formatted text. This can be used for | |
| implementation of line continuations, things like Vim "breakindent" and | |
| so on. | |
| Other attributes: | |
| :param search_field: An optional `SearchToolbar` object. | |
| """ | |
| def __init__( | |
| self, | |
| text: str = "", | |
| multiline: FilterOrBool = True, | |
| password: FilterOrBool = False, | |
| lexer: Lexer | None = None, | |
| auto_suggest: AutoSuggest | None = None, | |
| completer: Completer | None = None, | |
| complete_while_typing: FilterOrBool = True, | |
| validator: Validator | None = None, | |
| accept_handler: BufferAcceptHandler | None = None, | |
| history: History | None = None, | |
| focusable: FilterOrBool = True, | |
| focus_on_click: FilterOrBool = False, | |
| wrap_lines: FilterOrBool = True, | |
| read_only: FilterOrBool = False, | |
| width: AnyDimension = None, | |
| height: AnyDimension = None, | |
| dont_extend_height: FilterOrBool = False, | |
| dont_extend_width: FilterOrBool = False, | |
| line_numbers: bool = False, | |
| get_line_prefix: GetLinePrefixCallable | None = None, | |
| scrollbar: bool = False, | |
| style: str = "", | |
| search_field: SearchToolbar | None = None, | |
| preview_search: FilterOrBool = True, | |
| prompt: AnyFormattedText = "", | |
| input_processors: list[Processor] | None = None, | |
| name: str = "", | |
| ) -> None: | |
| if search_field is None: | |
| search_control = None | |
| elif isinstance(search_field, SearchToolbar): | |
| search_control = search_field.control | |
| if input_processors is None: | |
| input_processors = [] | |
| # Writeable attributes. | |
| self.completer = completer | |
| self.complete_while_typing = complete_while_typing | |
| self.lexer = lexer | |
| self.auto_suggest = auto_suggest | |
| self.read_only = read_only | |
| self.wrap_lines = wrap_lines | |
| self.validator = validator | |
| self.buffer = Buffer( | |
| document=Document(text, 0), | |
| multiline=multiline, | |
| read_only=Condition(lambda: is_true(self.read_only)), | |
| completer=DynamicCompleter(lambda: self.completer), | |
| complete_while_typing=Condition( | |
| lambda: is_true(self.complete_while_typing) | |
| ), | |
| validator=DynamicValidator(lambda: self.validator), | |
| auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), | |
| accept_handler=accept_handler, | |
| history=history, | |
| name=name, | |
| ) | |
| self.control = BufferControl( | |
| buffer=self.buffer, | |
| lexer=DynamicLexer(lambda: self.lexer), | |
| input_processors=[ | |
| ConditionalProcessor( | |
| AppendAutoSuggestion(), has_focus(self.buffer) & ~is_done | |
| ), | |
| ConditionalProcessor( | |
| processor=PasswordProcessor(), filter=to_filter(password) | |
| ), | |
| BeforeInput(prompt, style="class:text-area.prompt"), | |
| ] | |
| + input_processors, | |
| search_buffer_control=search_control, | |
| preview_search=preview_search, | |
| focusable=focusable, | |
| focus_on_click=focus_on_click, | |
| ) | |
| if multiline: | |
| if scrollbar: | |
| right_margins = [ScrollbarMargin(display_arrows=True)] | |
| else: | |
| right_margins = [] | |
| if line_numbers: | |
| left_margins = [NumberedMargin()] | |
| else: | |
| left_margins = [] | |
| else: | |
| height = D.exact(1) | |
| left_margins = [] | |
| right_margins = [] | |
| style = "class:text-area " + style | |
| # If no height was given, guarantee height of at least 1. | |
| if height is None: | |
| height = D(min=1) | |
| self.window = Window( | |
| height=height, | |
| width=width, | |
| dont_extend_height=dont_extend_height, | |
| dont_extend_width=dont_extend_width, | |
| content=self.control, | |
| style=style, | |
| wrap_lines=Condition(lambda: is_true(self.wrap_lines)), | |
| left_margins=left_margins, | |
| right_margins=right_margins, | |
| get_line_prefix=get_line_prefix, | |
| ) | |
| def text(self) -> str: | |
| """ | |
| The `Buffer` text. | |
| """ | |
| return self.buffer.text | |
| def text(self, value: str) -> None: | |
| self.document = Document(value, 0) | |
| def document(self) -> Document: | |
| """ | |
| The `Buffer` document (text + cursor position). | |
| """ | |
| return self.buffer.document | |
| def document(self, value: Document) -> None: | |
| self.buffer.set_document(value, bypass_readonly=True) | |
| def accept_handler(self) -> BufferAcceptHandler | None: | |
| """ | |
| The accept handler. Called when the user accepts the input. | |
| """ | |
| return self.buffer.accept_handler | |
| def accept_handler(self, value: BufferAcceptHandler) -> None: | |
| self.buffer.accept_handler = value | |
| def __pt_container__(self) -> Container: | |
| return self.window | |
| class Label: | |
| """ | |
| Widget that displays the given text. It is not editable or focusable. | |
| :param text: Text to display. Can be multiline. All value types accepted by | |
| :class:`prompt_toolkit.layout.FormattedTextControl` are allowed, | |
| including a callable. | |
| :param style: A style string. | |
| :param width: When given, use this width, rather than calculating it from | |
| the text size. | |
| :param dont_extend_width: When `True`, don't take up more width than | |
| preferred, i.e. the length of the longest line of | |
| the text, or value of `width` parameter, if | |
| given. `True` by default | |
| :param dont_extend_height: When `True`, don't take up more width than the | |
| preferred height, i.e. the number of lines of | |
| the text. `False` by default. | |
| """ | |
| def __init__( | |
| self, | |
| text: AnyFormattedText, | |
| style: str = "", | |
| width: AnyDimension = None, | |
| dont_extend_height: bool = True, | |
| dont_extend_width: bool = False, | |
| align: WindowAlign | Callable[[], WindowAlign] = WindowAlign.LEFT, | |
| # There is no cursor navigation in a label, so it makes sense to always | |
| # wrap lines by default. | |
| wrap_lines: FilterOrBool = True, | |
| ) -> None: | |
| self.text = text | |
| def get_width() -> AnyDimension: | |
| if width is None: | |
| text_fragments = to_formatted_text(self.text) | |
| text = fragment_list_to_text(text_fragments) | |
| if text: | |
| longest_line = max(get_cwidth(line) for line in text.splitlines()) | |
| else: | |
| return D(preferred=0) | |
| return D(preferred=longest_line) | |
| else: | |
| return width | |
| self.formatted_text_control = FormattedTextControl(text=lambda: self.text) | |
| self.window = Window( | |
| content=self.formatted_text_control, | |
| width=get_width, | |
| height=D(min=1), | |
| style="class:label " + style, | |
| dont_extend_height=dont_extend_height, | |
| dont_extend_width=dont_extend_width, | |
| align=align, | |
| wrap_lines=wrap_lines, | |
| ) | |
| def __pt_container__(self) -> Container: | |
| return self.window | |
| class Button: | |
| """ | |
| Clickable button. | |
| :param text: The caption for the button. | |
| :param handler: `None` or callable. Called when the button is clicked. No | |
| parameters are passed to this callable. Use for instance Python's | |
| `functools.partial` to pass parameters to this callable if needed. | |
| :param width: Width of the button. | |
| """ | |
| def __init__( | |
| self, | |
| text: str, | |
| handler: Callable[[], None] | None = None, | |
| width: int = 12, | |
| left_symbol: str = "<", | |
| right_symbol: str = ">", | |
| ) -> None: | |
| self.text = text | |
| self.left_symbol = left_symbol | |
| self.right_symbol = right_symbol | |
| self.handler = handler | |
| self.width = width | |
| self.control = FormattedTextControl( | |
| self._get_text_fragments, | |
| key_bindings=self._get_key_bindings(), | |
| focusable=True, | |
| ) | |
| def get_style() -> str: | |
| if get_app().layout.has_focus(self): | |
| return "class:button.focused" | |
| else: | |
| return "class:button" | |
| # Note: `dont_extend_width` is False, because we want to allow buttons | |
| # to take more space if the parent container provides more space. | |
| # Otherwise, we will also truncate the text. | |
| # Probably we need a better way here to adjust to width of the | |
| # button to the text. | |
| self.window = Window( | |
| self.control, | |
| align=WindowAlign.CENTER, | |
| height=1, | |
| width=width, | |
| style=get_style, | |
| dont_extend_width=False, | |
| dont_extend_height=True, | |
| ) | |
| def _get_text_fragments(self) -> StyleAndTextTuples: | |
| width = self.width - ( | |
| get_cwidth(self.left_symbol) + get_cwidth(self.right_symbol) | |
| ) | |
| text = (f"{{:^{width}}}").format(self.text) | |
| def handler(mouse_event: MouseEvent) -> None: | |
| if ( | |
| self.handler is not None | |
| and mouse_event.event_type == MouseEventType.MOUSE_UP | |
| ): | |
| self.handler() | |
| return [ | |
| ("class:button.arrow", self.left_symbol, handler), | |
| ("[SetCursorPosition]", ""), | |
| ("class:button.text", text, handler), | |
| ("class:button.arrow", self.right_symbol, handler), | |
| ] | |
| def _get_key_bindings(self) -> KeyBindings: | |
| "Key bindings for the Button." | |
| kb = KeyBindings() | |
| def _(event: E) -> None: | |
| if self.handler is not None: | |
| self.handler() | |
| return kb | |
| def __pt_container__(self) -> Container: | |
| return self.window | |
| class Frame: | |
| """ | |
| Draw a border around any container, optionally with a title text. | |
| Changing the title and body of the frame is possible at runtime by | |
| assigning to the `body` and `title` attributes of this class. | |
| :param body: Another container object. | |
| :param title: Text to be displayed in the top of the frame (can be formatted text). | |
| :param style: Style string to be applied to this widget. | |
| """ | |
| def __init__( | |
| self, | |
| body: AnyContainer, | |
| title: AnyFormattedText = "", | |
| style: str = "", | |
| width: AnyDimension = None, | |
| height: AnyDimension = None, | |
| key_bindings: KeyBindings | None = None, | |
| modal: bool = False, | |
| ) -> None: | |
| self.title = title | |
| self.body = body | |
| fill = partial(Window, style="class:frame.border") | |
| style = "class:frame " + style | |
| top_row_with_title = VSplit( | |
| [ | |
| fill(width=1, height=1, char=Border.TOP_LEFT), | |
| fill(char=Border.HORIZONTAL), | |
| fill(width=1, height=1, char="|"), | |
| # Notice: we use `Template` here, because `self.title` can be an | |
| # `HTML` object for instance. | |
| Label( | |
| lambda: Template(" {} ").format(self.title), | |
| style="class:frame.label", | |
| dont_extend_width=True, | |
| ), | |
| fill(width=1, height=1, char="|"), | |
| fill(char=Border.HORIZONTAL), | |
| fill(width=1, height=1, char=Border.TOP_RIGHT), | |
| ], | |
| height=1, | |
| ) | |
| top_row_without_title = VSplit( | |
| [ | |
| fill(width=1, height=1, char=Border.TOP_LEFT), | |
| fill(char=Border.HORIZONTAL), | |
| fill(width=1, height=1, char=Border.TOP_RIGHT), | |
| ], | |
| height=1, | |
| ) | |
| def has_title() -> bool: | |
| return bool(self.title) | |
| self.container = HSplit( | |
| [ | |
| ConditionalContainer(content=top_row_with_title, filter=has_title), | |
| ConditionalContainer(content=top_row_without_title, filter=~has_title), | |
| VSplit( | |
| [ | |
| fill(width=1, char=Border.VERTICAL), | |
| DynamicContainer(lambda: self.body), | |
| fill(width=1, char=Border.VERTICAL), | |
| # Padding is required to make sure that if the content is | |
| # too small, the right frame border is still aligned. | |
| ], | |
| padding=0, | |
| ), | |
| VSplit( | |
| [ | |
| fill(width=1, height=1, char=Border.BOTTOM_LEFT), | |
| fill(char=Border.HORIZONTAL), | |
| fill(width=1, height=1, char=Border.BOTTOM_RIGHT), | |
| ], | |
| # specifying height here will increase the rendering speed. | |
| height=1, | |
| ), | |
| ], | |
| width=width, | |
| height=height, | |
| style=style, | |
| key_bindings=key_bindings, | |
| modal=modal, | |
| ) | |
| def __pt_container__(self) -> Container: | |
| return self.container | |
| class Shadow: | |
| """ | |
| Draw a shadow underneath/behind this container. | |
| (This applies `class:shadow` the the cells under the shadow. The Style | |
| should define the colors for the shadow.) | |
| :param body: Another container object. | |
| """ | |
| def __init__(self, body: AnyContainer) -> None: | |
| self.container = FloatContainer( | |
| content=body, | |
| floats=[ | |
| Float( | |
| bottom=-1, | |
| height=1, | |
| left=1, | |
| right=-1, | |
| transparent=True, | |
| content=Window(style="class:shadow"), | |
| ), | |
| Float( | |
| bottom=-1, | |
| top=1, | |
| width=1, | |
| right=-1, | |
| transparent=True, | |
| content=Window(style="class:shadow"), | |
| ), | |
| ], | |
| ) | |
| def __pt_container__(self) -> Container: | |
| return self.container | |
| class Box: | |
| """ | |
| Add padding around a container. | |
| This also makes sure that the parent can provide more space than required by | |
| the child. This is very useful when wrapping a small element with a fixed | |
| size into a ``VSplit`` or ``HSplit`` object. The ``HSplit`` and ``VSplit`` | |
| try to make sure to adapt respectively the width and height, possibly | |
| shrinking other elements. Wrapping something in a ``Box`` makes it flexible. | |
| :param body: Another container object. | |
| :param padding: The margin to be used around the body. This can be | |
| overridden by `padding_left`, padding_right`, `padding_top` and | |
| `padding_bottom`. | |
| :param style: A style string. | |
| :param char: Character to be used for filling the space around the body. | |
| (This is supposed to be a character with a terminal width of 1.) | |
| """ | |
| def __init__( | |
| self, | |
| body: AnyContainer, | |
| padding: AnyDimension = None, | |
| padding_left: AnyDimension = None, | |
| padding_right: AnyDimension = None, | |
| padding_top: AnyDimension = None, | |
| padding_bottom: AnyDimension = None, | |
| width: AnyDimension = None, | |
| height: AnyDimension = None, | |
| style: str = "", | |
| char: None | str | Callable[[], str] = None, | |
| modal: bool = False, | |
| key_bindings: KeyBindings | None = None, | |
| ) -> None: | |
| self.padding = padding | |
| self.padding_left = padding_left | |
| self.padding_right = padding_right | |
| self.padding_top = padding_top | |
| self.padding_bottom = padding_bottom | |
| self.body = body | |
| def left() -> AnyDimension: | |
| if self.padding_left is None: | |
| return self.padding | |
| return self.padding_left | |
| def right() -> AnyDimension: | |
| if self.padding_right is None: | |
| return self.padding | |
| return self.padding_right | |
| def top() -> AnyDimension: | |
| if self.padding_top is None: | |
| return self.padding | |
| return self.padding_top | |
| def bottom() -> AnyDimension: | |
| if self.padding_bottom is None: | |
| return self.padding | |
| return self.padding_bottom | |
| self.container = HSplit( | |
| [ | |
| Window(height=top, char=char), | |
| VSplit( | |
| [ | |
| Window(width=left, char=char), | |
| body, | |
| Window(width=right, char=char), | |
| ] | |
| ), | |
| Window(height=bottom, char=char), | |
| ], | |
| width=width, | |
| height=height, | |
| style=style, | |
| modal=modal, | |
| key_bindings=None, | |
| ) | |
| def __pt_container__(self) -> Container: | |
| return self.container | |
| _T = TypeVar("_T") | |
| class _DialogList(Generic[_T]): | |
| """ | |
| Common code for `RadioList` and `CheckboxList`. | |
| """ | |
| open_character: str = "" | |
| close_character: str = "" | |
| container_style: str = "" | |
| default_style: str = "" | |
| selected_style: str = "" | |
| checked_style: str = "" | |
| multiple_selection: bool = False | |
| show_scrollbar: bool = True | |
| def __init__( | |
| self, | |
| values: Sequence[tuple[_T, AnyFormattedText]], | |
| default_values: Sequence[_T] | None = None, | |
| ) -> None: | |
| assert len(values) > 0 | |
| default_values = default_values or [] | |
| self.values = values | |
| # current_values will be used in multiple_selection, | |
| # current_value will be used otherwise. | |
| keys: list[_T] = [value for (value, _) in values] | |
| self.current_values: list[_T] = [ | |
| value for value in default_values if value in keys | |
| ] | |
| self.current_value: _T = ( | |
| default_values[0] | |
| if len(default_values) and default_values[0] in keys | |
| else values[0][0] | |
| ) | |
| # Cursor index: take first selected item or first item otherwise. | |
| if len(self.current_values) > 0: | |
| self._selected_index = keys.index(self.current_values[0]) | |
| else: | |
| self._selected_index = 0 | |
| # Key bindings. | |
| kb = KeyBindings() | |
| def _up(event: E) -> None: | |
| self._selected_index = max(0, self._selected_index - 1) | |
| def _down(event: E) -> None: | |
| self._selected_index = min(len(self.values) - 1, self._selected_index + 1) | |
| def _pageup(event: E) -> None: | |
| w = event.app.layout.current_window | |
| if w.render_info: | |
| self._selected_index = max( | |
| 0, self._selected_index - len(w.render_info.displayed_lines) | |
| ) | |
| def _pagedown(event: E) -> None: | |
| w = event.app.layout.current_window | |
| if w.render_info: | |
| self._selected_index = min( | |
| len(self.values) - 1, | |
| self._selected_index + len(w.render_info.displayed_lines), | |
| ) | |
| def _click(event: E) -> None: | |
| self._handle_enter() | |
| def _find(event: E) -> None: | |
| # We first check values after the selected value, then all values. | |
| values = list(self.values) | |
| for value in values[self._selected_index + 1 :] + values: | |
| text = fragment_list_to_text(to_formatted_text(value[1])).lower() | |
| if text.startswith(event.data.lower()): | |
| self._selected_index = self.values.index(value) | |
| return | |
| # Control and window. | |
| self.control = FormattedTextControl( | |
| self._get_text_fragments, key_bindings=kb, focusable=True | |
| ) | |
| self.window = Window( | |
| content=self.control, | |
| style=self.container_style, | |
| right_margins=[ | |
| ConditionalMargin( | |
| margin=ScrollbarMargin(display_arrows=True), | |
| filter=Condition(lambda: self.show_scrollbar), | |
| ), | |
| ], | |
| dont_extend_height=True, | |
| ) | |
| def _handle_enter(self) -> None: | |
| if self.multiple_selection: | |
| val = self.values[self._selected_index][0] | |
| if val in self.current_values: | |
| self.current_values.remove(val) | |
| else: | |
| self.current_values.append(val) | |
| else: | |
| self.current_value = self.values[self._selected_index][0] | |
| def _get_text_fragments(self) -> StyleAndTextTuples: | |
| def mouse_handler(mouse_event: MouseEvent) -> None: | |
| """ | |
| Set `_selected_index` and `current_value` according to the y | |
| position of the mouse click event. | |
| """ | |
| if mouse_event.event_type == MouseEventType.MOUSE_UP: | |
| self._selected_index = mouse_event.position.y | |
| self._handle_enter() | |
| result: StyleAndTextTuples = [] | |
| for i, value in enumerate(self.values): | |
| if self.multiple_selection: | |
| checked = value[0] in self.current_values | |
| else: | |
| checked = value[0] == self.current_value | |
| selected = i == self._selected_index | |
| style = "" | |
| if checked: | |
| style += " " + self.checked_style | |
| if selected: | |
| style += " " + self.selected_style | |
| result.append((style, self.open_character)) | |
| if selected: | |
| result.append(("[SetCursorPosition]", "")) | |
| if checked: | |
| result.append((style, "*")) | |
| else: | |
| result.append((style, " ")) | |
| result.append((style, self.close_character)) | |
| result.append((self.default_style, " ")) | |
| result.extend(to_formatted_text(value[1], style=self.default_style)) | |
| result.append(("", "\n")) | |
| # Add mouse handler to all fragments. | |
| for i in range(len(result)): | |
| result[i] = (result[i][0], result[i][1], mouse_handler) | |
| result.pop() # Remove last newline. | |
| return result | |
| def __pt_container__(self) -> Container: | |
| return self.window | |
| class RadioList(_DialogList[_T]): | |
| """ | |
| List of radio buttons. Only one can be checked at the same time. | |
| :param values: List of (value, label) tuples. | |
| """ | |
| open_character = "(" | |
| close_character = ")" | |
| container_style = "class:radio-list" | |
| default_style = "class:radio" | |
| selected_style = "class:radio-selected" | |
| checked_style = "class:radio-checked" | |
| multiple_selection = False | |
| def __init__( | |
| self, | |
| values: Sequence[tuple[_T, AnyFormattedText]], | |
| default: _T | None = None, | |
| ) -> None: | |
| if default is None: | |
| default_values = None | |
| else: | |
| default_values = [default] | |
| super().__init__(values, default_values=default_values) | |
| class CheckboxList(_DialogList[_T]): | |
| """ | |
| List of checkbox buttons. Several can be checked at the same time. | |
| :param values: List of (value, label) tuples. | |
| """ | |
| open_character = "[" | |
| close_character = "]" | |
| container_style = "class:checkbox-list" | |
| default_style = "class:checkbox" | |
| selected_style = "class:checkbox-selected" | |
| checked_style = "class:checkbox-checked" | |
| multiple_selection = True | |
| class Checkbox(CheckboxList[str]): | |
| """Backward compatibility util: creates a 1-sized CheckboxList | |
| :param text: the text | |
| """ | |
| show_scrollbar = False | |
| def __init__(self, text: AnyFormattedText = "", checked: bool = False) -> None: | |
| values = [("value", text)] | |
| super().__init__(values=values) | |
| self.checked = checked | |
| def checked(self) -> bool: | |
| return "value" in self.current_values | |
| def checked(self, value: bool) -> None: | |
| if value: | |
| self.current_values = ["value"] | |
| else: | |
| self.current_values = [] | |
| class VerticalLine: | |
| """ | |
| A simple vertical line with a width of 1. | |
| """ | |
| def __init__(self) -> None: | |
| self.window = Window( | |
| char=Border.VERTICAL, style="class:line,vertical-line", width=1 | |
| ) | |
| def __pt_container__(self) -> Container: | |
| return self.window | |
| class HorizontalLine: | |
| """ | |
| A simple horizontal line with a height of 1. | |
| """ | |
| def __init__(self) -> None: | |
| self.window = Window( | |
| char=Border.HORIZONTAL, style="class:line,horizontal-line", height=1 | |
| ) | |
| def __pt_container__(self) -> Container: | |
| return self.window | |
| class ProgressBar: | |
| def __init__(self) -> None: | |
| self._percentage = 60 | |
| self.label = Label("60%") | |
| self.container = FloatContainer( | |
| content=Window(height=1), | |
| floats=[ | |
| # We first draw the label, then the actual progress bar. Right | |
| # now, this is the only way to have the colors of the progress | |
| # bar appear on top of the label. The problem is that our label | |
| # can't be part of any `Window` below. | |
| Float(content=self.label, top=0, bottom=0), | |
| Float( | |
| left=0, | |
| top=0, | |
| right=0, | |
| bottom=0, | |
| content=VSplit( | |
| [ | |
| Window( | |
| style="class:progress-bar.used", | |
| width=lambda: D(weight=int(self._percentage)), | |
| ), | |
| Window( | |
| style="class:progress-bar", | |
| width=lambda: D(weight=int(100 - self._percentage)), | |
| ), | |
| ] | |
| ), | |
| ), | |
| ], | |
| ) | |
| def percentage(self) -> int: | |
| return self._percentage | |
| def percentage(self, value: int) -> None: | |
| self._percentage = value | |
| self.label.text = f"{value}%" | |
| def __pt_container__(self) -> Container: | |
| return self.container | |