diff options
| author | shadchin <[email protected]> | 2022-02-10 16:44:39 +0300 | 
|---|---|---|
| committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:44:39 +0300 | 
| commit | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (patch) | |
| tree | 64175d5cadab313b3e7039ebaa06c5bc3295e274 /contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets | |
| parent | 2598ef1d0aee359b4b6d5fdd1758916d5907d04f (diff) | |
Restoring authorship annotation for <[email protected]>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets')
5 files changed, 1861 insertions, 1861 deletions
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/__init__.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/__init__.py index a7559ebd0fd..552d3559488 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/__init__.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/__init__.py @@ -1,60 +1,60 @@ -"""  -Collection of reusable components for building full screen applications.  -These are higher level abstractions on top of the `prompt_toolkit.layout`  -module.  -  -Most of these widgets implement the ``__pt_container__`` method, which makes it  -possible to embed these in the layout like any other container.  -"""  -from .base import (  -    Box,  -    Button,  -    Checkbox,  -    CheckboxList,  -    Frame,  -    HorizontalLine,  -    Label,  -    ProgressBar,  -    RadioList,  -    Shadow,  -    TextArea,  -    VerticalLine,  -)  -from .dialogs import Dialog  -from .menus import MenuContainer, MenuItem  -from .toolbars import (  -    ArgToolbar,  -    CompletionsToolbar,  -    FormattedTextToolbar,  -    SearchToolbar,  -    SystemToolbar,  -    ValidationToolbar,  -)  -  -__all__ = [  -    # Base.  -    "TextArea",  -    "Label",  -    "Button",  -    "Frame",  -    "Shadow",  -    "Box",  -    "VerticalLine",  -    "HorizontalLine",  -    "CheckboxList",  -    "RadioList",  -    "Checkbox",  -    "ProgressBar",  -    # Toolbars.  -    "ArgToolbar",  -    "CompletionsToolbar",  -    "FormattedTextToolbar",  -    "SearchToolbar",  -    "SystemToolbar",  -    "ValidationToolbar",  -    # Dialogs.  -    "Dialog",  -    # Menus.  -    "MenuContainer",  -    "MenuItem",  -]  +""" +Collection of reusable components for building full screen applications. +These are higher level abstractions on top of the `prompt_toolkit.layout` +module. + +Most of these widgets implement the ``__pt_container__`` method, which makes it +possible to embed these in the layout like any other container. +""" +from .base import ( +    Box, +    Button, +    Checkbox, +    CheckboxList, +    Frame, +    HorizontalLine, +    Label, +    ProgressBar, +    RadioList, +    Shadow, +    TextArea, +    VerticalLine, +) +from .dialogs import Dialog +from .menus import MenuContainer, MenuItem +from .toolbars import ( +    ArgToolbar, +    CompletionsToolbar, +    FormattedTextToolbar, +    SearchToolbar, +    SystemToolbar, +    ValidationToolbar, +) + +__all__ = [ +    # Base. +    "TextArea", +    "Label", +    "Button", +    "Frame", +    "Shadow", +    "Box", +    "VerticalLine", +    "HorizontalLine", +    "CheckboxList", +    "RadioList", +    "Checkbox", +    "ProgressBar", +    # Toolbars. +    "ArgToolbar", +    "CompletionsToolbar", +    "FormattedTextToolbar", +    "SearchToolbar", +    "SystemToolbar", +    "ValidationToolbar", +    # Dialogs. +    "Dialog", +    # Menus. +    "MenuContainer", +    "MenuItem", +] diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/base.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/base.py index c059872793d..728190b54c2 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/base.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/base.py @@ -1,949 +1,949 @@ -"""  -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 functools import partial  -from typing import Callable, Generic, List, Optional, Sequence, Tuple, TypeVar, Union  -  -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.dimension import to_dimension  -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: Optional[Lexer] = None,  -        auto_suggest: Optional[AutoSuggest] = None,  -        completer: Optional[Completer] = None,  -        complete_while_typing: FilterOrBool = True,  -        validator: Optional[Validator] = None,  -        accept_handler: Optional[BufferAcceptHandler] = None,  -        history: Optional[History] = 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: Optional[GetLinePrefixCallable] = None,  -        scrollbar: bool = False,  -        style: str = "",  -        search_field: Optional[SearchToolbar] = None,  -        preview_search: FilterOrBool = True,  -        prompt: AnyFormattedText = "",  -        input_processors: Optional[List[Processor]] = None,  -    ) -> 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,  -        )  -  -        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,  -        )  -  -    @property  -    def text(self) -> str:  -        """  -        The `Buffer` text.  -        """  -        return self.buffer.text  -  -    @text.setter  -    def text(self, value: str) -> None:  -        self.document = Document(value, 0)  -  -    @property  -    def document(self) -> Document:  -        """  -        The `Buffer` document (text + cursor position).  -        """  -        return self.buffer.document  -  -    @document.setter  -    def document(self, value: Document) -> None:  -        self.buffer.set_document(value, bypass_readonly=True)  -  -    @property  -    def accept_handler(self) -> Optional[BufferAcceptHandler]:  -        """  -        The accept handler. Called when the user accepts the input.  -        """  -        return self.buffer.accept_handler  -  -    @accept_handler.setter  -    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,  +""" +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 functools import partial +from typing import Callable, Generic, List, Optional, Sequence, Tuple, TypeVar, Union + +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.dimension import to_dimension +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: Optional[Lexer] = None, +        auto_suggest: Optional[AutoSuggest] = None, +        completer: Optional[Completer] = None, +        complete_while_typing: FilterOrBool = True, +        validator: Optional[Validator] = None, +        accept_handler: Optional[BufferAcceptHandler] = None, +        history: Optional[History] = 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: Optional[GetLinePrefixCallable] = None, +        scrollbar: bool = False, +        style: str = "", +        search_field: Optional[SearchToolbar] = None, +        preview_search: FilterOrBool = True, +        prompt: AnyFormattedText = "", +        input_processors: Optional[List[Processor]] = None, +    ) -> 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, +        ) + +        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, +        ) + +    @property +    def text(self) -> str: +        """ +        The `Buffer` text. +        """ +        return self.buffer.text + +    @text.setter +    def text(self, value: str) -> None: +        self.document = Document(value, 0) + +    @property +    def document(self) -> Document: +        """ +        The `Buffer` document (text + cursor position). +        """ +        return self.buffer.document + +    @document.setter +    def document(self, value: Document) -> None: +        self.buffer.set_document(value, bypass_readonly=True) + +    @property +    def accept_handler(self) -> Optional[BufferAcceptHandler]: +        """ +        The accept handler. Called when the user accepts the input. +        """ +        return self.buffer.accept_handler + +    @accept_handler.setter +    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: Union[WindowAlign, Callable[[], WindowAlign]] = WindowAlign.LEFT, -    ) -> 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,  +    ) -> 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, -        )  -  -    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: Optional[Callable[[], 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 = ("{{:^{}}}".format(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()  -  -        @kb.add(" ")  -        @kb.add("enter")  -        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: Optional[KeyBindings] = 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,  -        )  -  -        @Condition  -        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: Union[None, str, Callable[[], str]] = None,  -        modal: bool = False,  -        key_bindings: Optional[KeyBindings] = None,  -    ) -> None:  -  -        if padding is None:  -            padding = D(preferred=0)  -  -        def get(value: AnyDimension) -> D:  -            if value is None:  -                value = padding  -            return to_dimension(value)  -  -        self.padding_left = get(padding_left)  -        self.padding_right = get(padding_right)  -        self.padding_top = get(padding_top)  -        self.padding_bottom = get(padding_bottom)  -        self.body = body  -  -        self.container = HSplit(  -            [  -                Window(height=self.padding_top, char=char),  -                VSplit(  -                    [  -                        Window(width=self.padding_left, char=char),  -                        body,  -                        Window(width=self.padding_right, char=char),  -                    ]  -                ),  -                Window(height=self.padding_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]]) -> None:  -        assert len(values) > 0  -  -        self.values = values  -        # current_values will be used in multiple_selection,  -        # current_value will be used otherwise.  -        self.current_values: List[_T] = []  -        self.current_value: _T = values[0][0]  -        self._selected_index = 0  -  -        # Key bindings.  -        kb = KeyBindings()  -  -        @kb.add("up")  -        def _up(event: E) -> None:  -            self._selected_index = max(0, self._selected_index - 1)  -  -        @kb.add("down")  -        def _down(event: E) -> None:  -            self._selected_index = min(len(self.values) - 1, self._selected_index + 1)  -  -        @kb.add("pageup")  -        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)  -                )  -  -        @kb.add("pagedown")  -        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),  -                )  -  -        @kb.add("enter")  -        @kb.add(" ")  -        def _click(event: E) -> None:  -            self._handle_enter()  -  -        @kb.add(Keys.Any)  -        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  -  -  -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)]  -        CheckboxList.__init__(self, values)  -        self.checked = checked  -  -    @property  -    def checked(self) -> bool:  -        return "value" in self.current_values  -  -    @checked.setter  -    def checked(self, value: bool) -> None:  -        if value:  -            self.current_values = ["value"]  -        else:  -            self.current_values = []  -  -  -class VerticalLine(object):  -    """  -    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)),  -                            ),  -                        ]  -                    ),  -                ),  -            ],  -        )  -  -    @property  -    def percentage(self) -> int:  -        return self._percentage  -  -    @percentage.setter  -    def percentage(self, value: int) -> None:  -        self._percentage = value  -        self.label.text = "{0}%".format(value)  -  -    def __pt_container__(self) -> Container:  -        return self.container  +        ) + +    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: Optional[Callable[[], 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 = ("{{:^{}}}".format(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() + +        @kb.add(" ") +        @kb.add("enter") +        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: Optional[KeyBindings] = 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, +        ) + +        @Condition +        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: Union[None, str, Callable[[], str]] = None, +        modal: bool = False, +        key_bindings: Optional[KeyBindings] = None, +    ) -> None: + +        if padding is None: +            padding = D(preferred=0) + +        def get(value: AnyDimension) -> D: +            if value is None: +                value = padding +            return to_dimension(value) + +        self.padding_left = get(padding_left) +        self.padding_right = get(padding_right) +        self.padding_top = get(padding_top) +        self.padding_bottom = get(padding_bottom) +        self.body = body + +        self.container = HSplit( +            [ +                Window(height=self.padding_top, char=char), +                VSplit( +                    [ +                        Window(width=self.padding_left, char=char), +                        body, +                        Window(width=self.padding_right, char=char), +                    ] +                ), +                Window(height=self.padding_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]]) -> None: +        assert len(values) > 0 + +        self.values = values +        # current_values will be used in multiple_selection, +        # current_value will be used otherwise. +        self.current_values: List[_T] = [] +        self.current_value: _T = values[0][0] +        self._selected_index = 0 + +        # Key bindings. +        kb = KeyBindings() + +        @kb.add("up") +        def _up(event: E) -> None: +            self._selected_index = max(0, self._selected_index - 1) + +        @kb.add("down") +        def _down(event: E) -> None: +            self._selected_index = min(len(self.values) - 1, self._selected_index + 1) + +        @kb.add("pageup") +        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) +                ) + +        @kb.add("pagedown") +        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), +                ) + +        @kb.add("enter") +        @kb.add(" ") +        def _click(event: E) -> None: +            self._handle_enter() + +        @kb.add(Keys.Any) +        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 + + +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)] +        CheckboxList.__init__(self, values) +        self.checked = checked + +    @property +    def checked(self) -> bool: +        return "value" in self.current_values + +    @checked.setter +    def checked(self, value: bool) -> None: +        if value: +            self.current_values = ["value"] +        else: +            self.current_values = [] + + +class VerticalLine(object): +    """ +    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)), +                            ), +                        ] +                    ), +                ), +            ], +        ) + +    @property +    def percentage(self) -> int: +        return self._percentage + +    @percentage.setter +    def percentage(self, value: int) -> None: +        self._percentage = value +        self.label.text = "{0}%".format(value) + +    def __pt_container__(self) -> Container: +        return self.container diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/dialogs.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/dialogs.py index d814d275eac..920582b4e68 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/dialogs.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/dialogs.py @@ -1,106 +1,106 @@ -"""  -Collection of reusable components for building full screen applications.  -"""  -from typing import Optional, Sequence, Union  -  -from prompt_toolkit.filters import has_completions, has_focus  -from prompt_toolkit.formatted_text import AnyFormattedText  -from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous  -from prompt_toolkit.key_binding.key_bindings import KeyBindings  -from prompt_toolkit.layout.containers import (  -    AnyContainer,  -    DynamicContainer,  -    HSplit,  -    VSplit,  -)  -from prompt_toolkit.layout.dimension import AnyDimension  -from prompt_toolkit.layout.dimension import Dimension as D  -  -from .base import Box, Button, Frame, Shadow  -  -__all__ = [  -    "Dialog",  -]  -  -  -class Dialog:  -    """  -    Simple dialog window. This is the base for input dialogs, message dialogs  -    and confirmation dialogs.  -  -    Changing the title and body of the dialog is possible at runtime by  -    assigning to the `body` and `title` attributes of this class.  -  -    :param body: Child container object.  -    :param title: Text to be displayed in the heading of the dialog.  -    :param buttons: A list of `Button` widgets, displayed at the bottom.  -    """  -  -    def __init__(  -        self,  -        body: AnyContainer,  -        title: AnyFormattedText = "",  -        buttons: Optional[Sequence[Button]] = None,  -        modal: bool = True,  -        width: AnyDimension = None,  -        with_background: bool = False,  -    ) -> None:  -  -        self.body = body  -        self.title = title  -  -        buttons = buttons or []  -  -        # When a button is selected, handle left/right key bindings.  -        buttons_kb = KeyBindings()  -        if len(buttons) > 1:  -            first_selected = has_focus(buttons[0])  -            last_selected = has_focus(buttons[-1])  -  -            buttons_kb.add("left", filter=~first_selected)(focus_previous)  -            buttons_kb.add("right", filter=~last_selected)(focus_next)  -  -        frame_body: AnyContainer  -        if buttons:  -            frame_body = HSplit(  -                [  -                    # Add optional padding around the body.  -                    Box(  -                        body=DynamicContainer(lambda: self.body),  -                        padding=D(preferred=1, max=1),  -                        padding_bottom=0,  -                    ),  -                    # The buttons.  -                    Box(  -                        body=VSplit(buttons, padding=1, key_bindings=buttons_kb),  -                        height=D(min=1, max=3, preferred=3),  -                    ),  -                ]  -            )  -        else:  -            frame_body = body  -  -        # Key bindings for whole dialog.  -        kb = KeyBindings()  -        kb.add("tab", filter=~has_completions)(focus_next)  -        kb.add("s-tab", filter=~has_completions)(focus_previous)  -  -        frame = Shadow(  -            body=Frame(  -                title=lambda: self.title,  -                body=frame_body,  -                style="class:dialog.body",  -                width=(None if with_background is None else width),  -                key_bindings=kb,  -                modal=modal,  -            )  -        )  -  -        self.container: Union[Box, Shadow]  -        if with_background:  -            self.container = Box(body=frame, style="class:dialog", width=width)  -        else:  -            self.container = frame  -  -    def __pt_container__(self) -> AnyContainer:  -        return self.container  +""" +Collection of reusable components for building full screen applications. +""" +from typing import Optional, Sequence, Union + +from prompt_toolkit.filters import has_completions, has_focus +from prompt_toolkit.formatted_text import AnyFormattedText +from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous +from prompt_toolkit.key_binding.key_bindings import KeyBindings +from prompt_toolkit.layout.containers import ( +    AnyContainer, +    DynamicContainer, +    HSplit, +    VSplit, +) +from prompt_toolkit.layout.dimension import AnyDimension +from prompt_toolkit.layout.dimension import Dimension as D + +from .base import Box, Button, Frame, Shadow + +__all__ = [ +    "Dialog", +] + + +class Dialog: +    """ +    Simple dialog window. This is the base for input dialogs, message dialogs +    and confirmation dialogs. + +    Changing the title and body of the dialog is possible at runtime by +    assigning to the `body` and `title` attributes of this class. + +    :param body: Child container object. +    :param title: Text to be displayed in the heading of the dialog. +    :param buttons: A list of `Button` widgets, displayed at the bottom. +    """ + +    def __init__( +        self, +        body: AnyContainer, +        title: AnyFormattedText = "", +        buttons: Optional[Sequence[Button]] = None, +        modal: bool = True, +        width: AnyDimension = None, +        with_background: bool = False, +    ) -> None: + +        self.body = body +        self.title = title + +        buttons = buttons or [] + +        # When a button is selected, handle left/right key bindings. +        buttons_kb = KeyBindings() +        if len(buttons) > 1: +            first_selected = has_focus(buttons[0]) +            last_selected = has_focus(buttons[-1]) + +            buttons_kb.add("left", filter=~first_selected)(focus_previous) +            buttons_kb.add("right", filter=~last_selected)(focus_next) + +        frame_body: AnyContainer +        if buttons: +            frame_body = HSplit( +                [ +                    # Add optional padding around the body. +                    Box( +                        body=DynamicContainer(lambda: self.body), +                        padding=D(preferred=1, max=1), +                        padding_bottom=0, +                    ), +                    # The buttons. +                    Box( +                        body=VSplit(buttons, padding=1, key_bindings=buttons_kb), +                        height=D(min=1, max=3, preferred=3), +                    ), +                ] +            ) +        else: +            frame_body = body + +        # Key bindings for whole dialog. +        kb = KeyBindings() +        kb.add("tab", filter=~has_completions)(focus_next) +        kb.add("s-tab", filter=~has_completions)(focus_previous) + +        frame = Shadow( +            body=Frame( +                title=lambda: self.title, +                body=frame_body, +                style="class:dialog.body", +                width=(None if with_background is None else width), +                key_bindings=kb, +                modal=modal, +            ) +        ) + +        self.container: Union[Box, Shadow] +        if with_background: +            self.container = Box(body=frame, style="class:dialog", width=width) +        else: +            self.container = frame + +    def __pt_container__(self) -> AnyContainer: +        return self.container diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/menus.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/menus.py index f4d91067c96..7203aae1181 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/menus.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/menus.py @@ -1,374 +1,374 @@ -from typing import Callable, Iterable, List, Optional, Sequence, Union  -  -from prompt_toolkit.application.current import get_app  -from prompt_toolkit.filters import Condition  -from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple, StyleAndTextTuples  -from prompt_toolkit.key_binding.key_bindings import KeyBindings, KeyBindingsBase  -from prompt_toolkit.key_binding.key_processor import KeyPressEvent  -from prompt_toolkit.keys import Keys  -from prompt_toolkit.layout.containers import (  -    AnyContainer,  -    ConditionalContainer,  -    Container,  -    Float,  -    FloatContainer,  -    HSplit,  -    Window,  -)  -from prompt_toolkit.layout.controls import FormattedTextControl  -from prompt_toolkit.mouse_events import MouseEvent, MouseEventType  -from prompt_toolkit.utils import get_cwidth  -from prompt_toolkit.widgets import Shadow  -  -from .base import Border  -  -__all__ = [  -    "MenuContainer",  -    "MenuItem",  -]  -  -E = KeyPressEvent  -  -  -class MenuContainer:  -    """  -    :param floats: List of extra Float objects to display.  -    :param menu_items: List of `MenuItem` objects.  -    """  -  -    def __init__(  -        self,  -        body: AnyContainer,  -        menu_items: List["MenuItem"],  -        floats: Optional[List[Float]] = None,  -        key_bindings: Optional[KeyBindingsBase] = None,  -    ) -> None:  -  -        self.body = body  -        self.menu_items = menu_items  -        self.selected_menu = [0]  -  -        # Key bindings.  -        kb = KeyBindings()  -  -        @Condition  -        def in_main_menu() -> bool:  -            return len(self.selected_menu) == 1  -  -        @Condition  -        def in_sub_menu() -> bool:  -            return len(self.selected_menu) > 1  -  -        # Navigation through the main menu.  -  -        @kb.add("left", filter=in_main_menu)  -        def _left(event: E) -> None:  -            self.selected_menu[0] = max(0, self.selected_menu[0] - 1)  -  -        @kb.add("right", filter=in_main_menu)  -        def _right(event: E) -> None:  -            self.selected_menu[0] = min(  -                len(self.menu_items) - 1, self.selected_menu[0] + 1  -            )  -  -        @kb.add("down", filter=in_main_menu)  -        def _down(event: E) -> None:  -            self.selected_menu.append(0)  -  -        @kb.add("c-c", filter=in_main_menu)  -        @kb.add("c-g", filter=in_main_menu)  -        def _cancel(event: E) -> None:  -            "Leave menu."  -            event.app.layout.focus_last()  -  -        # Sub menu navigation.  -  -        @kb.add("left", filter=in_sub_menu)  -        @kb.add("c-g", filter=in_sub_menu)  -        @kb.add("c-c", filter=in_sub_menu)  -        def _back(event: E) -> None:  -            "Go back to parent menu."  -            if len(self.selected_menu) > 1:  -                self.selected_menu.pop()  -  -        @kb.add("right", filter=in_sub_menu)  -        def _submenu(event: E) -> None:  -            "go into sub menu."  -            if self._get_menu(len(self.selected_menu) - 1).children:  -                self.selected_menu.append(0)  -  -            # If This item does not have a sub menu. Go up in the parent menu.  -            elif (  -                len(self.selected_menu) == 2  -                and self.selected_menu[0] < len(self.menu_items) - 1  -            ):  -                self.selected_menu = [  -                    min(len(self.menu_items) - 1, self.selected_menu[0] + 1)  -                ]  -                if self.menu_items[self.selected_menu[0]].children:  -                    self.selected_menu.append(0)  -  -        @kb.add("up", filter=in_sub_menu)  -        def _up_in_submenu(event: E) -> None:  -            "Select previous (enabled) menu item or return to main menu."  -            # Look for previous enabled items in this sub menu.  -            menu = self._get_menu(len(self.selected_menu) - 2)  -            index = self.selected_menu[-1]  -  -            previous_indexes = [  -                i  -                for i, item in enumerate(menu.children)  -                if i < index and not item.disabled  -            ]  -  -            if previous_indexes:  -                self.selected_menu[-1] = previous_indexes[-1]  -            elif len(self.selected_menu) == 2:  -                # Return to main menu.  -                self.selected_menu.pop()  -  -        @kb.add("down", filter=in_sub_menu)  -        def _down_in_submenu(event: E) -> None:  -            "Select next (enabled) menu item."  -            menu = self._get_menu(len(self.selected_menu) - 2)  -            index = self.selected_menu[-1]  -  -            next_indexes = [  -                i  -                for i, item in enumerate(menu.children)  -                if i > index and not item.disabled  -            ]  -  -            if next_indexes:  -                self.selected_menu[-1] = next_indexes[0]  -  -        @kb.add("enter")  -        def _click(event: E) -> None:  -            "Click the selected menu item."  -            item = self._get_menu(len(self.selected_menu) - 1)  -            if item.handler:  -                event.app.layout.focus_last()  -                item.handler()  -  -        # Controls.  -        self.control = FormattedTextControl(  -            self._get_menu_fragments, key_bindings=kb, focusable=True, show_cursor=False  -        )  -  -        self.window = Window(height=1, content=self.control, style="class:menu-bar")  -  -        submenu = self._submenu(0)  -        submenu2 = self._submenu(1)  -        submenu3 = self._submenu(2)  -  -        @Condition  -        def has_focus() -> bool:  -            return get_app().layout.current_window == self.window  -  -        self.container = FloatContainer(  -            content=HSplit(  -                [  -                    # The titlebar.  -                    self.window,  -                    # The 'body', like defined above.  -                    body,  -                ]  -            ),  -            floats=[  -                Float(  -                    xcursor=True,  -                    ycursor=True,  -                    content=ConditionalContainer(  -                        content=Shadow(body=submenu), filter=has_focus  -                    ),  -                ),  -                Float(  -                    attach_to_window=submenu,  -                    xcursor=True,  -                    ycursor=True,  -                    allow_cover_cursor=True,  -                    content=ConditionalContainer(  -                        content=Shadow(body=submenu2),  -                        filter=has_focus  -                        & Condition(lambda: len(self.selected_menu) >= 1),  -                    ),  -                ),  -                Float(  -                    attach_to_window=submenu2,  -                    xcursor=True,  -                    ycursor=True,  -                    allow_cover_cursor=True,  -                    content=ConditionalContainer(  -                        content=Shadow(body=submenu3),  -                        filter=has_focus  -                        & Condition(lambda: len(self.selected_menu) >= 2),  -                    ),  -                ),  -                # --  -            ]  -            + (floats or []),  -            key_bindings=key_bindings,  -        )  -  -    def _get_menu(self, level: int) -> "MenuItem":  -        menu = self.menu_items[self.selected_menu[0]]  -  -        for i, index in enumerate(self.selected_menu[1:]):  -            if i < level:  -                try:  -                    menu = menu.children[index]  -                except IndexError:  -                    return MenuItem("debug")  -  -        return menu  -  -    def _get_menu_fragments(self) -> StyleAndTextTuples:  -        focused = get_app().layout.has_focus(self.window)  -  -        # This is called during the rendering. When we discover that this  -        # widget doesn't have the focus anymore. Reset menu state.  -        if not focused:  -            self.selected_menu = [0]  -  -        # Generate text fragments for the main menu.  -        def one_item(i: int, item: MenuItem) -> Iterable[OneStyleAndTextTuple]:  -            def mouse_handler(mouse_event: MouseEvent) -> None:  -                hover = mouse_event.event_type == MouseEventType.MOUSE_MOVE  -                if (  -                    mouse_event.event_type == MouseEventType.MOUSE_DOWN  -                    or hover  -                    and focused  -                ):  -                    # Toggle focus.  -                    app = get_app()  -                    if not hover:  -                        if app.layout.has_focus(self.window):  -                            if self.selected_menu == [i]:  -                                app.layout.focus_last()  -                        else:  -                            app.layout.focus(self.window)  -                    self.selected_menu = [i]  -  -            yield ("class:menu-bar", " ", mouse_handler)  -            if i == self.selected_menu[0] and focused:  -                yield ("[SetMenuPosition]", "", mouse_handler)  -                style = "class:menu-bar.selected-item"  -            else:  -                style = "class:menu-bar"  -            yield style, item.text, mouse_handler  -  -        result: StyleAndTextTuples = []  -        for i, item in enumerate(self.menu_items):  -            result.extend(one_item(i, item))  -  -        return result  -  -    def _submenu(self, level: int = 0) -> Window:  -        def get_text_fragments() -> StyleAndTextTuples:  -            result: StyleAndTextTuples = []  -            if level < len(self.selected_menu):  -                menu = self._get_menu(level)  -                if menu.children:  -                    result.append(("class:menu", Border.TOP_LEFT))  -                    result.append(("class:menu", Border.HORIZONTAL * (menu.width + 4)))  -                    result.append(("class:menu", Border.TOP_RIGHT))  -                    result.append(("", "\n"))  -                    try:  -                        selected_item = self.selected_menu[level + 1]  -                    except IndexError:  -                        selected_item = -1  -  -                    def one_item(  -                        i: int, item: MenuItem  -                    ) -> Iterable[OneStyleAndTextTuple]:  -                        def mouse_handler(mouse_event: MouseEvent) -> None:  -                            if item.disabled:  -                                # The arrow keys can't interact with menu items that are disabled.  -                                # The mouse shouldn't be able to either.  -                                return  -                            hover = mouse_event.event_type == MouseEventType.MOUSE_MOVE  -                            if (  -                                mouse_event.event_type == MouseEventType.MOUSE_UP  -                                or hover  -                            ):  -                                app = get_app()  -                                if not hover and item.handler:  -                                    app.layout.focus_last()  -                                    item.handler()  -                                else:  -                                    self.selected_menu = self.selected_menu[  -                                        : level + 1  -                                    ] + [i]  -  -                        if i == selected_item:  -                            yield ("[SetCursorPosition]", "")  -                            style = "class:menu-bar.selected-item"  -                        else:  -                            style = ""  -  -                        yield ("class:menu", Border.VERTICAL)  -                        if item.text == "-":  -                            yield (  -                                style + "class:menu-border",  -                                "{}".format(Border.HORIZONTAL * (menu.width + 3)),  -                                mouse_handler,  -                            )  -                        else:  -                            yield (  -                                style,  -                                " {}".format(item.text).ljust(menu.width + 3),  -                                mouse_handler,  -                            )  -  -                        if item.children:  -                            yield (style, ">", mouse_handler)  -                        else:  -                            yield (style, " ", mouse_handler)  -  -                        if i == selected_item:  -                            yield ("[SetMenuPosition]", "")  -                        yield ("class:menu", Border.VERTICAL)  -  -                        yield ("", "\n")  -  -                    for i, item in enumerate(menu.children):  -                        result.extend(one_item(i, item))  -  -                    result.append(("class:menu", Border.BOTTOM_LEFT))  -                    result.append(("class:menu", Border.HORIZONTAL * (menu.width + 4)))  -                    result.append(("class:menu", Border.BOTTOM_RIGHT))  -            return result  -  -        return Window(FormattedTextControl(get_text_fragments), style="class:menu")  -  -    @property  -    def floats(self) -> Optional[List[Float]]:  -        return self.container.floats  -  -    def __pt_container__(self) -> Container:  -        return self.container  -  -  -class MenuItem:  -    def __init__(  -        self,  -        text: str = "",  -        handler: Optional[Callable[[], None]] = None,  -        children: Optional[List["MenuItem"]] = None,  -        shortcut: Optional[Sequence[Union[Keys, str]]] = None,  -        disabled: bool = False,  -    ) -> None:  -  -        self.text = text  -        self.handler = handler  -        self.children = children or []  -        self.shortcut = shortcut  -        self.disabled = disabled  -        self.selected_item = 0  -  -    @property  -    def width(self) -> int:  -        if self.children:  -            return max(get_cwidth(c.text) for c in self.children)  -        else:  -            return 0  +from typing import Callable, Iterable, List, Optional, Sequence, Union + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.filters import Condition +from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple, StyleAndTextTuples +from prompt_toolkit.key_binding.key_bindings import KeyBindings, KeyBindingsBase +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.keys import Keys +from prompt_toolkit.layout.containers import ( +    AnyContainer, +    ConditionalContainer, +    Container, +    Float, +    FloatContainer, +    HSplit, +    Window, +) +from prompt_toolkit.layout.controls import FormattedTextControl +from prompt_toolkit.mouse_events import MouseEvent, MouseEventType +from prompt_toolkit.utils import get_cwidth +from prompt_toolkit.widgets import Shadow + +from .base import Border + +__all__ = [ +    "MenuContainer", +    "MenuItem", +] + +E = KeyPressEvent + + +class MenuContainer: +    """ +    :param floats: List of extra Float objects to display. +    :param menu_items: List of `MenuItem` objects. +    """ + +    def __init__( +        self, +        body: AnyContainer, +        menu_items: List["MenuItem"], +        floats: Optional[List[Float]] = None, +        key_bindings: Optional[KeyBindingsBase] = None, +    ) -> None: + +        self.body = body +        self.menu_items = menu_items +        self.selected_menu = [0] + +        # Key bindings. +        kb = KeyBindings() + +        @Condition +        def in_main_menu() -> bool: +            return len(self.selected_menu) == 1 + +        @Condition +        def in_sub_menu() -> bool: +            return len(self.selected_menu) > 1 + +        # Navigation through the main menu. + +        @kb.add("left", filter=in_main_menu) +        def _left(event: E) -> None: +            self.selected_menu[0] = max(0, self.selected_menu[0] - 1) + +        @kb.add("right", filter=in_main_menu) +        def _right(event: E) -> None: +            self.selected_menu[0] = min( +                len(self.menu_items) - 1, self.selected_menu[0] + 1 +            ) + +        @kb.add("down", filter=in_main_menu) +        def _down(event: E) -> None: +            self.selected_menu.append(0) + +        @kb.add("c-c", filter=in_main_menu) +        @kb.add("c-g", filter=in_main_menu) +        def _cancel(event: E) -> None: +            "Leave menu." +            event.app.layout.focus_last() + +        # Sub menu navigation. + +        @kb.add("left", filter=in_sub_menu) +        @kb.add("c-g", filter=in_sub_menu) +        @kb.add("c-c", filter=in_sub_menu) +        def _back(event: E) -> None: +            "Go back to parent menu." +            if len(self.selected_menu) > 1: +                self.selected_menu.pop() + +        @kb.add("right", filter=in_sub_menu) +        def _submenu(event: E) -> None: +            "go into sub menu." +            if self._get_menu(len(self.selected_menu) - 1).children: +                self.selected_menu.append(0) + +            # If This item does not have a sub menu. Go up in the parent menu. +            elif ( +                len(self.selected_menu) == 2 +                and self.selected_menu[0] < len(self.menu_items) - 1 +            ): +                self.selected_menu = [ +                    min(len(self.menu_items) - 1, self.selected_menu[0] + 1) +                ] +                if self.menu_items[self.selected_menu[0]].children: +                    self.selected_menu.append(0) + +        @kb.add("up", filter=in_sub_menu) +        def _up_in_submenu(event: E) -> None: +            "Select previous (enabled) menu item or return to main menu." +            # Look for previous enabled items in this sub menu. +            menu = self._get_menu(len(self.selected_menu) - 2) +            index = self.selected_menu[-1] + +            previous_indexes = [ +                i +                for i, item in enumerate(menu.children) +                if i < index and not item.disabled +            ] + +            if previous_indexes: +                self.selected_menu[-1] = previous_indexes[-1] +            elif len(self.selected_menu) == 2: +                # Return to main menu. +                self.selected_menu.pop() + +        @kb.add("down", filter=in_sub_menu) +        def _down_in_submenu(event: E) -> None: +            "Select next (enabled) menu item." +            menu = self._get_menu(len(self.selected_menu) - 2) +            index = self.selected_menu[-1] + +            next_indexes = [ +                i +                for i, item in enumerate(menu.children) +                if i > index and not item.disabled +            ] + +            if next_indexes: +                self.selected_menu[-1] = next_indexes[0] + +        @kb.add("enter") +        def _click(event: E) -> None: +            "Click the selected menu item." +            item = self._get_menu(len(self.selected_menu) - 1) +            if item.handler: +                event.app.layout.focus_last() +                item.handler() + +        # Controls. +        self.control = FormattedTextControl( +            self._get_menu_fragments, key_bindings=kb, focusable=True, show_cursor=False +        ) + +        self.window = Window(height=1, content=self.control, style="class:menu-bar") + +        submenu = self._submenu(0) +        submenu2 = self._submenu(1) +        submenu3 = self._submenu(2) + +        @Condition +        def has_focus() -> bool: +            return get_app().layout.current_window == self.window + +        self.container = FloatContainer( +            content=HSplit( +                [ +                    # The titlebar. +                    self.window, +                    # The 'body', like defined above. +                    body, +                ] +            ), +            floats=[ +                Float( +                    xcursor=True, +                    ycursor=True, +                    content=ConditionalContainer( +                        content=Shadow(body=submenu), filter=has_focus +                    ), +                ), +                Float( +                    attach_to_window=submenu, +                    xcursor=True, +                    ycursor=True, +                    allow_cover_cursor=True, +                    content=ConditionalContainer( +                        content=Shadow(body=submenu2), +                        filter=has_focus +                        & Condition(lambda: len(self.selected_menu) >= 1), +                    ), +                ), +                Float( +                    attach_to_window=submenu2, +                    xcursor=True, +                    ycursor=True, +                    allow_cover_cursor=True, +                    content=ConditionalContainer( +                        content=Shadow(body=submenu3), +                        filter=has_focus +                        & Condition(lambda: len(self.selected_menu) >= 2), +                    ), +                ), +                # -- +            ] +            + (floats or []), +            key_bindings=key_bindings, +        ) + +    def _get_menu(self, level: int) -> "MenuItem": +        menu = self.menu_items[self.selected_menu[0]] + +        for i, index in enumerate(self.selected_menu[1:]): +            if i < level: +                try: +                    menu = menu.children[index] +                except IndexError: +                    return MenuItem("debug") + +        return menu + +    def _get_menu_fragments(self) -> StyleAndTextTuples: +        focused = get_app().layout.has_focus(self.window) + +        # This is called during the rendering. When we discover that this +        # widget doesn't have the focus anymore. Reset menu state. +        if not focused: +            self.selected_menu = [0] + +        # Generate text fragments for the main menu. +        def one_item(i: int, item: MenuItem) -> Iterable[OneStyleAndTextTuple]: +            def mouse_handler(mouse_event: MouseEvent) -> None: +                hover = mouse_event.event_type == MouseEventType.MOUSE_MOVE +                if ( +                    mouse_event.event_type == MouseEventType.MOUSE_DOWN +                    or hover +                    and focused +                ): +                    # Toggle focus. +                    app = get_app() +                    if not hover: +                        if app.layout.has_focus(self.window): +                            if self.selected_menu == [i]: +                                app.layout.focus_last() +                        else: +                            app.layout.focus(self.window) +                    self.selected_menu = [i] + +            yield ("class:menu-bar", " ", mouse_handler) +            if i == self.selected_menu[0] and focused: +                yield ("[SetMenuPosition]", "", mouse_handler) +                style = "class:menu-bar.selected-item" +            else: +                style = "class:menu-bar" +            yield style, item.text, mouse_handler + +        result: StyleAndTextTuples = [] +        for i, item in enumerate(self.menu_items): +            result.extend(one_item(i, item)) + +        return result + +    def _submenu(self, level: int = 0) -> Window: +        def get_text_fragments() -> StyleAndTextTuples: +            result: StyleAndTextTuples = [] +            if level < len(self.selected_menu): +                menu = self._get_menu(level) +                if menu.children: +                    result.append(("class:menu", Border.TOP_LEFT)) +                    result.append(("class:menu", Border.HORIZONTAL * (menu.width + 4))) +                    result.append(("class:menu", Border.TOP_RIGHT)) +                    result.append(("", "\n")) +                    try: +                        selected_item = self.selected_menu[level + 1] +                    except IndexError: +                        selected_item = -1 + +                    def one_item( +                        i: int, item: MenuItem +                    ) -> Iterable[OneStyleAndTextTuple]: +                        def mouse_handler(mouse_event: MouseEvent) -> None: +                            if item.disabled: +                                # The arrow keys can't interact with menu items that are disabled. +                                # The mouse shouldn't be able to either. +                                return +                            hover = mouse_event.event_type == MouseEventType.MOUSE_MOVE +                            if ( +                                mouse_event.event_type == MouseEventType.MOUSE_UP +                                or hover +                            ): +                                app = get_app() +                                if not hover and item.handler: +                                    app.layout.focus_last() +                                    item.handler() +                                else: +                                    self.selected_menu = self.selected_menu[ +                                        : level + 1 +                                    ] + [i] + +                        if i == selected_item: +                            yield ("[SetCursorPosition]", "") +                            style = "class:menu-bar.selected-item" +                        else: +                            style = "" + +                        yield ("class:menu", Border.VERTICAL) +                        if item.text == "-": +                            yield ( +                                style + "class:menu-border", +                                "{}".format(Border.HORIZONTAL * (menu.width + 3)), +                                mouse_handler, +                            ) +                        else: +                            yield ( +                                style, +                                " {}".format(item.text).ljust(menu.width + 3), +                                mouse_handler, +                            ) + +                        if item.children: +                            yield (style, ">", mouse_handler) +                        else: +                            yield (style, " ", mouse_handler) + +                        if i == selected_item: +                            yield ("[SetMenuPosition]", "") +                        yield ("class:menu", Border.VERTICAL) + +                        yield ("", "\n") + +                    for i, item in enumerate(menu.children): +                        result.extend(one_item(i, item)) + +                    result.append(("class:menu", Border.BOTTOM_LEFT)) +                    result.append(("class:menu", Border.HORIZONTAL * (menu.width + 4))) +                    result.append(("class:menu", Border.BOTTOM_RIGHT)) +            return result + +        return Window(FormattedTextControl(get_text_fragments), style="class:menu") + +    @property +    def floats(self) -> Optional[List[Float]]: +        return self.container.floats + +    def __pt_container__(self) -> Container: +        return self.container + + +class MenuItem: +    def __init__( +        self, +        text: str = "", +        handler: Optional[Callable[[], None]] = None, +        children: Optional[List["MenuItem"]] = None, +        shortcut: Optional[Sequence[Union[Keys, str]]] = None, +        disabled: bool = False, +    ) -> None: + +        self.text = text +        self.handler = handler +        self.children = children or [] +        self.shortcut = shortcut +        self.disabled = disabled +        self.selected_item = 0 + +    @property +    def width(self) -> int: +        if self.children: +            return max(get_cwidth(c.text) for c in self.children) +        else: +            return 0 diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/toolbars.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/toolbars.py index eaf29b5b18f..c3559c2b83c 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/toolbars.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/toolbars.py @@ -1,374 +1,374 @@ -from typing import Any, Optional  -  -from prompt_toolkit.application.current import get_app  -from prompt_toolkit.buffer import Buffer  -from prompt_toolkit.enums import SYSTEM_BUFFER  -from prompt_toolkit.filters import (  -    Condition,  -    FilterOrBool,  -    emacs_mode,  -    has_arg,  -    has_completions,  -    has_focus,  -    has_validation_error,  -    to_filter,  -    vi_mode,  -    vi_navigation_mode,  -)  -from prompt_toolkit.formatted_text import (  -    AnyFormattedText,  -    StyleAndTextTuples,  -    fragment_list_len,  -    to_formatted_text,  -)  -from prompt_toolkit.key_binding.key_bindings import (  -    ConditionalKeyBindings,  -    KeyBindings,  -    KeyBindingsBase,  -    merge_key_bindings,  -)  -from prompt_toolkit.key_binding.key_processor import KeyPressEvent  -from prompt_toolkit.key_binding.vi_state import InputMode  -from prompt_toolkit.keys import Keys  -from prompt_toolkit.layout.containers import ConditionalContainer, Container, Window  -from prompt_toolkit.layout.controls import (  -    BufferControl,  -    FormattedTextControl,  -    SearchBufferControl,  -    UIContent,  -    UIControl,  -)  -from prompt_toolkit.layout.dimension import Dimension  -from prompt_toolkit.layout.processors import BeforeInput  -from prompt_toolkit.lexers import SimpleLexer  -from prompt_toolkit.search import SearchDirection  -  -__all__ = [  -    "ArgToolbar",  -    "CompletionsToolbar",  -    "FormattedTextToolbar",  -    "SearchToolbar",  -    "SystemToolbar",  -    "ValidationToolbar",  -]  -  -E = KeyPressEvent  -  -  -class FormattedTextToolbar(Window):  -    def __init__(self, text: AnyFormattedText, style: str = "", **kw: Any) -> None:  -        # Note: The style needs to be applied to the toolbar as a whole, not  -        #       just the `FormattedTextControl`.  -        super().__init__(  -            FormattedTextControl(text, **kw),  -            style=style,  -            dont_extend_height=True,  -            height=Dimension(min=1),  -        )  -  -  -class SystemToolbar:  -    """  -    Toolbar for a system prompt.  -  -    :param prompt: Prompt to be displayed to the user.  -    """  -  -    def __init__(  -        self,  -        prompt: AnyFormattedText = "Shell command: ",  -        enable_global_bindings: FilterOrBool = True,  -    ) -> None:  -  -        self.prompt = prompt  -        self.enable_global_bindings = to_filter(enable_global_bindings)  -  -        self.system_buffer = Buffer(name=SYSTEM_BUFFER)  -  -        self._bindings = self._build_key_bindings()  -  -        self.buffer_control = BufferControl(  -            buffer=self.system_buffer,  -            lexer=SimpleLexer(style="class:system-toolbar.text"),  -            input_processors=[  -                BeforeInput(lambda: self.prompt, style="class:system-toolbar")  -            ],  -            key_bindings=self._bindings,  -        )  -  -        self.window = Window(  -            self.buffer_control, height=1, style="class:system-toolbar"  -        )  -  -        self.container = ConditionalContainer(  -            content=self.window, filter=has_focus(self.system_buffer)  -        )  -  -    def _get_display_before_text(self) -> StyleAndTextTuples:  -        return [  -            ("class:system-toolbar", "Shell command: "),  -            ("class:system-toolbar.text", self.system_buffer.text),  -            ("", "\n"),  -        ]  -  -    def _build_key_bindings(self) -> KeyBindingsBase:  -        focused = has_focus(self.system_buffer)  -  -        # Emacs  -        emacs_bindings = KeyBindings()  -        handle = emacs_bindings.add  -  -        @handle("escape", filter=focused)  -        @handle("c-g", filter=focused)  -        @handle("c-c", filter=focused)  -        def _cancel(event: E) -> None:  -            "Hide system prompt."  -            self.system_buffer.reset()  -            event.app.layout.focus_last()  -  -        @handle("enter", filter=focused)  -        async def _accept(event: E) -> None:  -            "Run system command."  -            await event.app.run_system_command(  -                self.system_buffer.text,  -                display_before_text=self._get_display_before_text(),  -            )  -            self.system_buffer.reset(append_to_history=True)  -            event.app.layout.focus_last()  -  -        # Vi.  -        vi_bindings = KeyBindings()  -        handle = vi_bindings.add  -  -        @handle("escape", filter=focused)  -        @handle("c-c", filter=focused)  -        def _cancel_vi(event: E) -> None:  -            "Hide system prompt."  -            event.app.vi_state.input_mode = InputMode.NAVIGATION  -            self.system_buffer.reset()  -            event.app.layout.focus_last()  -  -        @handle("enter", filter=focused)  -        async def _accept_vi(event: E) -> None:  -            "Run system command."  -            event.app.vi_state.input_mode = InputMode.NAVIGATION  -            event.app.run_system_command(  -                self.system_buffer.text,  -                display_before_text=self._get_display_before_text(),  -            )  -            self.system_buffer.reset(append_to_history=True)  -            event.app.layout.focus_last()  -  -        # Global bindings. (Listen to these bindings, even when this widget is  -        # not focussed.)  -        global_bindings = KeyBindings()  -        handle = global_bindings.add  -  -        @handle(Keys.Escape, "!", filter=~focused & emacs_mode, is_global=True)  -        def _focus_me(event: E) -> None:  -            "M-'!' will focus this user control."  -            event.app.layout.focus(self.window)  -  -        @handle("!", filter=~focused & vi_mode & vi_navigation_mode, is_global=True)  -        def _focus_me_vi(event: E) -> None:  -            "Focus."  -            event.app.vi_state.input_mode = InputMode.INSERT  -            event.app.layout.focus(self.window)  -  -        return merge_key_bindings(  -            [  -                ConditionalKeyBindings(emacs_bindings, emacs_mode),  -                ConditionalKeyBindings(vi_bindings, vi_mode),  -                ConditionalKeyBindings(global_bindings, self.enable_global_bindings),  -            ]  -        )  -  -    def __pt_container__(self) -> Container:  -        return self.container  -  -  -class ArgToolbar:  -    def __init__(self) -> None:  -        def get_formatted_text() -> StyleAndTextTuples:  -            arg = get_app().key_processor.arg or ""  -            if arg == "-":  -                arg = "-1"  -  -            return [  -                ("class:arg-toolbar", "Repeat: "),  -                ("class:arg-toolbar.text", arg),  -            ]  -  -        self.window = Window(FormattedTextControl(get_formatted_text), height=1)  -  -        self.container = ConditionalContainer(content=self.window, filter=has_arg)  -  -    def __pt_container__(self) -> Container:  -        return self.container  -  -  -class SearchToolbar:  -    """  -    :param vi_mode: Display '/' and '?' instead of I-search.  -    :param ignore_case: Search case insensitive.  -    """  -  -    def __init__(  -        self,  -        search_buffer: Optional[Buffer] = None,  -        vi_mode: bool = False,  -        text_if_not_searching: AnyFormattedText = "",  -        forward_search_prompt: AnyFormattedText = "I-search: ",  -        backward_search_prompt: AnyFormattedText = "I-search backward: ",  -        ignore_case: FilterOrBool = False,  -    ) -> None:  -  -        if search_buffer is None:  -            search_buffer = Buffer()  -  -        @Condition  -        def is_searching() -> bool:  -            return self.control in get_app().layout.search_links  -  -        def get_before_input() -> AnyFormattedText:  -            if not is_searching():  -                return text_if_not_searching  -            elif (  -                self.control.searcher_search_state.direction == SearchDirection.BACKWARD  -            ):  -                return "?" if vi_mode else backward_search_prompt  -            else:  -                return "/" if vi_mode else forward_search_prompt  -  -        self.search_buffer = search_buffer  -  -        self.control = SearchBufferControl(  -            buffer=search_buffer,  -            input_processors=[  -                BeforeInput(get_before_input, style="class:search-toolbar.prompt")  -            ],  -            lexer=SimpleLexer(style="class:search-toolbar.text"),  -            ignore_case=ignore_case,  -        )  -  -        self.container = ConditionalContainer(  -            content=Window(self.control, height=1, style="class:search-toolbar"),  -            filter=is_searching,  -        )  -  -    def __pt_container__(self) -> Container:  -        return self.container  -  -  -class _CompletionsToolbarControl(UIControl):  -    def create_content(self, width: int, height: int) -> UIContent:  -        all_fragments: StyleAndTextTuples = []  -  -        complete_state = get_app().current_buffer.complete_state  -        if complete_state:  -            completions = complete_state.completions  -            index = complete_state.complete_index  # Can be None!  -  -            # Width of the completions without the left/right arrows in the margins.  -            content_width = width - 6  -  -            # Booleans indicating whether we stripped from the left/right  -            cut_left = False  -            cut_right = False  -  -            # Create Menu content.  -            fragments: StyleAndTextTuples = []  -  -            for i, c in enumerate(completions):  -                # When there is no more place for the next completion  -                if fragment_list_len(fragments) + len(c.display_text) >= content_width:  -                    # If the current one was not yet displayed, page to the next sequence.  -                    if i <= (index or 0):  -                        fragments = []  -                        cut_left = True  -                    # If the current one is visible, stop here.  -                    else:  -                        cut_right = True  -                        break  -  -                fragments.extend(  -                    to_formatted_text(  -                        c.display_text,  -                        style=(  -                            "class:completion-toolbar.completion.current"  -                            if i == index  -                            else "class:completion-toolbar.completion"  -                        ),  -                    )  -                )  -                fragments.append(("", " "))  -  -            # Extend/strip until the content width.  -            fragments.append(("", " " * (content_width - fragment_list_len(fragments))))  -            fragments = fragments[:content_width]  -  -            # Return fragments  -            all_fragments.append(("", " "))  -            all_fragments.append(  -                ("class:completion-toolbar.arrow", "<" if cut_left else " ")  -            )  -            all_fragments.append(("", " "))  -  -            all_fragments.extend(fragments)  -  -            all_fragments.append(("", " "))  -            all_fragments.append(  -                ("class:completion-toolbar.arrow", ">" if cut_right else " ")  -            )  -            all_fragments.append(("", " "))  -  -        def get_line(i: int) -> StyleAndTextTuples:  -            return all_fragments  -  -        return UIContent(get_line=get_line, line_count=1)  -  -  -class CompletionsToolbar:  -    def __init__(self) -> None:  -        self.container = ConditionalContainer(  -            content=Window(  -                _CompletionsToolbarControl(), height=1, style="class:completion-toolbar"  -            ),  -            filter=has_completions,  -        )  -  -    def __pt_container__(self) -> Container:  -        return self.container  -  -  -class ValidationToolbar:  -    def __init__(self, show_position: bool = False) -> None:  -        def get_formatted_text() -> StyleAndTextTuples:  -            buff = get_app().current_buffer  -  -            if buff.validation_error:  -                row, column = buff.document.translate_index_to_position(  -                    buff.validation_error.cursor_position  -                )  -  -                if show_position:  -                    text = "%s (line=%s column=%s)" % (  -                        buff.validation_error.message,  -                        row + 1,  -                        column + 1,  -                    )  -                else:  -                    text = buff.validation_error.message  -  -                return [("class:validation-toolbar", text)]  -            else:  -                return []  -  -        self.control = FormattedTextControl(get_formatted_text)  -  -        self.container = ConditionalContainer(  -            content=Window(self.control, height=1), filter=has_validation_error  -        )  -  -    def __pt_container__(self) -> Container:  -        return self.container  +from typing import Any, Optional + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.buffer import Buffer +from prompt_toolkit.enums import SYSTEM_BUFFER +from prompt_toolkit.filters import ( +    Condition, +    FilterOrBool, +    emacs_mode, +    has_arg, +    has_completions, +    has_focus, +    has_validation_error, +    to_filter, +    vi_mode, +    vi_navigation_mode, +) +from prompt_toolkit.formatted_text import ( +    AnyFormattedText, +    StyleAndTextTuples, +    fragment_list_len, +    to_formatted_text, +) +from prompt_toolkit.key_binding.key_bindings import ( +    ConditionalKeyBindings, +    KeyBindings, +    KeyBindingsBase, +    merge_key_bindings, +) +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.key_binding.vi_state import InputMode +from prompt_toolkit.keys import Keys +from prompt_toolkit.layout.containers import ConditionalContainer, Container, Window +from prompt_toolkit.layout.controls import ( +    BufferControl, +    FormattedTextControl, +    SearchBufferControl, +    UIContent, +    UIControl, +) +from prompt_toolkit.layout.dimension import Dimension +from prompt_toolkit.layout.processors import BeforeInput +from prompt_toolkit.lexers import SimpleLexer +from prompt_toolkit.search import SearchDirection + +__all__ = [ +    "ArgToolbar", +    "CompletionsToolbar", +    "FormattedTextToolbar", +    "SearchToolbar", +    "SystemToolbar", +    "ValidationToolbar", +] + +E = KeyPressEvent + + +class FormattedTextToolbar(Window): +    def __init__(self, text: AnyFormattedText, style: str = "", **kw: Any) -> None: +        # Note: The style needs to be applied to the toolbar as a whole, not +        #       just the `FormattedTextControl`. +        super().__init__( +            FormattedTextControl(text, **kw), +            style=style, +            dont_extend_height=True, +            height=Dimension(min=1), +        ) + + +class SystemToolbar: +    """ +    Toolbar for a system prompt. + +    :param prompt: Prompt to be displayed to the user. +    """ + +    def __init__( +        self, +        prompt: AnyFormattedText = "Shell command: ", +        enable_global_bindings: FilterOrBool = True, +    ) -> None: + +        self.prompt = prompt +        self.enable_global_bindings = to_filter(enable_global_bindings) + +        self.system_buffer = Buffer(name=SYSTEM_BUFFER) + +        self._bindings = self._build_key_bindings() + +        self.buffer_control = BufferControl( +            buffer=self.system_buffer, +            lexer=SimpleLexer(style="class:system-toolbar.text"), +            input_processors=[ +                BeforeInput(lambda: self.prompt, style="class:system-toolbar") +            ], +            key_bindings=self._bindings, +        ) + +        self.window = Window( +            self.buffer_control, height=1, style="class:system-toolbar" +        ) + +        self.container = ConditionalContainer( +            content=self.window, filter=has_focus(self.system_buffer) +        ) + +    def _get_display_before_text(self) -> StyleAndTextTuples: +        return [ +            ("class:system-toolbar", "Shell command: "), +            ("class:system-toolbar.text", self.system_buffer.text), +            ("", "\n"), +        ] + +    def _build_key_bindings(self) -> KeyBindingsBase: +        focused = has_focus(self.system_buffer) + +        # Emacs +        emacs_bindings = KeyBindings() +        handle = emacs_bindings.add + +        @handle("escape", filter=focused) +        @handle("c-g", filter=focused) +        @handle("c-c", filter=focused) +        def _cancel(event: E) -> None: +            "Hide system prompt." +            self.system_buffer.reset() +            event.app.layout.focus_last() + +        @handle("enter", filter=focused) +        async def _accept(event: E) -> None: +            "Run system command." +            await event.app.run_system_command( +                self.system_buffer.text, +                display_before_text=self._get_display_before_text(), +            ) +            self.system_buffer.reset(append_to_history=True) +            event.app.layout.focus_last() + +        # Vi. +        vi_bindings = KeyBindings() +        handle = vi_bindings.add + +        @handle("escape", filter=focused) +        @handle("c-c", filter=focused) +        def _cancel_vi(event: E) -> None: +            "Hide system prompt." +            event.app.vi_state.input_mode = InputMode.NAVIGATION +            self.system_buffer.reset() +            event.app.layout.focus_last() + +        @handle("enter", filter=focused) +        async def _accept_vi(event: E) -> None: +            "Run system command." +            event.app.vi_state.input_mode = InputMode.NAVIGATION +            event.app.run_system_command( +                self.system_buffer.text, +                display_before_text=self._get_display_before_text(), +            ) +            self.system_buffer.reset(append_to_history=True) +            event.app.layout.focus_last() + +        # Global bindings. (Listen to these bindings, even when this widget is +        # not focussed.) +        global_bindings = KeyBindings() +        handle = global_bindings.add + +        @handle(Keys.Escape, "!", filter=~focused & emacs_mode, is_global=True) +        def _focus_me(event: E) -> None: +            "M-'!' will focus this user control." +            event.app.layout.focus(self.window) + +        @handle("!", filter=~focused & vi_mode & vi_navigation_mode, is_global=True) +        def _focus_me_vi(event: E) -> None: +            "Focus." +            event.app.vi_state.input_mode = InputMode.INSERT +            event.app.layout.focus(self.window) + +        return merge_key_bindings( +            [ +                ConditionalKeyBindings(emacs_bindings, emacs_mode), +                ConditionalKeyBindings(vi_bindings, vi_mode), +                ConditionalKeyBindings(global_bindings, self.enable_global_bindings), +            ] +        ) + +    def __pt_container__(self) -> Container: +        return self.container + + +class ArgToolbar: +    def __init__(self) -> None: +        def get_formatted_text() -> StyleAndTextTuples: +            arg = get_app().key_processor.arg or "" +            if arg == "-": +                arg = "-1" + +            return [ +                ("class:arg-toolbar", "Repeat: "), +                ("class:arg-toolbar.text", arg), +            ] + +        self.window = Window(FormattedTextControl(get_formatted_text), height=1) + +        self.container = ConditionalContainer(content=self.window, filter=has_arg) + +    def __pt_container__(self) -> Container: +        return self.container + + +class SearchToolbar: +    """ +    :param vi_mode: Display '/' and '?' instead of I-search. +    :param ignore_case: Search case insensitive. +    """ + +    def __init__( +        self, +        search_buffer: Optional[Buffer] = None, +        vi_mode: bool = False, +        text_if_not_searching: AnyFormattedText = "", +        forward_search_prompt: AnyFormattedText = "I-search: ", +        backward_search_prompt: AnyFormattedText = "I-search backward: ", +        ignore_case: FilterOrBool = False, +    ) -> None: + +        if search_buffer is None: +            search_buffer = Buffer() + +        @Condition +        def is_searching() -> bool: +            return self.control in get_app().layout.search_links + +        def get_before_input() -> AnyFormattedText: +            if not is_searching(): +                return text_if_not_searching +            elif ( +                self.control.searcher_search_state.direction == SearchDirection.BACKWARD +            ): +                return "?" if vi_mode else backward_search_prompt +            else: +                return "/" if vi_mode else forward_search_prompt + +        self.search_buffer = search_buffer + +        self.control = SearchBufferControl( +            buffer=search_buffer, +            input_processors=[ +                BeforeInput(get_before_input, style="class:search-toolbar.prompt") +            ], +            lexer=SimpleLexer(style="class:search-toolbar.text"), +            ignore_case=ignore_case, +        ) + +        self.container = ConditionalContainer( +            content=Window(self.control, height=1, style="class:search-toolbar"), +            filter=is_searching, +        ) + +    def __pt_container__(self) -> Container: +        return self.container + + +class _CompletionsToolbarControl(UIControl): +    def create_content(self, width: int, height: int) -> UIContent: +        all_fragments: StyleAndTextTuples = [] + +        complete_state = get_app().current_buffer.complete_state +        if complete_state: +            completions = complete_state.completions +            index = complete_state.complete_index  # Can be None! + +            # Width of the completions without the left/right arrows in the margins. +            content_width = width - 6 + +            # Booleans indicating whether we stripped from the left/right +            cut_left = False +            cut_right = False + +            # Create Menu content. +            fragments: StyleAndTextTuples = [] + +            for i, c in enumerate(completions): +                # When there is no more place for the next completion +                if fragment_list_len(fragments) + len(c.display_text) >= content_width: +                    # If the current one was not yet displayed, page to the next sequence. +                    if i <= (index or 0): +                        fragments = [] +                        cut_left = True +                    # If the current one is visible, stop here. +                    else: +                        cut_right = True +                        break + +                fragments.extend( +                    to_formatted_text( +                        c.display_text, +                        style=( +                            "class:completion-toolbar.completion.current" +                            if i == index +                            else "class:completion-toolbar.completion" +                        ), +                    ) +                ) +                fragments.append(("", " ")) + +            # Extend/strip until the content width. +            fragments.append(("", " " * (content_width - fragment_list_len(fragments)))) +            fragments = fragments[:content_width] + +            # Return fragments +            all_fragments.append(("", " ")) +            all_fragments.append( +                ("class:completion-toolbar.arrow", "<" if cut_left else " ") +            ) +            all_fragments.append(("", " ")) + +            all_fragments.extend(fragments) + +            all_fragments.append(("", " ")) +            all_fragments.append( +                ("class:completion-toolbar.arrow", ">" if cut_right else " ") +            ) +            all_fragments.append(("", " ")) + +        def get_line(i: int) -> StyleAndTextTuples: +            return all_fragments + +        return UIContent(get_line=get_line, line_count=1) + + +class CompletionsToolbar: +    def __init__(self) -> None: +        self.container = ConditionalContainer( +            content=Window( +                _CompletionsToolbarControl(), height=1, style="class:completion-toolbar" +            ), +            filter=has_completions, +        ) + +    def __pt_container__(self) -> Container: +        return self.container + + +class ValidationToolbar: +    def __init__(self, show_position: bool = False) -> None: +        def get_formatted_text() -> StyleAndTextTuples: +            buff = get_app().current_buffer + +            if buff.validation_error: +                row, column = buff.document.translate_index_to_position( +                    buff.validation_error.cursor_position +                ) + +                if show_position: +                    text = "%s (line=%s column=%s)" % ( +                        buff.validation_error.message, +                        row + 1, +                        column + 1, +                    ) +                else: +                    text = buff.validation_error.message + +                return [("class:validation-toolbar", text)] +            else: +                return [] + +        self.control = FormattedTextControl(get_formatted_text) + +        self.container = ConditionalContainer( +            content=Window(self.control, height=1), filter=has_validation_error +        ) + +    def __pt_container__(self) -> Container: +        return self.container  | 
