| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | """ |
| | Main driver for the selection menu, based on https://github.com/bchao1/bullet |
| | """ |
| |
|
| | import builtins |
| | import sys |
| | from typing import Optional |
| |
|
| | from ...utils.imports import _is_package_available |
| | from . import cursor, input |
| | from .helpers import Direction, clear_line, forceWrite, linebreak, move_cursor, reset_cursor, writeColor |
| | from .keymap import KEYMAP |
| |
|
| |
|
| | in_colab = False |
| | try: |
| | in_colab = _is_package_available("google.colab") |
| | except ModuleNotFoundError: |
| | pass |
| |
|
| |
|
| | @input.register |
| | class BulletMenu: |
| | """ |
| | A CLI menu to select a choice from a list of choices using the keyboard. |
| | """ |
| |
|
| | def __init__(self, prompt: Optional[str] = None, choices: list = []): |
| | self.position = 0 |
| | self.choices = choices |
| | self.prompt = prompt |
| | if sys.platform == "win32": |
| | self.arrow_char = "*" |
| | else: |
| | self.arrow_char = "➔ " |
| |
|
| | def write_choice(self, index, end: str = ""): |
| | if sys.platform != "win32": |
| | writeColor(self.choices[index], 32, end) |
| | else: |
| | forceWrite(self.choices[index], end) |
| |
|
| | def print_choice(self, index: int): |
| | "Prints the choice at the given index" |
| | if index == self.position: |
| | forceWrite(f" {self.arrow_char} ") |
| | self.write_choice(index) |
| | else: |
| | forceWrite(f" {self.choices[index]}") |
| | reset_cursor() |
| |
|
| | def move_direction(self, direction: Direction, num_spaces: int = 1): |
| | "Should not be directly called, used to move a direction of either up or down" |
| | old_position = self.position |
| | if direction == Direction.DOWN: |
| | if self.position + 1 >= len(self.choices): |
| | return |
| | self.position += num_spaces |
| | else: |
| | if self.position - 1 < 0: |
| | return |
| | self.position -= num_spaces |
| | clear_line() |
| | self.print_choice(old_position) |
| | move_cursor(num_spaces, direction.name) |
| | self.print_choice(self.position) |
| |
|
| | @input.mark(KEYMAP["up"]) |
| | def move_up(self): |
| | self.move_direction(Direction.UP) |
| |
|
| | @input.mark(KEYMAP["down"]) |
| | def move_down(self): |
| | self.move_direction(Direction.DOWN) |
| |
|
| | @input.mark(KEYMAP["newline"]) |
| | def select(self): |
| | move_cursor(len(self.choices) - self.position, "DOWN") |
| | return self.position |
| |
|
| | @input.mark(KEYMAP["interrupt"]) |
| | def interrupt(self): |
| | move_cursor(len(self.choices) - self.position, "DOWN") |
| | raise KeyboardInterrupt |
| |
|
| | @input.mark_multiple(*[KEYMAP[str(number)] for number in range(10)]) |
| | def select_row(self): |
| | index = int(chr(self.current_selection)) |
| | movement = index - self.position |
| | if index == self.position: |
| | return |
| | if index < len(self.choices): |
| | if self.position > index: |
| | self.move_direction(Direction.UP, -movement) |
| | elif self.position < index: |
| | self.move_direction(Direction.DOWN, movement) |
| | else: |
| | return |
| | else: |
| | return |
| |
|
| | def run(self, default_choice: int = 0): |
| | "Start the menu and return the selected choice" |
| | if self.prompt: |
| | linebreak() |
| | forceWrite(self.prompt, "\n") |
| | if in_colab: |
| | forceWrite("Please input a choice index (starting from 0), and press enter", "\n") |
| | else: |
| | forceWrite("Please select a choice using the arrow or number keys, and selecting with enter", "\n") |
| | self.position = default_choice |
| | for i in range(len(self.choices)): |
| | self.print_choice(i) |
| | forceWrite("\n") |
| | move_cursor(len(self.choices) - self.position, "UP") |
| | with cursor.hide(): |
| | while True: |
| | if in_colab: |
| | try: |
| | choice = int(builtins.input()) |
| | except ValueError: |
| | choice = default_choice |
| | else: |
| | choice = self.handle_input() |
| | if choice is not None: |
| | reset_cursor() |
| | for _ in range(len(self.choices) + 1): |
| | move_cursor(1, "UP") |
| | clear_line() |
| | self.write_choice(choice, "\n") |
| | return choice |
| |
|