PyTermGUI 7.7.1__tar.gz → 7.7.2__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.
- {pytermgui-7.7.1 → pytermgui-7.7.2}/PKG-INFO +1 -1
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/__init__.py +1 -1
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/regex.py +1 -1
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/collapsible.py +8 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/window_manager/layouts.py +2 -1
- pytermgui-7.7.1/pytermgui/cell.py +0 -320
- pytermgui-7.7.1/tests/not_test_cell.py +0 -125
- {pytermgui-7.7.1 → pytermgui-7.7.2}/.gitignore +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/CHANGELOG.md +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/LICENSE +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/README.md +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/examples/README.md +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pyproject.toml +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/animations.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/ansi_interface.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/cmd.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/color_info.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/colors.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/context_managers.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/enums.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/exceptions.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/exporters.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/fancy_repr.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/file_loaders.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/helpers.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/highlighters.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/input.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/inspector.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/markup/__init__.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/markup/aliases.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/markup/language.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/markup/macros.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/markup/parsing.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/markup/style_maps.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/markup/tokens.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/palettes.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/prettifiers.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/pretty.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/py.typed +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/serialization.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/term.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/__init__.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/base.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/boxes.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/button.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/checkbox.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/color_picker.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/containers.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/fancy_repr.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/frames.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/inline.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/input_field.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/keyboard_button.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/pixel_matrix.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/slider.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/styles.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/widgets/toggle.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/win32console.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/window_manager/__init__.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/window_manager/compositor.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/window_manager/manager.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/pytermgui/window_manager/window.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/__init__.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/_exporter_targets.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/colorgrids.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test.json +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test.yaml +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_animations.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_auto.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_colors.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_dump_n_load.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_exporters.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_fancy_repr.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_helpers.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_highlight_markup_literal.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_layouts.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_parser.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_regex.py +0 -0
- {pytermgui-7.7.1 → pytermgui-7.7.2}/tests/test_styles.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyTermGUI
|
|
3
|
-
Version: 7.7.
|
|
3
|
+
Version: 7.7.2
|
|
4
4
|
Summary: Python TUI framework with mouse support, modular widget system, customizable and rapid terminal markup language and more!
|
|
5
5
|
Project-URL: homepage, https://github.com/bczsalba/PyTermGUI
|
|
6
6
|
Project-URL: repository, https://github.com/bczsalba/PyTermGUI
|
|
@@ -7,6 +7,7 @@ from typing import Any
|
|
|
7
7
|
from ..enums import Overflow
|
|
8
8
|
from ..input import keys
|
|
9
9
|
from .containers import Container
|
|
10
|
+
from .base import Widget
|
|
10
11
|
from .toggle import Toggle
|
|
11
12
|
|
|
12
13
|
__all__ = ["Collapsible"]
|
|
@@ -53,6 +54,13 @@ class Collapsible(Container):
|
|
|
53
54
|
|
|
54
55
|
self._is_expanded = False
|
|
55
56
|
|
|
57
|
+
@property
|
|
58
|
+
def selectables(self) -> list[tuple[Widget, int]]:
|
|
59
|
+
if self._is_expanded:
|
|
60
|
+
return super().selectables
|
|
61
|
+
|
|
62
|
+
return [(self.trigger, 0)]
|
|
63
|
+
|
|
56
64
|
def toggle(self) -> Collapsible:
|
|
57
65
|
"""Toggles expanded state.
|
|
58
66
|
|
|
@@ -285,7 +285,8 @@ class Layout:
|
|
|
285
285
|
"""Adds a new slot to the layout.
|
|
286
286
|
|
|
287
287
|
Args:
|
|
288
|
-
name: The name of the slot.
|
|
288
|
+
name: The **snakeified** name of the slot. A non-snake case name
|
|
289
|
+
will cause issues when trying to retrieve the slot (see GH#147).
|
|
289
290
|
slot: An already instantiated `Slot` instance. If this is given,
|
|
290
291
|
the additional width & height arguments will be ignored.
|
|
291
292
|
width: The width for the new slot. See below for special types.
|
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
|
|
5
|
-
STYLE_START_CHAR = "\x1b"
|
|
6
|
-
STYLE_END_CHAR = "m"
|
|
7
|
-
|
|
8
|
-
UNSETTER_LOOKUP = {
|
|
9
|
-
**{"38": {"39"}, "48": {"49"}, "54": {"53"}},
|
|
10
|
-
**{str(index + 20): {str(index)} for index in range(1, 10)},
|
|
11
|
-
**{"22": {"1", "2"}},
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def _is_color(style: str) -> bool:
|
|
16
|
-
"""Determines whether the given style string is a color."""
|
|
17
|
-
|
|
18
|
-
if style.startswith(("38", "48")):
|
|
19
|
-
return True
|
|
20
|
-
|
|
21
|
-
if style.isdigit():
|
|
22
|
-
index = int(style)
|
|
23
|
-
|
|
24
|
-
if 30 <= index <= 38 or 40 <= index <= 48:
|
|
25
|
-
return True
|
|
26
|
-
|
|
27
|
-
return False
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _process_styles(styles: list[str]) -> tuple[list[str], str, str]:
|
|
31
|
-
"""Processes the given styles, cleaning them up along the way.
|
|
32
|
-
|
|
33
|
-
This does 2 major tasks:
|
|
34
|
-
|
|
35
|
-
- Remove styles that are unset by later ones
|
|
36
|
-
- Extract the fore and background colors, returning them
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
style: The list of styles to process.
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
A tuple containing:
|
|
43
|
-
|
|
44
|
-
- The processed list of styles
|
|
45
|
-
- The foreground color
|
|
46
|
-
- The background color
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
# TODO: Split up compound styles before handling them.
|
|
50
|
-
|
|
51
|
-
applied = []
|
|
52
|
-
foreground = ""
|
|
53
|
-
background = ""
|
|
54
|
-
|
|
55
|
-
for style in styles:
|
|
56
|
-
unset_styles = UNSETTER_LOOKUP.get(style)
|
|
57
|
-
|
|
58
|
-
if _is_color(style):
|
|
59
|
-
if style.startswith("3"):
|
|
60
|
-
foreground = style
|
|
61
|
-
else:
|
|
62
|
-
background = style
|
|
63
|
-
|
|
64
|
-
continue
|
|
65
|
-
|
|
66
|
-
if style == "39":
|
|
67
|
-
foreground = ""
|
|
68
|
-
|
|
69
|
-
elif style == "49":
|
|
70
|
-
background = ""
|
|
71
|
-
|
|
72
|
-
elif style == "0":
|
|
73
|
-
applied = []
|
|
74
|
-
foreground = ""
|
|
75
|
-
background = ""
|
|
76
|
-
|
|
77
|
-
elif unset_styles is not None:
|
|
78
|
-
for unset_style in unset_styles:
|
|
79
|
-
if unset_style in applied:
|
|
80
|
-
applied.remove(unset_style)
|
|
81
|
-
|
|
82
|
-
if style not in applied:
|
|
83
|
-
applied.append(style)
|
|
84
|
-
|
|
85
|
-
if foreground:
|
|
86
|
-
applied.append(foreground)
|
|
87
|
-
|
|
88
|
-
if background:
|
|
89
|
-
applied.append(background)
|
|
90
|
-
|
|
91
|
-
return applied, foreground, background
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def _split(styles: str) -> list[str]:
|
|
95
|
-
"""Splits styles into a list of their content.
|
|
96
|
-
|
|
97
|
-
For example, `\x1b[38;5;141;1m` would become `38;5;141` and `1`.
|
|
98
|
-
"""
|
|
99
|
-
|
|
100
|
-
return [style.rstrip("m") for style in styles.split("\x1b[")[1:]]
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def _combine(styles: list[str]) -> str:
|
|
104
|
-
"""Combines a list of style content into a compound style setter.
|
|
105
|
-
|
|
106
|
-
Instead of doing individual tags, it puts all of the styles into
|
|
107
|
-
a single unit, i.e. instead of `\x1b[38;5;141m\x1b[1m` it returns
|
|
108
|
-
`\x1b[38;5;141;1m`.
|
|
109
|
-
"""
|
|
110
|
-
|
|
111
|
-
if len(styles) == 0:
|
|
112
|
-
return ""
|
|
113
|
-
|
|
114
|
-
return f"\x1b[{';'.join(styles)}{STYLE_END_CHAR}"
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
@dataclass
|
|
118
|
-
class Cell:
|
|
119
|
-
"""A unit that represents a single, possibly ANSI-styled character."""
|
|
120
|
-
|
|
121
|
-
char: str = ""
|
|
122
|
-
styles: str = ""
|
|
123
|
-
|
|
124
|
-
_foreground: str | None = None
|
|
125
|
-
_background: str | None = None
|
|
126
|
-
|
|
127
|
-
def __str__(self) -> str:
|
|
128
|
-
"""Gets the content of this cell."""
|
|
129
|
-
|
|
130
|
-
return self.styles + self.char
|
|
131
|
-
|
|
132
|
-
@property
|
|
133
|
-
def foreground(self) -> str | None:
|
|
134
|
-
"""Returns the currently set foreground color in this cell."""
|
|
135
|
-
|
|
136
|
-
return self._foreground
|
|
137
|
-
|
|
138
|
-
@foreground.setter
|
|
139
|
-
def foreground(self, new: str | None) -> None:
|
|
140
|
-
"""Allows setting the foreground color, while unsetting the current one."""
|
|
141
|
-
|
|
142
|
-
current = self.foreground
|
|
143
|
-
|
|
144
|
-
if current is not None:
|
|
145
|
-
self.styles = self.styles.replace(current, "")
|
|
146
|
-
|
|
147
|
-
self.styles += new
|
|
148
|
-
|
|
149
|
-
if new is not None:
|
|
150
|
-
self._foreground = f"\x1b[{new}m"
|
|
151
|
-
|
|
152
|
-
else:
|
|
153
|
-
self._foreground = None
|
|
154
|
-
|
|
155
|
-
@property
|
|
156
|
-
def background(self) -> str | None:
|
|
157
|
-
"""Returns the currently set background color in this cell."""
|
|
158
|
-
|
|
159
|
-
return self._background
|
|
160
|
-
|
|
161
|
-
@background.setter
|
|
162
|
-
def background(self, new: str | None) -> None:
|
|
163
|
-
"""Allows setting the background color, while unsetting the current one."""
|
|
164
|
-
|
|
165
|
-
current = self.background
|
|
166
|
-
|
|
167
|
-
if current is not None:
|
|
168
|
-
self.styles = self.styles.replace(current, "")
|
|
169
|
-
|
|
170
|
-
self.styles += new
|
|
171
|
-
|
|
172
|
-
if new is not None:
|
|
173
|
-
self._background = f"\x1b[{new}m"
|
|
174
|
-
|
|
175
|
-
else:
|
|
176
|
-
self._background = None
|
|
177
|
-
|
|
178
|
-
@classmethod
|
|
179
|
-
def from_line(cls, line: str, styles: str = "") -> Generator[None, None, Cell]:
|
|
180
|
-
"""Yields Cells generated from a line of text.
|
|
181
|
-
|
|
182
|
-
Args:
|
|
183
|
-
line: The input line. May or may not contain ANSI styles.
|
|
184
|
-
styles: Styles that are applied to the line from before the line started.
|
|
185
|
-
|
|
186
|
-
Returns:
|
|
187
|
-
A Cell generator.
|
|
188
|
-
"""
|
|
189
|
-
|
|
190
|
-
in_style = False
|
|
191
|
-
current_style = styles
|
|
192
|
-
current_styles = []
|
|
193
|
-
|
|
194
|
-
for char in line:
|
|
195
|
-
if char == STYLE_START_CHAR:
|
|
196
|
-
in_style = True
|
|
197
|
-
current_style = ""
|
|
198
|
-
continue
|
|
199
|
-
|
|
200
|
-
if in_style:
|
|
201
|
-
if char == "[":
|
|
202
|
-
continue
|
|
203
|
-
|
|
204
|
-
if char == STYLE_END_CHAR:
|
|
205
|
-
current_styles.append(current_style)
|
|
206
|
-
in_style = False
|
|
207
|
-
continue
|
|
208
|
-
|
|
209
|
-
current_style += char
|
|
210
|
-
continue
|
|
211
|
-
|
|
212
|
-
current_styles, fore, back = _process_styles(current_styles)
|
|
213
|
-
|
|
214
|
-
yield Cell(char or " ", _combine(current_styles), fore, back)
|
|
215
|
-
|
|
216
|
-
if current_style != "":
|
|
217
|
-
current_styles, fore, back = _process_styles(
|
|
218
|
-
current_styles + [current_style]
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
yield Cell("", _combine(current_styles), fore, back)
|
|
222
|
-
|
|
223
|
-
@classmethod
|
|
224
|
-
def first_of(cls, line: str) -> Cell:
|
|
225
|
-
"""Returns the first cell yielded by `from_line`."""
|
|
226
|
-
|
|
227
|
-
for cell in cls.from_line(line):
|
|
228
|
-
return cell
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
class CellMatrix:
|
|
232
|
-
def __init__(self, width: int, height: int, default: str = " ") -> None:
|
|
233
|
-
self._cells = []
|
|
234
|
-
self._changes: list[tuple[int, int, Cell]] = []
|
|
235
|
-
|
|
236
|
-
for _ in range(height):
|
|
237
|
-
row = []
|
|
238
|
-
|
|
239
|
-
for _ in range(width):
|
|
240
|
-
row.append(Cell(default))
|
|
241
|
-
|
|
242
|
-
self._cells.append(row)
|
|
243
|
-
|
|
244
|
-
self.width = width
|
|
245
|
-
self.height = height
|
|
246
|
-
|
|
247
|
-
def resize(self, width: int, height: int, default: str = " ") -> None:
|
|
248
|
-
diff_cols = width - self.width
|
|
249
|
-
diff_rows = height - self.height
|
|
250
|
-
|
|
251
|
-
default_cell = list(Cell.from_line(default))[0]
|
|
252
|
-
|
|
253
|
-
for row in range(self.height):
|
|
254
|
-
self._cells[row].extend([default_cell] * diff_cols)
|
|
255
|
-
|
|
256
|
-
for _ in range(diff_rows):
|
|
257
|
-
self._cells.append([default_cell] * (width))
|
|
258
|
-
|
|
259
|
-
self.width, self.height = width, height
|
|
260
|
-
|
|
261
|
-
def add_text(
|
|
262
|
-
self, text: str, offset: tuple[int, int] = (0, 0), register_change: bool = True
|
|
263
|
-
) -> None:
|
|
264
|
-
x, y = offset
|
|
265
|
-
|
|
266
|
-
if y >= self.height:
|
|
267
|
-
return
|
|
268
|
-
|
|
269
|
-
for i, cell in enumerate(Cell.from_line(text)):
|
|
270
|
-
# Sometimes some styles (usually the global unsetter 0) are left over at
|
|
271
|
-
# the end of a line of text. These are dealt with here; overflowing either
|
|
272
|
-
# into the next cell in the same row, or the first cell of the next one.
|
|
273
|
-
if cell.char == "":
|
|
274
|
-
if x + i < self.width:
|
|
275
|
-
self._cells[y][x + i].styles += cell.styles
|
|
276
|
-
|
|
277
|
-
else:
|
|
278
|
-
if y + 1 >= self.height:
|
|
279
|
-
continue
|
|
280
|
-
|
|
281
|
-
self._cells[y + 1][0].styles += cell.styles
|
|
282
|
-
|
|
283
|
-
continue
|
|
284
|
-
|
|
285
|
-
if self.width <= x + i:
|
|
286
|
-
break
|
|
287
|
-
|
|
288
|
-
if self._cells[y][x + i] != cell:
|
|
289
|
-
self._cells[y][x + i] = cell
|
|
290
|
-
|
|
291
|
-
if register_change:
|
|
292
|
-
self._changes.append((x + i, y, cell))
|
|
293
|
-
|
|
294
|
-
def fill(self, content: str) -> None:
|
|
295
|
-
for row in range(self.height):
|
|
296
|
-
for col in range(self.width):
|
|
297
|
-
self.add_text(content, (col, row), register_change=False)
|
|
298
|
-
|
|
299
|
-
def build_lines(self) -> list[str]:
|
|
300
|
-
lines = []
|
|
301
|
-
for row in self._cells:
|
|
302
|
-
buff = ""
|
|
303
|
-
|
|
304
|
-
for cell in row:
|
|
305
|
-
buff += str(cell)
|
|
306
|
-
|
|
307
|
-
lines.append(buff + "")
|
|
308
|
-
|
|
309
|
-
return lines
|
|
310
|
-
|
|
311
|
-
def get_diff(self, origin: tuple[int, int] = (1, 1)) -> tuple[str, int]:
|
|
312
|
-
buff = ""
|
|
313
|
-
|
|
314
|
-
for x, y, cell in self._changes:
|
|
315
|
-
buff += f"\x1b[{y + origin[1]};{x + origin[0]}H{cell}"
|
|
316
|
-
|
|
317
|
-
change_count = len(self._changes)
|
|
318
|
-
self._changes.clear()
|
|
319
|
-
|
|
320
|
-
return buff, change_count
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
from pytermgui.cell import (
|
|
2
|
-
Cell,
|
|
3
|
-
CellMatrix,
|
|
4
|
-
_is_color,
|
|
5
|
-
_combine,
|
|
6
|
-
_split,
|
|
7
|
-
_process_styles,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def test_is_color():
|
|
12
|
-
assert _is_color("31")
|
|
13
|
-
assert _is_color("45")
|
|
14
|
-
assert _is_color("38;5;141")
|
|
15
|
-
assert _is_color("48;5;61")
|
|
16
|
-
assert _is_color("38;2;22;33;44")
|
|
17
|
-
assert _is_color("48;2;22;33;44")
|
|
18
|
-
|
|
19
|
-
assert not _is_color("1")
|
|
20
|
-
assert not _is_color("9")
|
|
21
|
-
assert not _is_color("22")
|
|
22
|
-
assert not _is_color("1")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def test_combine():
|
|
26
|
-
assert _combine(["41", "38;5;141", "1"]) == "\x1b[41;38;5;141;1m"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def test_split():
|
|
30
|
-
assert _split("\x1b[38;2;53;12;123m\x1b[1m\x1b[9m\x1b[48;2;21;21;21m") == [
|
|
31
|
-
"38;2;53;12;123",
|
|
32
|
-
"1",
|
|
33
|
-
"9",
|
|
34
|
-
"48;2;21;21;21",
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_process_styles():
|
|
39
|
-
tests = [
|
|
40
|
-
# Bold & Dim both get unset by the same unsetter
|
|
41
|
-
(["38;5;141", "1", "9", "2", "22"], {"38;5;141", "9", "22"}),
|
|
42
|
-
# 0 unsets everything
|
|
43
|
-
(["38;2;66;66;66", "1", "0"], {"0"}),
|
|
44
|
-
# Duplicates are ignored
|
|
45
|
-
(["1", "1", "9"], {"1", "9"}),
|
|
46
|
-
# Foreground colors work; background ones unset
|
|
47
|
-
(["38;5;131", "1", "41", "49"], {"38;5;131", "1", "49"}),
|
|
48
|
-
# Background colors work; foreground ones unset
|
|
49
|
-
(["48;2;33;44;55", "7", "35", "39"], {"48;2;33;44;55", "7", "39"}),
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
for target, expected in tests:
|
|
53
|
-
output = _process_styles(target)[0]
|
|
54
|
-
|
|
55
|
-
if len(set(output)) != len(output):
|
|
56
|
-
raise ValueError(f"Output {output} contains duplicates.")
|
|
57
|
-
|
|
58
|
-
assert set(output) == expected
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_cell_from_line():
|
|
62
|
-
tests = [
|
|
63
|
-
# General example
|
|
64
|
-
(
|
|
65
|
-
"\x1b[38;5;141\x1b[1mHello\x1b[7mThere",
|
|
66
|
-
[
|
|
67
|
-
Cell("H", "\x1b[38;5;141;1m", "38;5;141"),
|
|
68
|
-
Cell("e", "\x1b[38;5;141;1m", "38;5;141"),
|
|
69
|
-
Cell("l", "\x1b[38;5;141;1m", "38;5;141"),
|
|
70
|
-
Cell("l", "\x1b[38;5;141;1m", "38;5;141"),
|
|
71
|
-
Cell("o", "\x1b[38;5;141;1m", "38;5;141"),
|
|
72
|
-
Cell("T", "\x1b[7;38;5;141;1m", "38;5;141"),
|
|
73
|
-
Cell("h", "\x1b[7;38;5;141;1m", "38;5;141"),
|
|
74
|
-
Cell("e", "\x1b[7;38;5;141;1m", "38;5;141"),
|
|
75
|
-
Cell("r", "\x1b[7;38;5;141;1m", "38;5;141"),
|
|
76
|
-
Cell("e", "\x1b[7;38;5;141;1m", "38;5;141"),
|
|
77
|
-
# Trailing style
|
|
78
|
-
Cell("", "\x1b[7;38;5;141;1m", "38;5;141"),
|
|
79
|
-
],
|
|
80
|
-
),
|
|
81
|
-
# Achy breaky code (chode)
|
|
82
|
-
# Unsetters & trailing
|
|
83
|
-
(
|
|
84
|
-
"\x1b[1m\x1b[33m1\x1b[22m2\x1b[0m",
|
|
85
|
-
[
|
|
86
|
-
Cell(char="1", styles="\x1b[1;33m"),
|
|
87
|
-
Cell(char="2", styles="\x1b[22;33m"),
|
|
88
|
-
Cell(char="", styles="\x1b[0m"),
|
|
89
|
-
],
|
|
90
|
-
),
|
|
91
|
-
# (Future) Compound styles
|
|
92
|
-
# (
|
|
93
|
-
# "\x1b[1;33m1\x1b[22m2\x1b[0m",
|
|
94
|
-
# [
|
|
95
|
-
# Cell(char="1", styles="\x1b[1;33m"),
|
|
96
|
-
# Cell(char="2", styles="\x1b[22;33m"),
|
|
97
|
-
# Cell(char="", styles="\x1b[0m"),
|
|
98
|
-
# ],
|
|
99
|
-
# ),
|
|
100
|
-
]
|
|
101
|
-
|
|
102
|
-
for target, expected in tests:
|
|
103
|
-
assert list(Cell.from_line(target)) == expected
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def test_cell_foreground():
|
|
107
|
-
assert Cell.first_of("\x1b[38;5;141mHello there").foreground == "\x1b[38;5;141m"
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def test_cell_matrix_resize():
|
|
111
|
-
mat = CellMatrix(5, 5, default="x")
|
|
112
|
-
mat.resize(10, 10, default="O")
|
|
113
|
-
|
|
114
|
-
assert mat.build_lines() == [
|
|
115
|
-
"xxxxxOOOOO",
|
|
116
|
-
"xxxxxOOOOO",
|
|
117
|
-
"xxxxxOOOOO",
|
|
118
|
-
"xxxxxOOOOO",
|
|
119
|
-
"xxxxxOOOOO",
|
|
120
|
-
"OOOOOOOOOO",
|
|
121
|
-
"OOOOOOOOOO",
|
|
122
|
-
"OOOOOOOOOO",
|
|
123
|
-
"OOOOOOOOOO",
|
|
124
|
-
"OOOOOOOOOO",
|
|
125
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|