A curses menu that allows the user to select one item from each list. Args: lists (list[list[str]]): A list of lists of strings, where each list represents a list of items to be selected from. prompts (list[str], optional): A list of prompts to be displayed above
| 10 | |
| 11 | |
| 12 | class Menu: |
| 13 | """A curses menu that allows the user to select one item from each list. |
| 14 | |
| 15 | Args: |
| 16 | lists (list[list[str]]): A list of lists of strings, where each list |
| 17 | represents a list of items to be selected from. |
| 18 | prompts (list[str], optional): A list of prompts to be displayed above |
| 19 | each list. Defaults to None, in which case each list will be |
| 20 | displayed without a prompt. |
| 21 | """ |
| 22 | |
| 23 | def __init__(self, lists, prompts=None): |
| 24 | self.choices_lists = lists |
| 25 | self.prompts = prompts or ['Please make a selection:'] * len(lists) |
| 26 | self.choices = [] |
| 27 | self.current_window = [] |
| 28 | |
| 29 | def draw_menu(self, stdscr, selected_row_idx, offset, max_rows): |
| 30 | stdscr.clear() |
| 31 | h, w = stdscr.getmaxyx() |
| 32 | for idx, row in enumerate(self.current_window[offset:offset + |
| 33 | max_rows]): |
| 34 | x = w // 2 - len(row) // 2 |
| 35 | y = min(h - 1, |
| 36 | idx + 1) # Ensure y never goes beyond the window height |
| 37 | if idx == selected_row_idx - offset: |
| 38 | stdscr.attron(curses.color_pair(1)) |
| 39 | stdscr.addstr(y, x, row) |
| 40 | stdscr.attroff(curses.color_pair(1)) |
| 41 | else: |
| 42 | stdscr.addstr(y, x, row) |
| 43 | stdscr.refresh() |
| 44 | |
| 45 | def run(self): |
| 46 | curses.wrapper(self.main_loop) |
| 47 | return self.choices |
| 48 | |
| 49 | def main_loop(self, stdscr): |
| 50 | curses.curs_set(0) |
| 51 | curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) |
| 52 | h, w = stdscr.getmaxyx() |
| 53 | max_rows = h - 2 |
| 54 | |
| 55 | for choices, prompt in zip(self.choices_lists, self.prompts): |
| 56 | self.current_window = [prompt] + choices |
| 57 | current_row_idx = 1 |
| 58 | offset = 0 |
| 59 | |
| 60 | while 1: |
| 61 | self.draw_menu(stdscr, current_row_idx, offset, max_rows) |
| 62 | key = stdscr.getch() |
| 63 | |
| 64 | if key == curses.KEY_UP and current_row_idx > 1: |
| 65 | current_row_idx -= 1 |
| 66 | if current_row_idx - offset < 1: |
| 67 | offset -= 1 |
| 68 | |
| 69 | elif key == curses.KEY_DOWN and current_row_idx < len(choices): |