-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathui.py
More file actions
124 lines (91 loc) · 2.49 KB
/
ui.py
File metadata and controls
124 lines (91 loc) · 2.49 KB
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
114
115
116
117
118
119
120
121
122
123
from collections.abc import Callable, Sequence
from dataclasses import dataclass
from enum import StrEnum, auto
# =========================
# Config
# =========================
BORDER_CHAR = "="
BORDER_LENGTH = 50
# =========================
# Message Types
# =========================
class MessageType(StrEnum):
INFO = auto()
SUCCESS = auto()
WARNING = auto()
NOTE = auto()
ERROR = auto()
MESSAGE_PREFIXES = {
MessageType.INFO: "",
MessageType.SUCCESS: "[✔] SUCCESS: ",
MessageType.WARNING: "[⚠️] WARNING: ",
MessageType.NOTE: "[!] NOTE: ",
MessageType.ERROR: "[✖] ERROR: "
}
# =========================
# Display Functions
# =========================
def display_divider():
print(BORDER_CHAR * BORDER_LENGTH)
def display_message(message: str, message_type: MessageType = MessageType.INFO):
prefix = MESSAGE_PREFIXES.get(message_type, "")
print(f"{prefix}{message}")
def display_header(title: str):
display_divider()
print(f"{title:^{BORDER_LENGTH}}")
display_divider()
# =========================
# UI Components
# =========================
# -- Menu Component --
@dataclass
class MenuOption:
"""
Represents a selectable menu option.
"""
label: str
action: Callable[[], None]
@dataclass
class Menu:
"""
Represents a terminal menu component.
"""
title: str
options: Sequence[MenuOption]
def display_menu(menu: Menu) -> None:
"""
Render a menu component to the terminal.
Args:
menu:
The menu instance to display.
"""
display_header(menu.title)
max_digits = len(str(len(menu.options)))
for index, option in enumerate(menu.options, start=1):
print(
f"{index:0{max_digits}d}. "
f"{option.label}"
)
# =========================
# Input Handling
# =========================
def prompt_menu_selection(menu: Menu) -> MenuOption:
"""
Prompt the user to select a menu option.
Args:
menu:
The menu instance to interact with.
Returns:
MenuOption:
The selected menu option.
"""
while True:
try:
choice = int(
input("\nEnter your choice: ")
)
if 1 <= choice <= len(menu.options):
return menu.options[choice - 1]
display_message("Choice out of range.", MessageType.WARNING)
except ValueError:
display_message("Enter a valid number.", MessageType.WARNING)