make-selection 1.0.2__tar.gz → 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {make_selection-1.0.2 → make_selection-1.0.4}/PKG-INFO +1 -1
- {make_selection-1.0.2 → make_selection-1.0.4}/pyproject.toml +1 -1
- {make_selection-1.0.2 → make_selection-1.0.4}/src/make_selection/make_selection.py +52 -38
- {make_selection-1.0.2 → make_selection-1.0.4}/src/make_selection.egg-info/PKG-INFO +1 -1
- {make_selection-1.0.2 → make_selection-1.0.4}/LICENSE +0 -0
- {make_selection-1.0.2 → make_selection-1.0.4}/README.md +0 -0
- {make_selection-1.0.2 → make_selection-1.0.4}/setup.cfg +0 -0
- {make_selection-1.0.2 → make_selection-1.0.4}/src/make_selection/__init__.py +0 -0
- {make_selection-1.0.2 → make_selection-1.0.4}/src/make_selection.egg-info/SOURCES.txt +0 -0
- {make_selection-1.0.2 → make_selection-1.0.4}/src/make_selection.egg-info/dependency_links.txt +0 -0
- {make_selection-1.0.2 → make_selection-1.0.4}/src/make_selection.egg-info/top_level.txt +0 -0
|
@@ -10,10 +10,11 @@ Ansi escape codes are used as described here: https://gist.github.com/fnky/45871
|
|
|
10
10
|
import sys
|
|
11
11
|
if sys.platform != "win32":
|
|
12
12
|
raise NotImplementedError("This module is only available on Windows.")
|
|
13
|
-
from typing import Any
|
|
14
|
-
from copy import copy
|
|
15
13
|
import msvcrt
|
|
16
14
|
import ctypes
|
|
15
|
+
from typing import Any
|
|
16
|
+
from copy import copy
|
|
17
|
+
from enum import Enum
|
|
17
18
|
|
|
18
19
|
stdout = -11
|
|
19
20
|
enable_ansi_codes = 7
|
|
@@ -38,6 +39,16 @@ BACKSPACE = 8
|
|
|
38
39
|
SPACEBAR = 32
|
|
39
40
|
CTL_RIGHT = 116
|
|
40
41
|
|
|
42
|
+
class Mode(Enum):
|
|
43
|
+
NORMAL = 0
|
|
44
|
+
MULTI_SELECT = 1
|
|
45
|
+
MULTI_DELETE = 2
|
|
46
|
+
|
|
47
|
+
class Option:
|
|
48
|
+
def __init__(self, obj: Any, sub_string_start: int=0) -> None:
|
|
49
|
+
self.value = obj
|
|
50
|
+
self.sub_string_start = sub_string_start
|
|
51
|
+
|
|
41
52
|
class Menu:
|
|
42
53
|
def __init__(self, options: list, label: str, window_size: int=10, multi_select: bool=False) -> None:
|
|
43
54
|
assert options
|
|
@@ -45,21 +56,23 @@ class Menu:
|
|
|
45
56
|
assert 1 <= window_size and window_size <= 25
|
|
46
57
|
window_size = min((len(options)), window_size)
|
|
47
58
|
|
|
48
|
-
self.options_original = options
|
|
49
|
-
self.options_current = copy(
|
|
59
|
+
self.options_original = [Option(op) for op in options]
|
|
60
|
+
self.options_current = copy(self.options_original)
|
|
50
61
|
self.options_selected = []
|
|
51
|
-
self.search_indices = []
|
|
52
62
|
self.search_string = ""
|
|
53
63
|
self.label = label
|
|
54
64
|
self.selected_index = 0
|
|
55
|
-
self.multi_select = multi_select
|
|
56
65
|
self.window_top = 0
|
|
57
66
|
self.window_size_original = window_size
|
|
58
67
|
self.window_size_current = window_size
|
|
68
|
+
self.help_string_multi_select = "Enter: Select, Ctl+C: Cancel, Ctl\u2192: Done"
|
|
69
|
+
self.help_string_normal = "Enter: Select, Ctl+C: Cancel"
|
|
59
70
|
if multi_select:
|
|
60
|
-
self.
|
|
71
|
+
self.mode = Mode.MULTI_SELECT
|
|
72
|
+
self.help_string = self.help_string_multi_select
|
|
61
73
|
else:
|
|
62
|
-
self.
|
|
74
|
+
self.mode = Mode.NORMAL
|
|
75
|
+
self.help_string = self.help_string_normal
|
|
63
76
|
|
|
64
77
|
def show(self):
|
|
65
78
|
self.printMenu()
|
|
@@ -68,9 +81,10 @@ class Menu:
|
|
|
68
81
|
char = self.getChar()
|
|
69
82
|
if char == SPECIAL_KEY:
|
|
70
83
|
char = self.getChar()
|
|
71
|
-
if char == CTL_RIGHT
|
|
72
|
-
self.
|
|
73
|
-
|
|
84
|
+
if char == CTL_RIGHT:
|
|
85
|
+
if self.mode == Mode.MULTI_SELECT:
|
|
86
|
+
self.printSelected()
|
|
87
|
+
return self.multiSelectGetValues(self.options_selected)
|
|
74
88
|
elif 1 < len(self.options_current):
|
|
75
89
|
# NOTE: Update selected index
|
|
76
90
|
if char == UP_ARROW:
|
|
@@ -101,12 +115,12 @@ class Menu:
|
|
|
101
115
|
self.search(self.options_original)
|
|
102
116
|
something_changed = True
|
|
103
117
|
elif char == ENTER_KEY and self.options_current:
|
|
104
|
-
if self.
|
|
118
|
+
if self.mode == Mode.MULTI_SELECT:
|
|
105
119
|
self.multiSelectAdd()
|
|
106
120
|
something_changed = True
|
|
107
|
-
|
|
121
|
+
elif self.mode == Mode.NORMAL:
|
|
108
122
|
self.printSelected()
|
|
109
|
-
return self.options_current[self.selected_index]
|
|
123
|
+
return self.options_current[self.selected_index].value
|
|
110
124
|
elif char == CTL_C:
|
|
111
125
|
self.clearMenu()
|
|
112
126
|
print("cancelled")
|
|
@@ -117,15 +131,13 @@ class Menu:
|
|
|
117
131
|
self.printMenu()
|
|
118
132
|
|
|
119
133
|
def search(self, search_list: list) -> None:
|
|
120
|
-
found_indices = []
|
|
121
134
|
found_options = []
|
|
122
135
|
for o in search_list:
|
|
123
|
-
found_i = str(o).lower().find(self.search_string.lower())
|
|
136
|
+
found_i = str(o.value).lower().find(self.search_string.lower())
|
|
124
137
|
if found_i != -1:
|
|
125
|
-
|
|
138
|
+
o.sub_string_start = found_i
|
|
126
139
|
found_options.append(o)
|
|
127
140
|
self.options_current = found_options
|
|
128
|
-
self.search_indices = found_indices
|
|
129
141
|
self.resetWindow()
|
|
130
142
|
|
|
131
143
|
def multiSelectAdd(self) -> None:
|
|
@@ -135,6 +147,9 @@ class Menu:
|
|
|
135
147
|
self.options_selected.append(selected_option)
|
|
136
148
|
self.resetWindow()
|
|
137
149
|
|
|
150
|
+
def multiSelectGetValues(self, options: list[Option]) -> list[Any]:
|
|
151
|
+
return [op.value for op in options]
|
|
152
|
+
|
|
138
153
|
def getChar(self) -> int:
|
|
139
154
|
return ord(msvcrt.getch())
|
|
140
155
|
|
|
@@ -159,8 +174,8 @@ class Menu:
|
|
|
159
174
|
def printMenu(self):
|
|
160
175
|
header = f"{ANSI_BLUE}{self.label}>{ANSI_RESET}{self.search_string}"
|
|
161
176
|
header_num_lines = 1
|
|
162
|
-
if self.
|
|
163
|
-
header += f"\n{ANSI_GREEN}{len(self.options_selected)} items
|
|
177
|
+
if self.mode == Mode.MULTI_SELECT:
|
|
178
|
+
header += f"\n{ANSI_GREEN}{len(self.options_selected)} items in list!{ANSI_RESET}"
|
|
164
179
|
header_num_lines = 2
|
|
165
180
|
print(header)
|
|
166
181
|
|
|
@@ -172,19 +187,16 @@ class Menu:
|
|
|
172
187
|
self.window_size_current = 1
|
|
173
188
|
else:
|
|
174
189
|
for i in range(self.window_top, bottom):
|
|
175
|
-
option_to_print = str(self.options_current[i])
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
option_to_print = f"{opt_beg}{ANSI_MAGENTA}{opt_mid}{ANSI_RESET}{opt_end}"
|
|
186
|
-
elif i == self.selected_index:
|
|
187
|
-
option_to_print = f"{ANSI_HIGHLIGHT}{option_to_print}{ANSI_RESET}"
|
|
190
|
+
option_to_print = str(self.options_current[i].value)
|
|
191
|
+
highlight_beg = self.options_current[i].sub_string_start
|
|
192
|
+
highlight_end = highlight_beg + len(self.search_string)
|
|
193
|
+
opt_beg = option_to_print[0 : highlight_beg]
|
|
194
|
+
opt_mid = option_to_print[highlight_beg : highlight_end]
|
|
195
|
+
opt_end = option_to_print[highlight_end :]
|
|
196
|
+
if i == self.selected_index:
|
|
197
|
+
option_to_print = f"{ANSI_HIGHLIGHT}{opt_beg}{ANSI_HIGHLIGHT_SEARCH_STRING}{opt_mid}{ANSI_HIGHLIGHT}{opt_end}{ANSI_RESET}"
|
|
198
|
+
else:
|
|
199
|
+
option_to_print = f"{opt_beg}{ANSI_MAGENTA}{opt_mid}{ANSI_RESET}{opt_end}"
|
|
188
200
|
print(option_to_print)
|
|
189
201
|
|
|
190
202
|
footer = f"{ANSI_YELLOW}{self.help_string}{ANSI_RESET}\n"
|
|
@@ -193,13 +205,15 @@ class Menu:
|
|
|
193
205
|
|
|
194
206
|
def printSelected(self):
|
|
195
207
|
self.clearMenu()
|
|
196
|
-
if self.
|
|
197
|
-
if len(self.options_selected)
|
|
198
|
-
print(f"{self.label}>
|
|
208
|
+
if self.mode == Mode.MULTI_SELECT:
|
|
209
|
+
if len(self.options_selected) == 0:
|
|
210
|
+
print(f"{self.label}> (0 items) []")
|
|
211
|
+
elif len(self.options_selected) == 1:
|
|
212
|
+
print(f"{self.label}> (1 item) {self.multiSelectGetValues(self.options_selected)}")
|
|
199
213
|
else:
|
|
200
|
-
print(f"{self.label}>
|
|
214
|
+
print(f"{self.label}> ({len(self.options_selected)} items) [{self.options_selected[0].value}, ...]")
|
|
201
215
|
else:
|
|
202
|
-
print(f"{self.label}> {self.options_current[self.selected_index]}")
|
|
216
|
+
print(f"{self.label}> {self.options_current[self.selected_index].value}")
|
|
203
217
|
|
|
204
218
|
def makeSelection(options: list[Any], label: str, window_size: int=None, multi_select: bool=False) -> Any:
|
|
205
219
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{make_selection-1.0.2 → make_selection-1.0.4}/src/make_selection.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|