diff options
author | shadchin <shadchin@yandex-team.ru> | 2022-02-10 16:44:39 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:39 +0300 |
commit | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (patch) | |
tree | 64175d5cadab313b3e7039ebaa06c5bc3295e274 /contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/menus.py | |
parent | 2598ef1d0aee359b4b6d5fdd1758916d5907d04f (diff) | |
download | ydb-e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0.tar.gz |
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/menus.py')
-rw-r--r-- | contrib/python/prompt-toolkit/py3/prompt_toolkit/widgets/menus.py | 748 |
1 files changed, 374 insertions, 374 deletions
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 f4d91067c9..7203aae118 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 |