File size: 2,776 Bytes
256a159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import sys

if sys.platform == 'win32':  # Always return win32 for Windows
    # curses is not supported on Windows
    # If you want to use this function in Windows platform
    # you can try `windows_curses` module by yourself
    curses = None
else:
    import curses


class Menu:
    """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
            each list. Defaults to None, in which case each list will be
            displayed without a prompt.
    """

    def __init__(self, lists, prompts=None):
        self.choices_lists = lists
        self.prompts = prompts or ['Please make a selection:'] * len(lists)
        self.choices = []
        self.current_window = []

    def draw_menu(self, stdscr, selected_row_idx, offset, max_rows):
        stdscr.clear()
        h, w = stdscr.getmaxyx()
        for idx, row in enumerate(self.current_window[offset:offset +
                                                      max_rows]):
            x = w // 2 - len(row) // 2
            y = min(h - 1,
                    idx + 1)  # Ensure y never goes beyond the window height
            if idx == selected_row_idx - offset:
                stdscr.attron(curses.color_pair(1))
                stdscr.addstr(y, x, row)
                stdscr.attroff(curses.color_pair(1))
            else:
                stdscr.addstr(y, x, row)
        stdscr.refresh()

    def run(self):
        curses.wrapper(self.main_loop)
        return self.choices

    def main_loop(self, stdscr):
        curses.curs_set(0)
        curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
        h, w = stdscr.getmaxyx()
        max_rows = h - 2

        for choices, prompt in zip(self.choices_lists, self.prompts):
            self.current_window = [prompt] + choices
            current_row_idx = 1
            offset = 0

            while 1:
                self.draw_menu(stdscr, current_row_idx, offset, max_rows)
                key = stdscr.getch()

                if key == curses.KEY_UP and current_row_idx > 1:
                    current_row_idx -= 1
                    if current_row_idx - offset < 1:
                        offset -= 1

                elif key == curses.KEY_DOWN and current_row_idx < len(choices):
                    current_row_idx += 1
                    if current_row_idx - offset > max_rows - 1:
                        offset += 1

                elif key == curses.KEY_ENTER or key in [10, 13]:
                    self.choices.append(choices[current_row_idx - 1])
                    break