urwid 2.6.0.post0__py3-none-any.whl

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.

Potentially problematic release.


This version of urwid might be problematic. Click here for more details.

Files changed (75) hide show
  1. urwid/__init__.py +333 -0
  2. urwid/canvas.py +1413 -0
  3. urwid/command_map.py +137 -0
  4. urwid/container.py +59 -0
  5. urwid/decoration.py +65 -0
  6. urwid/display/__init__.py +97 -0
  7. urwid/display/_posix_raw_display.py +413 -0
  8. urwid/display/_raw_display_base.py +914 -0
  9. urwid/display/_web.css +12 -0
  10. urwid/display/_web.js +462 -0
  11. urwid/display/_win32.py +171 -0
  12. urwid/display/_win32_raw_display.py +269 -0
  13. urwid/display/common.py +1219 -0
  14. urwid/display/curses.py +690 -0
  15. urwid/display/escape.py +624 -0
  16. urwid/display/html_fragment.py +251 -0
  17. urwid/display/lcd.py +518 -0
  18. urwid/display/raw.py +37 -0
  19. urwid/display/web.py +636 -0
  20. urwid/event_loop/__init__.py +55 -0
  21. urwid/event_loop/abstract_loop.py +175 -0
  22. urwid/event_loop/asyncio_loop.py +231 -0
  23. urwid/event_loop/glib_loop.py +294 -0
  24. urwid/event_loop/main_loop.py +721 -0
  25. urwid/event_loop/select_loop.py +230 -0
  26. urwid/event_loop/tornado_loop.py +206 -0
  27. urwid/event_loop/trio_loop.py +302 -0
  28. urwid/event_loop/twisted_loop.py +269 -0
  29. urwid/event_loop/zmq_loop.py +275 -0
  30. urwid/font.py +695 -0
  31. urwid/graphics.py +96 -0
  32. urwid/highlight.css +19 -0
  33. urwid/listbox.py +1899 -0
  34. urwid/monitored_list.py +522 -0
  35. urwid/numedit.py +376 -0
  36. urwid/signals.py +330 -0
  37. urwid/split_repr.py +130 -0
  38. urwid/str_util.py +358 -0
  39. urwid/text_layout.py +632 -0
  40. urwid/treetools.py +515 -0
  41. urwid/util.py +557 -0
  42. urwid/version.py +16 -0
  43. urwid/vterm.py +1806 -0
  44. urwid/widget/__init__.py +181 -0
  45. urwid/widget/attr_map.py +161 -0
  46. urwid/widget/attr_wrap.py +140 -0
  47. urwid/widget/bar_graph.py +649 -0
  48. urwid/widget/big_text.py +77 -0
  49. urwid/widget/box_adapter.py +126 -0
  50. urwid/widget/columns.py +1145 -0
  51. urwid/widget/constants.py +574 -0
  52. urwid/widget/container.py +227 -0
  53. urwid/widget/divider.py +110 -0
  54. urwid/widget/edit.py +718 -0
  55. urwid/widget/filler.py +403 -0
  56. urwid/widget/frame.py +539 -0
  57. urwid/widget/grid_flow.py +539 -0
  58. urwid/widget/line_box.py +194 -0
  59. urwid/widget/overlay.py +829 -0
  60. urwid/widget/padding.py +597 -0
  61. urwid/widget/pile.py +971 -0
  62. urwid/widget/popup.py +170 -0
  63. urwid/widget/progress_bar.py +141 -0
  64. urwid/widget/scrollable.py +597 -0
  65. urwid/widget/solid_fill.py +44 -0
  66. urwid/widget/text.py +354 -0
  67. urwid/widget/widget.py +852 -0
  68. urwid/widget/widget_decoration.py +166 -0
  69. urwid/widget/wimp.py +792 -0
  70. urwid/wimp.py +23 -0
  71. urwid-2.6.0.post0.dist-info/COPYING +504 -0
  72. urwid-2.6.0.post0.dist-info/METADATA +332 -0
  73. urwid-2.6.0.post0.dist-info/RECORD +75 -0
  74. urwid-2.6.0.post0.dist-info/WHEEL +5 -0
  75. urwid-2.6.0.post0.dist-info/top_level.txt +1 -0
urwid/widget/text.py ADDED
@@ -0,0 +1,354 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+
5
+ from urwid import text_layout
6
+ from urwid.canvas import apply_text_layout
7
+ from urwid.split_repr import remove_defaults
8
+ from urwid.str_util import calc_width
9
+ from urwid.util import decompose_tagmarkup, get_encoding
10
+
11
+ from .constants import Align, Sizing, WrapMode
12
+ from .widget import Widget, WidgetError
13
+
14
+ if typing.TYPE_CHECKING:
15
+ from collections.abc import Hashable
16
+
17
+ from typing_extensions import Literal
18
+
19
+ from urwid.canvas import TextCanvas
20
+
21
+
22
+ class TextError(WidgetError):
23
+ pass
24
+
25
+
26
+ class Text(Widget):
27
+ """
28
+ a horizontally resizeable text widget
29
+ """
30
+
31
+ _sizing = frozenset([Sizing.FLOW, Sizing.FIXED])
32
+
33
+ ignore_focus = True
34
+ _repr_content_length_max = 140
35
+
36
+ def __init__(
37
+ self,
38
+ markup: str | tuple[Hashable, str] | list[str | tuple[Hashable, str]],
39
+ align: Literal["left", "center", "right"] | Align = Align.LEFT,
40
+ wrap: Literal["space", "any", "clip", "ellipsis"] | WrapMode = WrapMode.SPACE,
41
+ layout: text_layout.TextLayout | None = None,
42
+ ) -> None:
43
+ """
44
+ :param markup: content of text widget, one of:
45
+
46
+ bytes or unicode
47
+ text to be displayed
48
+
49
+ (*display attribute*, *text markup*)
50
+ *text markup* with *display attribute* applied to all parts
51
+ of *text markup* with no display attribute already applied
52
+
53
+ [*text markup*, *text markup*, ... ]
54
+ all *text markup* in the list joined together
55
+
56
+ :type markup: :ref:`text-markup`
57
+ :param align: typically ``'left'``, ``'center'`` or ``'right'``
58
+ :type align: text alignment mode
59
+ :param wrap: typically ``'space'``, ``'any'``, ``'clip'`` or ``'ellipsis'``
60
+ :type wrap: text wrapping mode
61
+ :param layout: defaults to a shared :class:`StandardTextLayout` instance
62
+ :type layout: text layout instance
63
+
64
+ >>> Text(u"Hello")
65
+ <Text fixed/flow widget 'Hello'>
66
+ >>> t = Text(('bold', u"stuff"), 'right', 'any')
67
+ >>> t
68
+ <Text fixed/flow widget 'stuff' align='right' wrap='any'>
69
+ >>> print(t.text)
70
+ stuff
71
+ >>> t.attrib
72
+ [('bold', 5)]
73
+ """
74
+ super().__init__()
75
+ self._cache_maxcol = None
76
+ self.set_text(markup)
77
+ self.set_layout(align, wrap, layout)
78
+
79
+ def _repr_words(self) -> list[str]:
80
+ """
81
+ Show the text in the repr in python3 format (b prefix for byte strings) and truncate if it's too long
82
+ """
83
+ first = super()._repr_words()
84
+ text = self.get_text()[0]
85
+ rest = repr(text)
86
+ if len(rest) > self._repr_content_length_max:
87
+ rest = (
88
+ rest[: self._repr_content_length_max * 2 // 3 - 3] + "..." + rest[-self._repr_content_length_max // 3 :]
89
+ )
90
+ return [*first, rest]
91
+
92
+ def _repr_attrs(self) -> dict[str, typing.Any]:
93
+ attrs = {
94
+ **super()._repr_attrs(),
95
+ "align": self._align_mode,
96
+ "wrap": self._wrap_mode,
97
+ }
98
+ return remove_defaults(attrs, Text.__init__)
99
+
100
+ def _invalidate(self) -> None:
101
+ self._cache_maxcol = None
102
+ super()._invalidate()
103
+
104
+ def set_text(self, markup: str | tuple[Hashable, str] | list[str | tuple[Hashable, str]]) -> None:
105
+ """
106
+ Set content of text widget.
107
+
108
+ :param markup: see :class:`Text` for description.
109
+ :type markup: text markup
110
+
111
+ >>> t = Text(u"foo")
112
+ >>> print(t.text)
113
+ foo
114
+ >>> t.set_text(u"bar")
115
+ >>> print(t.text)
116
+ bar
117
+ >>> t.text = u"baz" # not supported because text stores text but set_text() takes markup
118
+ Traceback (most recent call last):
119
+ AttributeError: can't set attribute
120
+ """
121
+ self._text, self._attrib = decompose_tagmarkup(markup)
122
+ self._invalidate()
123
+
124
+ def get_text(self) -> tuple[str | bytes, list[tuple[Hashable, int]]]:
125
+ """
126
+ :returns: (*text*, *display attributes*)
127
+
128
+ *text*
129
+ complete bytes/unicode content of text widget
130
+
131
+ *display attributes*
132
+ run length encoded display attributes for *text*, eg.
133
+ ``[('attr1', 10), ('attr2', 5)]``
134
+
135
+ >>> Text(u"Hello").get_text() # ... = u in Python 2
136
+ (...'Hello', [])
137
+ >>> Text(('bright', u"Headline")).get_text()
138
+ (...'Headline', [('bright', 8)])
139
+ >>> Text([('a', u"one"), u"two", ('b', u"three")]).get_text()
140
+ (...'onetwothree', [('a', 3), (None, 3), ('b', 5)])
141
+ """
142
+ return self._text, self._attrib
143
+
144
+ @property
145
+ def text(self) -> str | bytes:
146
+ """
147
+ Read-only property returning the complete bytes/unicode content
148
+ of this widget
149
+ """
150
+ return self.get_text()[0]
151
+
152
+ @property
153
+ def attrib(self) -> list[tuple[Hashable, int]]:
154
+ """
155
+ Read-only property returning the run-length encoded display
156
+ attributes of this widget
157
+ """
158
+ return self.get_text()[1]
159
+
160
+ def set_align_mode(self, mode: Literal["left", "center", "right"] | Align) -> None:
161
+ """
162
+ Set text alignment mode. Supported modes depend on text layout
163
+ object in use but defaults to a :class:`StandardTextLayout` instance
164
+
165
+ :param mode: typically ``'left'``, ``'center'`` or ``'right'``
166
+ :type mode: text alignment mode
167
+
168
+ >>> t = Text(u"word")
169
+ >>> t.set_align_mode('right')
170
+ >>> t.align
171
+ 'right'
172
+ >>> t.render((10,)).text # ... = b in Python 3
173
+ [...' word']
174
+ >>> t.align = 'center'
175
+ >>> t.render((10,)).text
176
+ [...' word ']
177
+ >>> t.align = 'somewhere'
178
+ Traceback (most recent call last):
179
+ TextError: Alignment mode 'somewhere' not supported.
180
+ """
181
+ if not self.layout.supports_align_mode(mode):
182
+ raise TextError(f"Alignment mode {mode!r} not supported.")
183
+ self._align_mode = mode
184
+ self._invalidate()
185
+
186
+ def set_wrap_mode(self, mode: Literal["space", "any", "clip", "ellipsis"] | WrapMode) -> None:
187
+ """
188
+ Set text wrapping mode. Supported modes depend on text layout
189
+ object in use but defaults to a :class:`StandardTextLayout` instance
190
+
191
+ :param mode: typically ``'space'``, ``'any'``, ``'clip'`` or ``'ellipsis'``
192
+ :type mode: text wrapping mode
193
+
194
+ >>> t = Text(u"some words")
195
+ >>> t.render((6,)).text # ... = b in Python 3
196
+ [...'some ', ...'words ']
197
+ >>> t.set_wrap_mode('clip')
198
+ >>> t.wrap
199
+ 'clip'
200
+ >>> t.render((6,)).text
201
+ [...'some w']
202
+ >>> t.wrap = 'any' # Urwid 0.9.9 or later
203
+ >>> t.render((6,)).text
204
+ [...'some w', ...'ords ']
205
+ >>> t.wrap = 'somehow'
206
+ Traceback (most recent call last):
207
+ TextError: Wrap mode 'somehow' not supported.
208
+ """
209
+ if not self.layout.supports_wrap_mode(mode):
210
+ raise TextError(f"Wrap mode {mode!r} not supported.")
211
+ self._wrap_mode = mode
212
+ self._invalidate()
213
+
214
+ def set_layout(
215
+ self,
216
+ align: Literal["left", "center", "right"] | Align,
217
+ wrap: Literal["space", "any", "clip", "ellipsis"] | WrapMode,
218
+ layout: text_layout.TextLayout | None = None,
219
+ ) -> None:
220
+ """
221
+ Set the text layout object, alignment and wrapping modes at
222
+ the same time.
223
+
224
+ :type align: text alignment mode
225
+ :param wrap: typically 'space', 'any', 'clip' or 'ellipsis'
226
+ :type wrap: text wrapping mode
227
+ :param layout: defaults to a shared :class:`StandardTextLayout` instance
228
+ :type layout: text layout instance
229
+
230
+ >>> t = Text(u"hi")
231
+ >>> t.set_layout('right', 'clip')
232
+ >>> t
233
+ <Text fixed/flow widget 'hi' align='right' wrap='clip'>
234
+ """
235
+ if layout is None:
236
+ layout = text_layout.default_layout
237
+ self._layout = layout
238
+ self.set_align_mode(align)
239
+ self.set_wrap_mode(wrap)
240
+
241
+ align = property(lambda self: self._align_mode, set_align_mode)
242
+ wrap = property(lambda self: self._wrap_mode, set_wrap_mode)
243
+
244
+ @property
245
+ def layout(self):
246
+ return self._layout
247
+
248
+ def render(self, size: tuple[int] | tuple[()], focus: bool = False) -> TextCanvas:
249
+ """
250
+ Render contents with wrapping and alignment. Return canvas.
251
+
252
+ See :meth:`Widget.render` for parameter details.
253
+
254
+ >>> Text(u"important things").render((18,)).text
255
+ [b'important things ']
256
+ >>> Text(u"important things").render((11,)).text
257
+ [b'important ', b'things ']
258
+ >>> Text("demo text").render(()).text
259
+ [b'demo text']
260
+ """
261
+ text, attr = self.get_text()
262
+ if size:
263
+ (maxcol,) = size
264
+ else:
265
+ maxcol, _ = self.pack(focus=focus)
266
+
267
+ trans = self.get_line_translation(maxcol, (text, attr))
268
+ return apply_text_layout(text, attr, trans, maxcol)
269
+
270
+ def rows(self, size: tuple[int], focus: bool = False) -> int:
271
+ """
272
+ Return the number of rows the rendered text requires.
273
+
274
+ See :meth:`Widget.rows` for parameter details.
275
+
276
+ >>> Text(u"important things").rows((18,))
277
+ 1
278
+ >>> Text(u"important things").rows((11,))
279
+ 2
280
+ """
281
+ (maxcol,) = size
282
+ return len(self.get_line_translation(maxcol))
283
+
284
+ def get_line_translation(
285
+ self,
286
+ maxcol: int,
287
+ ta: tuple[str | bytes, list[tuple[Hashable, int]]] | None = None,
288
+ ) -> list[list[tuple[int, int, int | bytes] | tuple[int, int | None]]]:
289
+ """
290
+ Return layout structure used to map self.text to a canvas.
291
+ This method is used internally, but may be useful for debugging custom layout classes.
292
+
293
+ :param maxcol: columns available for display
294
+ :type maxcol: int
295
+ :param ta: ``None`` or the (*text*, *display attributes*) tuple
296
+ returned from :meth:`.get_text`
297
+ :type ta: text and display attributes
298
+ """
299
+ if not self._cache_maxcol or self._cache_maxcol != maxcol:
300
+ self._update_cache_translation(maxcol, ta)
301
+ return self._cache_translation
302
+
303
+ def _update_cache_translation(
304
+ self,
305
+ maxcol: int,
306
+ ta: tuple[str | bytes, list[tuple[Hashable, int]]] | None,
307
+ ) -> None:
308
+ if ta:
309
+ text, _attr = ta
310
+ else:
311
+ text, _attr = self.get_text()
312
+ self._cache_maxcol = maxcol
313
+ self._cache_translation = self.layout.layout(text, maxcol, self._align_mode, self._wrap_mode)
314
+
315
+ def pack(self, size: tuple[int] | tuple[()] | None = None, focus: bool = False) -> tuple[int, int]:
316
+ """
317
+ Return the number of screen columns and rows required for
318
+ this Text widget to be displayed without wrapping or
319
+ clipping, as a single element tuple.
320
+
321
+ :param size: ``None`` or ``()`` for unlimited screen columns (like FIXED sizing)
322
+ or (*maxcol*,) to specify a maximum column size
323
+ :type size: widget size
324
+ :param focus: widget is focused on
325
+ :type focus: bool
326
+
327
+ >>> Text(u"important things").pack()
328
+ (16, 1)
329
+ >>> Text(u"important things").pack((15,))
330
+ (9, 2)
331
+ >>> Text(u"important things").pack((8,))
332
+ (8, 2)
333
+ >>> Text(u"important things").pack(())
334
+ (16, 1)
335
+ """
336
+ text, attr = self.get_text()
337
+
338
+ if size:
339
+ (maxcol,) = size
340
+ if not hasattr(self.layout, "pack"):
341
+ return size
342
+ trans = self.get_line_translation(maxcol, (text, attr))
343
+ cols = self.layout.pack(maxcol, trans)
344
+ return (cols, len(trans))
345
+
346
+ if text:
347
+ if isinstance(text, bytes):
348
+ text = text.decode(get_encoding())
349
+
350
+ return (
351
+ max(calc_width(line, 0, len(line)) for line in text.splitlines(keepends=False)),
352
+ text.count("\n") + 1,
353
+ )
354
+ return 0, 1