aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.ru>2022-02-10 16:44:30 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:44:30 +0300
commit2598ef1d0aee359b4b6d5fdd1758916d5907d04f (patch)
tree012bb94d777798f1f56ac1cec429509766d05181 /contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets
parent6751af0b0c1b952fede40b19b71da8025b5d8bcf (diff)
downloadydb-2598ef1d0aee359b4b6d5fdd1758916d5907d04f.tar.gz
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets')
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/__init__.py120
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/base.py1894
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/dialogs.py212
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/menus.py748
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/toolbars.py748
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 552d355948..a7559ebd0f 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 728190b54c..c059872793 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 920582b4e6..d814d275ea 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 7203aae118..f4d91067c9 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 c3559c2b83..eaf29b5b18 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