PyTermGUI 7.7.1__tar.gz → 7.7.3__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.
Files changed (79) hide show
  1. {pytermgui-7.7.1 → pytermgui-7.7.3}/CHANGELOG.md +10 -2
  2. {pytermgui-7.7.1 → pytermgui-7.7.3}/PKG-INFO +6 -8
  3. {pytermgui-7.7.1 → pytermgui-7.7.3}/pyproject.toml +1 -3
  4. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/__init__.py +1 -1
  5. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/input.py +17 -17
  6. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/regex.py +1 -1
  7. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/collapsible.py +8 -0
  8. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/window_manager/layouts.py +2 -1
  9. pytermgui-7.7.1/pytermgui/cell.py +0 -320
  10. pytermgui-7.7.1/tests/not_test_cell.py +0 -125
  11. {pytermgui-7.7.1 → pytermgui-7.7.3}/.gitignore +0 -0
  12. {pytermgui-7.7.1 → pytermgui-7.7.3}/LICENSE +0 -0
  13. {pytermgui-7.7.1 → pytermgui-7.7.3}/README.md +0 -0
  14. {pytermgui-7.7.1 → pytermgui-7.7.3}/examples/README.md +0 -0
  15. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/animations.py +0 -0
  16. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/ansi_interface.py +0 -0
  17. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/cmd.py +0 -0
  18. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/color_info.py +0 -0
  19. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/colors.py +0 -0
  20. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/context_managers.py +0 -0
  21. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/enums.py +0 -0
  22. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/exceptions.py +0 -0
  23. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/exporters.py +0 -0
  24. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/fancy_repr.py +0 -0
  25. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/file_loaders.py +0 -0
  26. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/helpers.py +0 -0
  27. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/highlighters.py +0 -0
  28. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/inspector.py +0 -0
  29. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/markup/__init__.py +0 -0
  30. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/markup/aliases.py +0 -0
  31. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/markup/language.py +0 -0
  32. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/markup/macros.py +0 -0
  33. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/markup/parsing.py +0 -0
  34. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/markup/style_maps.py +0 -0
  35. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/markup/tokens.py +0 -0
  36. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/palettes.py +0 -0
  37. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/prettifiers.py +0 -0
  38. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/pretty.py +0 -0
  39. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/py.typed +0 -0
  40. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/serialization.py +0 -0
  41. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/term.py +0 -0
  42. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/__init__.py +0 -0
  43. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/base.py +0 -0
  44. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/boxes.py +0 -0
  45. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/button.py +0 -0
  46. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/checkbox.py +0 -0
  47. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/color_picker.py +0 -0
  48. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/containers.py +0 -0
  49. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/fancy_repr.py +0 -0
  50. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/frames.py +0 -0
  51. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/inline.py +0 -0
  52. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/input_field.py +0 -0
  53. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/keyboard_button.py +0 -0
  54. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/pixel_matrix.py +0 -0
  55. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/slider.py +0 -0
  56. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/styles.py +0 -0
  57. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/widgets/toggle.py +0 -0
  58. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/win32console.py +0 -0
  59. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/window_manager/__init__.py +0 -0
  60. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/window_manager/compositor.py +0 -0
  61. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/window_manager/manager.py +0 -0
  62. {pytermgui-7.7.1 → pytermgui-7.7.3}/pytermgui/window_manager/window.py +0 -0
  63. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/__init__.py +0 -0
  64. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/_exporter_targets.py +0 -0
  65. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/colorgrids.py +0 -0
  66. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test.json +0 -0
  67. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test.yaml +0 -0
  68. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_animations.py +0 -0
  69. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_auto.py +0 -0
  70. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_colors.py +0 -0
  71. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_dump_n_load.py +0 -0
  72. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_exporters.py +0 -0
  73. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_fancy_repr.py +0 -0
  74. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_helpers.py +0 -0
  75. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_highlight_markup_literal.py +0 -0
  76. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_layouts.py +0 -0
  77. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_parser.py +0 -0
  78. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_regex.py +0 -0
  79. {pytermgui-7.7.1 → pytermgui-7.7.3}/tests/test_styles.py +0 -0
@@ -1,3 +1,12 @@
1
+ ## [7.7.2] - 2024-08-31
2
+
3
+ ### Additions
4
+
5
+ - Stop allowing selection of closed Collapsible's items
6
+ - Clarify some documentation
7
+
8
+ <!-- HATCH README END -->
9
+
1
10
  ## [7.7.1] - 2024-02-24
2
11
 
3
12
  ### Additions
@@ -8,8 +17,6 @@
8
17
 
9
18
  - Fix `inline` widget referring to `hover` mouse mode
10
19
 
11
- <!-- HATCH README END -->
12
-
13
20
  ## [7.7.0] - 2023-09-06
14
21
 
15
22
  ### Additions
@@ -403,6 +410,7 @@
403
410
 
404
411
  <!-- HATCH URI DEFINITIONS START -->
405
412
 
413
+ [7.7.2]: https://github.com/bczsalba/pytermgui/compare/v7.7.1...v7.7.2
406
414
  [7.7.1]: https://github.com/bczsalba/pytermgui/compare/v7.7.0...v7.7.1
407
415
  [7.7.0]: https://github.com/bczsalba/pytermgui/compare/v7.6.0...v7.7.0
408
416
  [7.6.0]: https://github.com/bczsalba/pytermgui/compare/v7.5.0...v7.6.0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: PyTermGUI
3
- Version: 7.7.1
3
+ Version: 7.7.3
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
@@ -99,21 +99,19 @@ Additionally, there are a couple of neat tools to make your general Python devel
99
99
 
100
100
  ## Latest release
101
101
 
102
- ### [7.7.1] - 2024-02-24
102
+ ### [7.7.2] - 2024-08-31
103
103
 
104
104
  ### Additions
105
105
 
106
- - Add support for wide characters
107
-
108
- ### Bugfixes
109
-
110
- - Fix `inline` widget referring to `hover` mouse mode
106
+ - Stop allowing selection of closed Collapsible's items
107
+ - Clarify some documentation
111
108
 
112
109
 
113
110
  Read the full changelog [here](https://github.com/bczsalba/pytermgui/blob/master/CHANGELOG.md).
114
111
 
115
112
 
116
113
 
114
+ [7.7.2]: https://github.com/bczsalba/pytermgui/compare/v7.7.1...v7.7.2
117
115
  [7.7.1]: https://github.com/bczsalba/pytermgui/compare/v7.7.0...v7.7.1
118
116
  [7.7.0]: https://github.com/bczsalba/pytermgui/compare/v7.6.0...v7.7.0
119
117
  [7.6.0]: https://github.com/bczsalba/pytermgui/compare/v7.5.0...v7.6.0
@@ -1,7 +1,5 @@
1
1
  [build-system]
2
- # requires = ["setuptools", "setuptools-scm"]
3
- # build-backend = "setuptools.build_meta"
4
- requires = ["hatchling", "hatch-fancy-pypi-readme"]
2
+ requires = ["hatchling >= 1.26.1", "hatch-fancy-pypi-readme"]
5
3
  build-backend = "hatchling.build"
6
4
 
7
5
  [project]
@@ -40,7 +40,7 @@ if "-m" in sys.argv: # pragma: no cover
40
40
 
41
41
  warnings.filterwarnings("ignore")
42
42
 
43
- __version__ = "7.7.1"
43
+ __version__ = "7.7.3"
44
44
 
45
45
 
46
46
  def auto(data: Any, **widget_args: Any) -> Optional[Widget | list[Splitter]]:
@@ -331,25 +331,25 @@ try:
331
331
  # TODO: Add shift+arrow keys
332
332
  _platform_keys = {
333
333
  "ESC": "\x1b",
334
- "LEFT": "\x1bK",
335
- "RIGHT": "\x1bM",
336
- "UP": "\x1bH",
337
- "DOWN": "\x1bP",
334
+ "LEFT": "\x1b[D",
335
+ "RIGHT": "\x1b[C",
336
+ "UP": "\x1b[A",
337
+ "DOWN": "\x1b[B",
338
338
  "ENTER": "\r",
339
339
  "RETURN": "\r",
340
- "BACKSPACE": "\x08",
341
- "F1": "\x00;",
342
- "F2": "\x00<",
343
- "F3": "\x00=",
344
- "F4": "\x00>",
345
- "F5": "\x00?",
346
- "F6": "\x00@",
347
- "F7": "\x00A",
348
- "F8": "\x00B",
349
- "F9": "\x00C",
350
- "F10": "\x00D",
351
- "F11": "\xe0\x85",
352
- "F12": "\xe0\x86",
340
+ "BACKSPACE": "\x7f",
341
+ "F1": "\x1bOP",
342
+ "F2": "\x1bOQ",
343
+ "F3": "\x1bOR",
344
+ "F4": "\x1bOS",
345
+ "F5": "\x1b[15~",
346
+ "F6": "\x1b[17~",
347
+ "F7": "\x1b[18~",
348
+ "F8": "\x1b[19~",
349
+ "F9": "\x1b[20~",
350
+ "F10": "\x1b[21~",
351
+ "F11": "\x1b[23~",
352
+ "F12": "\x1b[24~",
353
353
  }
354
354
 
355
355
  _getch = _GetchWindows()
@@ -71,7 +71,7 @@ def real_length(text: str) -> int:
71
71
  The display-length of text.
72
72
  """
73
73
 
74
- return wcswidth(strip_ansi(text))
74
+ return max(wcswidth(strip_ansi(text)), 0)
75
75
 
76
76
 
77
77
  def escape_markup(text: str) -> str:
@@ -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. Used for display purposes.
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