File: cmenu.py

package info (click to toggle)
urwid 3.0.4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 3,232 kB
  • sloc: python: 29,010; javascript: 382; sh: 34; makefile: 22
file content (113 lines) | stat: -rw-r--r-- 3,188 bytes parent folder | download | duplicates (2)
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from __future__ import annotations

import typing

import urwid

if typing.TYPE_CHECKING:
    from collections.abc import Callable, Hashable, Iterable


def menu_button(
    caption: str | tuple[Hashable, str] | list[str | tuple[Hashable, str]],
    callback: Callable[[urwid.Button], typing.Any],
) -> urwid.AttrMap:
    button = urwid.Button(caption, on_press=callback)
    return urwid.AttrMap(button, None, focus_map="reversed")


def sub_menu(
    caption: str | tuple[Hashable, str] | list[str | tuple[Hashable, str]],
    choices: Iterable[urwid.Widget],
) -> urwid.Widget:
    contents = menu(caption, choices)

    def open_menu(button: urwid.Button) -> None:
        return top.open_box(contents)

    return menu_button([caption, "..."], open_menu)


def menu(
    title: str | tuple[Hashable, str] | list[str | tuple[Hashable, str]],
    choices: Iterable[urwid.Widget],
) -> urwid.ListBox:
    body = [urwid.Text(title), urwid.Divider(), *choices]
    return urwid.ListBox(urwid.SimpleFocusListWalker(body))


def item_chosen(button: urwid.Button) -> None:
    response = urwid.Text(["You chose ", button.label, "\n"])
    done = menu_button("Ok", exit_program)
    top.open_box(urwid.Filler(urwid.Pile([response, done])))


def exit_program(button: urwid.Button) -> typing.NoReturn:
    raise urwid.ExitMainLoop()


menu_top = menu(
    "Main Menu",
    [
        sub_menu(
            "Applications",
            [
                sub_menu(
                    "Accessories",
                    [
                        menu_button("Text Editor", item_chosen),
                        menu_button("Terminal", item_chosen),
                    ],
                ),
            ],
        ),
        sub_menu(
            "System",
            [
                sub_menu(
                    "Preferences",
                    [menu_button("Appearance", item_chosen)],
                ),
                menu_button("Lock Screen", item_chosen),
            ],
        ),
    ],
)


class CascadingBoxes(urwid.WidgetPlaceholder):
    max_box_levels = 4

    def __init__(self, box: urwid.Widget) -> None:
        super().__init__(urwid.SolidFill("/"))
        self.box_level = 0
        self.open_box(box)

    def open_box(self, box: urwid.Widget) -> None:
        self.original_widget = urwid.Overlay(
            urwid.LineBox(box),
            self.original_widget,
            align=urwid.CENTER,
            width=(urwid.RELATIVE, 80),
            valign=urwid.MIDDLE,
            height=(urwid.RELATIVE, 80),
            min_width=24,
            min_height=8,
            left=self.box_level * 3,
            right=(self.max_box_levels - self.box_level - 1) * 3,
            top=self.box_level * 2,
            bottom=(self.max_box_levels - self.box_level - 1) * 2,
        )
        self.box_level += 1

    def keypress(self, size, key: str) -> str | None:
        if key == "esc" and self.box_level > 1:
            self.original_widget = self.original_widget[0]
            self.box_level -= 1
            return None

        return super().keypress(size, key)


top = CascadingBoxes(menu_top)
urwid.MainLoop(top, palette=[("reversed", "standout", "")]).run()