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
@@ -0,0 +1,251 @@
1
+ # Urwid html fragment output wrapper for "screen shots"
2
+ # Copyright (C) 2004-2007 Ian Ward
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ #
18
+ # Urwid web site: https://urwid.org/
19
+
20
+
21
+ """
22
+ HTML PRE-based UI implementation
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import html
28
+ import typing
29
+
30
+ from urwid import str_util
31
+ from urwid.event_loop import ExitMainLoop
32
+
33
+ from .common import AttrSpec, BaseScreen
34
+
35
+ if typing.TYPE_CHECKING:
36
+ from typing_extensions import Literal
37
+
38
+ from urwid import Canvas
39
+
40
+ # replace control characters with ?'s
41
+ _trans_table = "?" * 32 + "".join([chr(x) for x in range(32, 256)])
42
+
43
+ _default_foreground = "black"
44
+ _default_background = "light gray"
45
+
46
+
47
+ class HtmlGeneratorSimulationError(Exception):
48
+ pass
49
+
50
+
51
+ class HtmlGenerator(BaseScreen):
52
+ # class variables
53
+ fragments: typing.ClassVar[list[str]] = []
54
+ sizes: typing.ClassVar[list[tuple[int, int]]] = []
55
+ keys: typing.ClassVar[list[list[str] | tuple[list[str], list[int]]]] = []
56
+ started = True
57
+
58
+ def __init__(self):
59
+ super().__init__()
60
+ self.colors = 16
61
+ self.bright_is_bold = False # ignored
62
+ self.has_underline = True # ignored
63
+ self.register_palette_entry(None, _default_foreground, _default_background)
64
+
65
+ def set_terminal_properties(
66
+ self,
67
+ colors: int | None = None,
68
+ bright_is_bold: bool | None = None,
69
+ has_underline: bool | None = None,
70
+ ) -> None:
71
+ if colors is None:
72
+ colors = self.colors
73
+ if bright_is_bold is None:
74
+ bright_is_bold = self.bright_is_bold
75
+ if has_underline is None:
76
+ has_underline = self.has_underline
77
+
78
+ self.colors = colors
79
+ self.bright_is_bold = bright_is_bold
80
+ self.has_underline = has_underline
81
+
82
+ def set_input_timeouts(self, *args):
83
+ pass
84
+
85
+ def reset_default_terminal_palette(self, *args):
86
+ pass
87
+
88
+ def draw_screen(self, size: tuple[int, int], canvas: Canvas):
89
+ """Create an html fragment from the render object.
90
+ Append it to HtmlGenerator.fragments list.
91
+ """
92
+ # collect output in l
93
+ lines = []
94
+
95
+ _cols, rows = size
96
+
97
+ if canvas.rows() != rows:
98
+ raise ValueError(rows)
99
+
100
+ if canvas.cursor is not None:
101
+ cx, cy = canvas.cursor
102
+ else:
103
+ cx = cy = None
104
+
105
+ y = -1
106
+ for row in canvas.content():
107
+ y += 1
108
+ col = 0
109
+
110
+ for a, _cs, run in row:
111
+ t_run = run.decode().translate(_trans_table)
112
+ if isinstance(a, AttrSpec):
113
+ aspec = a
114
+ else:
115
+ aspec = self._palette[a][{1: 1, 16: 0, 88: 2, 256: 3}[self.colors]]
116
+
117
+ if y == cy and col <= cx:
118
+ run_width = str_util.calc_width(t_run, 0, len(t_run))
119
+ if col + run_width > cx:
120
+ lines.append(html_span(t_run, aspec, cx - col))
121
+ else:
122
+ lines.append(html_span(t_run, aspec))
123
+ col += run_width
124
+ else:
125
+ lines.append(html_span(t_run, aspec))
126
+
127
+ lines.append("\n")
128
+
129
+ # add the fragment to the list
130
+ self.fragments.append(f"<pre>{''.join(lines)}</pre>")
131
+
132
+ def get_cols_rows(self):
133
+ """Return the next screen size in HtmlGenerator.sizes."""
134
+ if not self.sizes:
135
+ raise HtmlGeneratorSimulationError("Ran out of screen sizes to return!")
136
+ return self.sizes.pop(0)
137
+
138
+ @typing.overload
139
+ def get_input(self, raw_keys: Literal[False]) -> list[str]: ...
140
+
141
+ @typing.overload
142
+ def get_input(self, raw_keys: Literal[True]) -> tuple[list[str], list[int]]: ...
143
+
144
+ def get_input(self, raw_keys: bool = False) -> list[str] | tuple[list[str], list[int]]:
145
+ """Return the next list of keypresses in HtmlGenerator.keys."""
146
+ if not self.keys:
147
+ raise ExitMainLoop()
148
+ if raw_keys:
149
+ return (self.keys.pop(0), [])
150
+ return self.keys.pop(0)
151
+
152
+
153
+ _default_aspec = AttrSpec(_default_foreground, _default_background)
154
+ (_d_fg_r, _d_fg_g, _d_fg_b, _d_bg_r, _d_bg_g, _d_bg_b) = _default_aspec.get_rgb_values()
155
+
156
+
157
+ def html_span(s, aspec, cursor: int = -1):
158
+ fg_r, fg_g, fg_b, bg_r, bg_g, bg_b = aspec.get_rgb_values()
159
+ # use real colours instead of default fg/bg
160
+ if fg_r is None:
161
+ fg_r, fg_g, fg_b = _d_fg_r, _d_fg_g, _d_fg_b
162
+ if bg_r is None:
163
+ bg_r, bg_g, bg_b = _d_bg_r, _d_bg_g, _d_bg_b
164
+ html_fg = f"#{fg_r:02x}{fg_g:02x}{fg_b:02x}"
165
+ html_bg = f"#{bg_r:02x}{bg_g:02x}{bg_b:02x}"
166
+ if aspec.standout:
167
+ html_fg, html_bg = html_bg, html_fg
168
+ extra = ";text-decoration:underline" * aspec.underline + ";font-weight:bold" * aspec.bold
169
+
170
+ def _span(fg: str, bg: str, string: str) -> str:
171
+ if not s:
172
+ return ""
173
+ return f'<span style="color:{fg};background:{bg}{extra}">{html_escape(string)}</span>'
174
+
175
+ if cursor >= 0:
176
+ c_off, _ign = str_util.calc_text_pos(s, 0, len(s), cursor)
177
+ c2_off = str_util.move_next_char(s, c_off, len(s))
178
+ return (
179
+ _span(html_fg, html_bg, s[:c_off])
180
+ + _span(html_bg, html_fg, s[c_off:c2_off])
181
+ + _span(html_fg, html_bg, s[c2_off:])
182
+ )
183
+
184
+ return _span(html_fg, html_bg, s)
185
+
186
+
187
+ def html_escape(text: str) -> str:
188
+ """Escape text so that it will be displayed safely within HTML"""
189
+ return html.escape(text)
190
+
191
+
192
+ def screenshot_init(sizes: list[tuple[int, int]], keys: list[list[str]]) -> None:
193
+ """
194
+ Replace curses_display.Screen and raw_display.Screen class with
195
+ HtmlGenerator.
196
+
197
+ Call this function before executing an application that uses
198
+ curses_display.Screen to have that code use HtmlGenerator instead.
199
+
200
+ sizes -- list of ( columns, rows ) tuples to be returned by each call
201
+ to HtmlGenerator.get_cols_rows()
202
+ keys -- list of lists of keys to be returned by each call to
203
+ HtmlGenerator.get_input()
204
+
205
+ Lists of keys may include "window resize" to force the application to
206
+ call get_cols_rows and read a new screen size.
207
+
208
+ For example, the following call will prepare an application to:
209
+ 1. start in 80x25 with its first call to get_cols_rows()
210
+ 2. take a screenshot when it calls draw_screen(..)
211
+ 3. simulate 5 "down" keys from get_input()
212
+ 4. take a screenshot when it calls draw_screen(..)
213
+ 5. simulate keys "a", "b", "c" and a "window resize"
214
+ 6. resize to 20x10 on its second call to get_cols_rows()
215
+ 7. take a screenshot when it calls draw_screen(..)
216
+ 8. simulate a "Q" keypress to quit the application
217
+
218
+ screenshot_init( [ (80,25), (20,10) ],
219
+ [ ["down"]*5, ["a","b","c","window resize"], ["Q"] ] )
220
+ """
221
+ for row, col in sizes:
222
+ if not isinstance(row, int):
223
+ raise TypeError(f"sizes must be list[tuple[int, int]], with values >0 : {row!r}")
224
+ if row <= 0:
225
+ raise ValueError(f"sizes must be list[tuple[int, int]], with values >0 : {row!r}")
226
+ if not isinstance(col, int):
227
+ raise TypeError(f"sizes must be list[tuple[int, int]], with values >0 : {col!r}")
228
+ if col <= 0:
229
+ raise ValueError(f"sizes must be list[tuple[int, int]], with values >0 : {col!r}")
230
+
231
+ for line in keys:
232
+ if not isinstance(line, list):
233
+ raise TypeError(f"keys must be list[list[str]]: {line!r}")
234
+ for k in line:
235
+ if not isinstance(k, str):
236
+ raise TypeError(f"keys must be list[list[str]]: {k!r}")
237
+
238
+ from . import curses, raw
239
+
240
+ curses.Screen = HtmlGenerator
241
+ raw.Screen = HtmlGenerator
242
+
243
+ HtmlGenerator.sizes = sizes
244
+ HtmlGenerator.keys = keys
245
+
246
+
247
+ def screenshot_collect():
248
+ """Return screenshots as a list of HTML fragments."""
249
+ fragments = HtmlGenerator.fragments
250
+ HtmlGenerator.fragments = []
251
+ return fragments