Source code for zelfred.ui_process_key_pressed

# -*- coding: utf-8 -*-

"""
See :class:`UIProcessKeyPressedMixin`.
"""

import typing as T

from . import keyboard
from . import exc

if T.TYPE_CHECKING:
    from .item import T_ITEM
    from .ui import UI


[docs]class UIProcessKeyPressedMixin: """ This class implements the key pressed event processing logics. **Important** All methods whose names start with ``process_`` are abstract methods that handle specific key press events. They implement the Default behavior:. Experienced developers can override them to customize the behavior. """
[docs] def cursor_up_and_down(self: "UI"): """ A helper method should be called after the cursor moves up or down. - need_run_handler: False, the user input query is not changed, so we don't need to run the handler again. - need_move_to_end: True, the dropdown menu may change, so we need to move the cursor to the end of the line in the next event loop - need_clear_items: True, the dropdown menu may change, so we need to clear the dropdown menu. - need_clear_query: False, the user input query is not changed, so we don't need to clear the user input query. - need_print_query: False, the user input query is not changed, so we don't need to print the user input query. - need_print_items: True, the dropdown menu may change, so we need to print the dropdown menu. - need_process_input: True, we need to process the user input next time. """ self.need_run_handler: bool = False self.need_clear_query: bool = False self.need_print_query: bool = False
[docs] def wait_next_user_input(self: "UI"): """ Don't repaint """ self.cursor_left_and_right()
[docs] def cursor_left_and_right(self: "UI"): """ A helper method should be called after the cursor moves left and right. - need_run_handler: False, the user input query is not changed, so we don't need to run the handler again. - need_move_to_end: False, the dropdown menu is not changed, so we don't need to move the cursor to the end of the line. - need_clear_items: False, the dropdown menu is not changed, so we don't need to clear the dropdown menu. - need_clear_query: False, the user input query is not changed, so we don't need to clear the user input query. - need_print_query: False, the user input query is not changed, so we don't need to print the user input query. - need_print_items: False, the dropdown menu is not change, so we don't need to print the dropdown menu. - need_process_input: True, we need to process the user input next time. """ self.need_run_handler = False self.need_move_to_end = False self.need_clear_items = False self.need_clear_query = False self.need_print_query = False self.need_print_items = False
[docs] def process_up(self: "UI"): """ Default behavior: select the previous item in dropdown menu. """ self.cursor_up_and_down() self.dropdown.press_up()
[docs] def process_down(self: "UI"): """ Default behavior: select the next item in dropdown menu. """ self.cursor_up_and_down() self.dropdown.press_down()
[docs] def process_ctrl_e(self: "UI"): """ Default behavior: select the previous item in dropdown menu. """ self.cursor_up_and_down() self.dropdown.press_up()
[docs] def process_ctrl_d(self: "UI"): """ Default behavior: select the next item in dropdown menu. """ self.cursor_up_and_down() self.dropdown.press_down()
[docs] def process_ctrl_r(self: "UI"): """ Default behavior: scroll up the dropdown menu (press UP 5 times). """ self.cursor_up_and_down() self.dropdown.scroll_up()
[docs] def process_ctrl_f(self: "UI"): """ Default behavior: scroll down the dropdown menu (press DOWN 5 times). """ self.cursor_up_and_down() self.dropdown.scroll_down()
[docs] def process_left(self: "UI"): """ Default behavior: move user input cursor to the previous character. """ self.cursor_left_and_right() self.line_editor.press_left()
[docs] def process_right(self: "UI"): """ Default behavior: move user input cursor to the next character. """ self.cursor_left_and_right() self.line_editor.press_right()
[docs] def process_alt_left(self: "UI"): """ Default behavior: Move cursor to the beginning of previous word. """ self.cursor_left_and_right() self.line_editor.move_word_backward()
[docs] def process_alt_right(self: "UI"): """ Default behavior: Move cursor to the beginning of next word. """ self.cursor_left_and_right() self.line_editor.move_word_forward()
[docs] def process_home(self: "UI"): """ Default behavior: Move cursor to the beginning of the line. """ self.cursor_left_and_right() self.line_editor.press_home()
[docs] def process_end(self: "UI"): """ Default behavior: Move cursor to the end of the line. """ self.cursor_left_and_right() self.line_editor.press_end()
[docs] def press_backspace(self: "UI"): """ Default behavior: Delete character backwards in the line editor. Also move cursor to the left. """ self.line_editor.press_backspace()
[docs] def press_delete(self: "UI"): """ Default behavior: Delete character forwards in the line editor. Also, the cursor stays. """ self.line_editor.press_delete()
[docs] def process_ctrl_k(self: "UI"): """ Default behavior: Delete the previous word. """ self.line_editor.delete_word_backward()
[docs] def process_ctrl_l(self: "UI"): """ Default behavior: Delete the next word. """ self.line_editor.delete_word_forward()
[docs] def process_tab(self: "UI"): """ Default behavior: Auto complete. """ self.line_editor.clear_line() selected_item = self.dropdown.selected_item if selected_item.autocomplete: self.line_editor.enter_text(selected_item.autocomplete)
[docs] def process_ctrl_x(self: "UI"): """ Default behavior: Clear the user input. """ self.line_editor.clear_line()
[docs] def process_ctrl_c(self: "UI"): """ Default behavior: Keyboard interrupt. """ raise KeyboardInterrupt()
def validate_selected_item(self: "UI") -> T.Optional["T_ITEM"]: if self.dropdown.n_items == 0: raise exc.EndOfInputError( selection="select nothing", ) else: self.move_to_end() if self.dropdown.items: return self.dropdown.selected_item return None
[docs] def process_enter(self: "UI"): """ Default behavior: User action for enter. """ selected_item = self.validate_selected_item() if selected_item is not None: selected_item.enter_handler(ui=self) selected_item.post_enter_handler(ui=self)
[docs] def process_ctrl_a(self: "UI"): """ Default behavior: User action for CTRL A. """ selected_item = self.validate_selected_item() if selected_item is not None: selected_item.ctrl_a_handler(ui=self) selected_item.post_ctrl_a_handler(ui=self)
[docs] def process_ctrl_w(self: "UI"): """ Default behavior: User action for CTRL W. """ selected_item = self.validate_selected_item() if selected_item is not None: selected_item.ctrl_w_handler(ui=self) selected_item.post_ctrl_w_handler(ui=self)
[docs] def process_ctrl_u(self: "UI"): """ Default behavior: User action for CTRL U. """ selected_item = self.validate_selected_item() if selected_item is not None: selected_item.ctrl_u_handler(ui=self) selected_item.post_ctrl_u_handler(ui=self)
[docs] def process_ctrl_p(self: "UI"): """ Default behavior: User action for CTRL P. """ selected_item = self.validate_selected_item() if selected_item is not None: selected_item.ctrl_p_handler(ui=self) selected_item.post_ctrl_p_handler(ui=self)
[docs] def process_f1(self: "UI"): """ Default behavior: Jump out the sub-session, return to the previous view. """ raise exc.JumpOutSessionError
[docs] def process_ctrl_t(self: "UI"): """ Default behavior: Do nothing. This is reserved for user custom shortcut. """ self.wait_next_user_input()
[docs] def process_ctrl_g(self: "UI"): """ Default behavior: Do nothing. This is reserved for user custom shortcut. """ self.wait_next_user_input()
[docs] def process_ctrl_b(self: "UI"): """ Default behavior: Do nothing. This is reserved for user custom shortcut. """ self.wait_next_user_input()
[docs] def process_ctrl_n(self: "UI"): """ Default behavior: Do nothing. This is reserved for user custom shortcut. """ self.wait_next_user_input()
def _create_key_processor_mapper(self): """ Create a key processor mapper. So we can map the key to the corresponding processor. """ self._key_processor_mapper = { # dropdown menu keyboard.UP: self.process_up, keyboard.DOWN: self.process_down, keyboard.CTRL_E: self.process_ctrl_e, keyboard.CTRL_D: self.process_ctrl_d, keyboard.CTRL_R: self.process_ctrl_r, keyboard.CTRL_F: self.process_ctrl_f, # line editor - move cursor keyboard.LEFT: self.process_left, keyboard.RIGHT: self.process_right, keyboard.ALT_LEFT: self.process_alt_left, keyboard.ALT_RIGHT: self.process_alt_right, keyboard.HOME: self.process_home, keyboard.END: self.process_end, # line editor - change text keyboard.BACKSPACE: self.press_backspace, keyboard.DELETE: self.press_delete, keyboard.CTRL_K: self.process_ctrl_k, keyboard.CTRL_L: self.process_ctrl_l, keyboard.TAB: self.process_tab, keyboard.CTRL_X: self.process_ctrl_x, # special keyboard.CTRL_C: self.process_ctrl_c, keyboard.F1: self.process_f1, # User defined (customizable) item action keyboard.ENTER: self.process_enter, keyboard.CTRL_A: self.process_ctrl_a, keyboard.CTRL_W: self.process_ctrl_w, keyboard.CTRL_U: self.process_ctrl_u, keyboard.CTRL_P: self.process_ctrl_p, # User defined UI keybinding: keyboard.CTRL_T: self.process_ctrl_t, keyboard.CTRL_G: self.process_ctrl_g, keyboard.CTRL_B: self.process_ctrl_b, keyboard.CTRL_N: self.process_ctrl_n, } def _process_key_pressed_input(self: "UI", key: str): """ Try to process the key pressed :return: a boolean flag indicating whether the key pressed is processed """ if key in self._key_processor_mapper: self._key_processor_mapper[key]() else: self.line_editor.press_key(key)