urwid 2.6.4__py3-none-any.whl → 2.6.6__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.
- urwid/__init__.py +46 -33
- urwid/canvas.py +4 -6
- urwid/display/_posix_raw_display.py +3 -4
- urwid/display/_raw_display_base.py +4 -5
- urwid/display/_win32_raw_display.py +1 -2
- urwid/display/curses.py +1 -1
- urwid/display/html_fragment.py +1 -1
- urwid/event_loop/__init__.py +5 -5
- urwid/event_loop/main_loop.py +26 -21
- urwid/event_loop/zmq_loop.py +2 -2
- urwid/numedit.py +5 -1
- urwid/signals.py +1 -1
- urwid/str_util.py +1 -2
- urwid/text_layout.py +8 -4
- urwid/version.py +2 -2
- urwid/vterm.py +10 -10
- urwid/widget/__init__.py +19 -1
- urwid/widget/bar_graph.py +8 -4
- urwid/widget/big_text.py +14 -4
- urwid/widget/box_adapter.py +11 -3
- urwid/widget/columns.py +104 -34
- urwid/widget/constants.py +43 -76
- urwid/widget/container.py +2 -2
- urwid/widget/divider.py +5 -1
- urwid/widget/edit.py +20 -14
- urwid/widget/filler.py +11 -4
- urwid/widget/frame.py +91 -28
- urwid/widget/grid_flow.py +66 -15
- urwid/{listbox.py → widget/listbox.py} +27 -23
- urwid/{monitored_list.py → widget/monitored_list.py} +2 -0
- urwid/widget/overlay.py +99 -14
- urwid/widget/padding.py +16 -3
- urwid/widget/pile.py +57 -13
- urwid/widget/progress_bar.py +5 -1
- urwid/widget/scrollable.py +28 -9
- urwid/widget/solid_fill.py +5 -1
- urwid/widget/text.py +11 -3
- urwid/{treetools.py → widget/treetools.py} +35 -18
- urwid/widget/widget.py +17 -9
- urwid/widget/wimp.py +8 -4
- {urwid-2.6.4.dist-info → urwid-2.6.6.dist-info}/METADATA +1 -1
- urwid-2.6.6.dist-info/RECORD +74 -0
- urwid-2.6.4.dist-info/RECORD +0 -74
- {urwid-2.6.4.dist-info → urwid-2.6.6.dist-info}/COPYING +0 -0
- {urwid-2.6.4.dist-info → urwid-2.6.6.dist-info}/WHEEL +0 -0
- {urwid-2.6.4.dist-info → urwid-2.6.6.dist-info}/top_level.txt +0 -0
urwid/widget/frame.py
CHANGED
|
@@ -4,6 +4,7 @@ import typing
|
|
|
4
4
|
import warnings
|
|
5
5
|
|
|
6
6
|
from urwid.canvas import CanvasCombine, CompositeCanvas
|
|
7
|
+
from urwid.split_repr import remove_defaults
|
|
7
8
|
from urwid.util import is_mouse_press
|
|
8
9
|
|
|
9
10
|
from .constants import Sizing, VAlign
|
|
@@ -17,6 +18,11 @@ if typing.TYPE_CHECKING:
|
|
|
17
18
|
from typing_extensions import Literal
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
BodyWidget = typing.TypeVar("BodyWidget")
|
|
22
|
+
HeaderWidget = typing.TypeVar("HeaderWidget")
|
|
23
|
+
FooterWidget = typing.TypeVar("FooterWidget")
|
|
24
|
+
|
|
25
|
+
|
|
20
26
|
class FrameError(WidgetError):
|
|
21
27
|
pass
|
|
22
28
|
|
|
@@ -34,15 +40,14 @@ def _check_widget_subclass(widget: Widget | None) -> None:
|
|
|
34
40
|
)
|
|
35
41
|
|
|
36
42
|
|
|
37
|
-
class Frame(Widget, WidgetContainerMixin):
|
|
43
|
+
class Frame(Widget, WidgetContainerMixin, typing.Generic[BodyWidget, HeaderWidget, FooterWidget]):
|
|
38
44
|
"""
|
|
39
45
|
Frame widget is a box widget with optional header and footer
|
|
40
46
|
flow widgets placed above and below the box widget.
|
|
41
47
|
|
|
42
48
|
.. note:: The main difference between a Frame and a :class:`Pile` widget
|
|
43
49
|
defined as: `Pile([('pack', header), body, ('pack', footer)])` is that
|
|
44
|
-
the Frame will not automatically change focus up and down in response to
|
|
45
|
-
keystrokes.
|
|
50
|
+
the Frame will not automatically change focus up and down in response to keystrokes.
|
|
46
51
|
"""
|
|
47
52
|
|
|
48
53
|
_selectable = True
|
|
@@ -50,9 +55,9 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
50
55
|
|
|
51
56
|
def __init__(
|
|
52
57
|
self,
|
|
53
|
-
body:
|
|
54
|
-
header:
|
|
55
|
-
footer:
|
|
58
|
+
body: BodyWidget,
|
|
59
|
+
header: HeaderWidget = None,
|
|
60
|
+
footer: FooterWidget = None,
|
|
56
61
|
focus_part: Literal["header", "footer", "body"] | Widget = "body",
|
|
57
62
|
):
|
|
58
63
|
"""
|
|
@@ -85,19 +90,35 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
85
90
|
_check_widget_subclass(body)
|
|
86
91
|
_check_widget_subclass(footer)
|
|
87
92
|
|
|
93
|
+
def _repr_attrs(self) -> dict[str, typing.Any]:
|
|
94
|
+
attrs = {
|
|
95
|
+
**super()._repr_attrs(),
|
|
96
|
+
"body": self._body,
|
|
97
|
+
"header": self._header,
|
|
98
|
+
"footer": self._footer,
|
|
99
|
+
"focus_part": self.focus_part,
|
|
100
|
+
}
|
|
101
|
+
return remove_defaults(attrs, Frame.__init__)
|
|
102
|
+
|
|
103
|
+
def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
|
|
104
|
+
yield "body", self._body
|
|
105
|
+
yield "header", self._header
|
|
106
|
+
yield "footer", self._footer
|
|
107
|
+
yield "focus_part", self.focus_part
|
|
108
|
+
|
|
88
109
|
@property
|
|
89
|
-
def header(self) ->
|
|
110
|
+
def header(self) -> HeaderWidget:
|
|
90
111
|
return self._header
|
|
91
112
|
|
|
92
113
|
@header.setter
|
|
93
|
-
def header(self, header:
|
|
114
|
+
def header(self, header: HeaderWidget) -> None:
|
|
94
115
|
_check_widget_subclass(header)
|
|
95
116
|
self._header = header
|
|
96
117
|
if header is None and self.focus_part == "header":
|
|
97
118
|
self.focus_part = "body"
|
|
98
119
|
self._invalidate()
|
|
99
120
|
|
|
100
|
-
def get_header(self) ->
|
|
121
|
+
def get_header(self) -> HeaderWidget:
|
|
101
122
|
warnings.warn(
|
|
102
123
|
f"method `{self.__class__.__name__}.get_header` is deprecated, "
|
|
103
124
|
f"standard property `{self.__class__.__name__}.header` should be used instead",
|
|
@@ -106,7 +127,7 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
106
127
|
)
|
|
107
128
|
return self.header
|
|
108
129
|
|
|
109
|
-
def set_header(self, header:
|
|
130
|
+
def set_header(self, header: HeaderWidget) -> None:
|
|
110
131
|
warnings.warn(
|
|
111
132
|
f"method `{self.__class__.__name__}.set_header` is deprecated, "
|
|
112
133
|
f"standard property `{self.__class__.__name__}.header` should be used instead",
|
|
@@ -116,16 +137,16 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
116
137
|
self.header = header
|
|
117
138
|
|
|
118
139
|
@property
|
|
119
|
-
def body(self) ->
|
|
140
|
+
def body(self) -> BodyWidget:
|
|
120
141
|
return self._body
|
|
121
142
|
|
|
122
143
|
@body.setter
|
|
123
|
-
def body(self, body:
|
|
144
|
+
def body(self, body: BodyWidget) -> None:
|
|
124
145
|
_check_widget_subclass(body)
|
|
125
146
|
self._body = body
|
|
126
147
|
self._invalidate()
|
|
127
148
|
|
|
128
|
-
def get_body(self) ->
|
|
149
|
+
def get_body(self) -> BodyWidget:
|
|
129
150
|
warnings.warn(
|
|
130
151
|
f"method `{self.__class__.__name__}.get_body` is deprecated, "
|
|
131
152
|
f"standard property {self.__class__.__name__}.body should be used instead",
|
|
@@ -134,7 +155,7 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
134
155
|
)
|
|
135
156
|
return self.body
|
|
136
157
|
|
|
137
|
-
def set_body(self, body:
|
|
158
|
+
def set_body(self, body: BodyWidget) -> None:
|
|
138
159
|
warnings.warn(
|
|
139
160
|
f"method `{self.__class__.__name__}.set_body` is deprecated, "
|
|
140
161
|
f"standard property `{self.__class__.__name__}.body` should be used instead",
|
|
@@ -144,18 +165,18 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
144
165
|
self.body = body
|
|
145
166
|
|
|
146
167
|
@property
|
|
147
|
-
def footer(self) ->
|
|
168
|
+
def footer(self) -> FooterWidget:
|
|
148
169
|
return self._footer
|
|
149
170
|
|
|
150
171
|
@footer.setter
|
|
151
|
-
def footer(self, footer:
|
|
172
|
+
def footer(self, footer: FooterWidget) -> None:
|
|
152
173
|
_check_widget_subclass(footer)
|
|
153
174
|
self._footer = footer
|
|
154
175
|
if footer is None and self.focus_part == "footer":
|
|
155
176
|
self.focus_part = "body"
|
|
156
177
|
self._invalidate()
|
|
157
178
|
|
|
158
|
-
def get_footer(self) ->
|
|
179
|
+
def get_footer(self) -> FooterWidget:
|
|
159
180
|
warnings.warn(
|
|
160
181
|
f"method `{self.__class__.__name__}.get_footer` is deprecated, "
|
|
161
182
|
f"standard property `{self.__class__.__name__}.footer` should be used instead",
|
|
@@ -164,7 +185,7 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
164
185
|
)
|
|
165
186
|
return self.footer
|
|
166
187
|
|
|
167
|
-
def set_footer(self, footer:
|
|
188
|
+
def set_footer(self, footer: FooterWidget) -> None:
|
|
168
189
|
warnings.warn(
|
|
169
190
|
f"method `{self.__class__.__name__}.set_footer` is deprecated, "
|
|
170
191
|
f"standard property `{self.__class__.__name__}.footer` should be used instead",
|
|
@@ -228,13 +249,13 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
228
249
|
self.focus_position = part
|
|
229
250
|
|
|
230
251
|
@property
|
|
231
|
-
def focus(self) ->
|
|
252
|
+
def focus(self) -> BodyWidget | HeaderWidget | FooterWidget:
|
|
232
253
|
"""
|
|
233
254
|
child :class:`Widget` in focus: the body, header or footer widget.
|
|
234
255
|
This is a read-only property."""
|
|
235
256
|
return {"header": self._header, "footer": self._footer, "body": self._body}[self.focus_part]
|
|
236
257
|
|
|
237
|
-
def _get_focus(self) ->
|
|
258
|
+
def _get_focus(self) -> BodyWidget | HeaderWidget | FooterWidget:
|
|
238
259
|
warnings.warn(
|
|
239
260
|
f"method `{self.__class__.__name__}._get_focus` is deprecated, "
|
|
240
261
|
f"please use `{self.__class__.__name__}.focus` property",
|
|
@@ -244,7 +265,12 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
244
265
|
return {"header": self._header, "footer": self._footer, "body": self._body}[self.focus_part]
|
|
245
266
|
|
|
246
267
|
@property
|
|
247
|
-
def contents(
|
|
268
|
+
def contents(
|
|
269
|
+
self,
|
|
270
|
+
) -> MutableMapping[
|
|
271
|
+
Literal["header", "footer", "body"],
|
|
272
|
+
tuple[BodyWidget | HeaderWidget | FooterWidget, None],
|
|
273
|
+
]:
|
|
248
274
|
"""
|
|
249
275
|
a dict-like object similar to::
|
|
250
276
|
|
|
@@ -267,7 +293,12 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
267
293
|
"""
|
|
268
294
|
|
|
269
295
|
# noinspection PyMethodParameters
|
|
270
|
-
class FrameContents(
|
|
296
|
+
class FrameContents(
|
|
297
|
+
typing.MutableMapping[
|
|
298
|
+
str,
|
|
299
|
+
typing.Tuple[typing.Union[BodyWidget, HeaderWidget, FooterWidget], None],
|
|
300
|
+
]
|
|
301
|
+
):
|
|
271
302
|
# pylint: disable=no-self-argument
|
|
272
303
|
|
|
273
304
|
__slots__ = ()
|
|
@@ -298,7 +329,18 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
298
329
|
keys.append("footer")
|
|
299
330
|
return keys
|
|
300
331
|
|
|
301
|
-
|
|
332
|
+
@typing.overload
|
|
333
|
+
def _contents__getitem__(self, key: Literal["body"]) -> tuple[BodyWidget, None]: ...
|
|
334
|
+
|
|
335
|
+
@typing.overload
|
|
336
|
+
def _contents__getitem__(self, key: Literal["header"]) -> tuple[HeaderWidget, None]: ...
|
|
337
|
+
|
|
338
|
+
@typing.overload
|
|
339
|
+
def _contents__getitem__(self, key: Literal["footer"]) -> tuple[FooterWidget, None]: ...
|
|
340
|
+
|
|
341
|
+
def _contents__getitem__(
|
|
342
|
+
self, key: Literal["body", "header", "footer"]
|
|
343
|
+
) -> tuple[BodyWidget | HeaderWidget | FooterWidget, None]:
|
|
302
344
|
if key == "body":
|
|
303
345
|
return (self._body, None)
|
|
304
346
|
if key == "header" and self._header:
|
|
@@ -307,7 +349,20 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
307
349
|
return (self._footer, None)
|
|
308
350
|
raise KeyError(f"Frame.contents has no key: {key!r}")
|
|
309
351
|
|
|
310
|
-
|
|
352
|
+
@typing.overload
|
|
353
|
+
def _contents__setitem__(self, key: Literal["body"], value: tuple[BodyWidget, None]) -> None: ...
|
|
354
|
+
|
|
355
|
+
@typing.overload
|
|
356
|
+
def _contents__setitem__(self, key: Literal["header"], value: tuple[HeaderWidget, None]) -> None: ...
|
|
357
|
+
|
|
358
|
+
@typing.overload
|
|
359
|
+
def _contents__setitem__(self, key: Literal["footer"], value: tuple[FooterWidget, None]) -> None: ...
|
|
360
|
+
|
|
361
|
+
def _contents__setitem__(
|
|
362
|
+
self,
|
|
363
|
+
key: Literal["body", "header", "footer"],
|
|
364
|
+
value: tuple[BodyWidget | HeaderWidget | FooterWidget, None],
|
|
365
|
+
) -> None:
|
|
311
366
|
if key not in {"body", "header", "footer"}:
|
|
312
367
|
raise KeyError(f"Frame.contents has no key: {key!r}")
|
|
313
368
|
try:
|
|
@@ -323,7 +378,7 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
323
378
|
else:
|
|
324
379
|
self.header = value_w
|
|
325
380
|
|
|
326
|
-
def _contents__delitem__(self, key: Literal["header", "footer"
|
|
381
|
+
def _contents__delitem__(self, key: Literal["header", "footer"]) -> None:
|
|
327
382
|
if key not in {"header", "footer"}:
|
|
328
383
|
raise KeyError(f"Frame.contents can't remove key: {key!r}")
|
|
329
384
|
if (key == "header" and self._header is None) or (key == "footer" and self._footer is None):
|
|
@@ -400,7 +455,11 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
400
455
|
|
|
401
456
|
return (hrows, frows), (hrows, frows)
|
|
402
457
|
|
|
403
|
-
def render(
|
|
458
|
+
def render(
|
|
459
|
+
self,
|
|
460
|
+
size: tuple[int, int], # type: ignore[override]
|
|
461
|
+
focus: bool = False,
|
|
462
|
+
) -> CompositeCanvas:
|
|
404
463
|
(maxcol, maxrow) = size
|
|
405
464
|
(htrim, ftrim), (hrows, frows) = self.frame_top_bottom((maxcol, maxrow), focus)
|
|
406
465
|
|
|
@@ -436,7 +495,11 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
436
495
|
|
|
437
496
|
return CanvasCombine(combinelist)
|
|
438
497
|
|
|
439
|
-
def keypress(
|
|
498
|
+
def keypress(
|
|
499
|
+
self,
|
|
500
|
+
size: tuple[int, int], # type: ignore[override]
|
|
501
|
+
key: str,
|
|
502
|
+
) -> str | None:
|
|
440
503
|
"""Pass keypress to widget in focus."""
|
|
441
504
|
(maxcol, maxrow) = size
|
|
442
505
|
|
|
@@ -464,7 +527,7 @@ class Frame(Widget, WidgetContainerMixin):
|
|
|
464
527
|
|
|
465
528
|
def mouse_event(
|
|
466
529
|
self,
|
|
467
|
-
size: tuple[int, int],
|
|
530
|
+
size: tuple[int, int], # type: ignore[override]
|
|
468
531
|
event: str,
|
|
469
532
|
button: int,
|
|
470
533
|
col: int,
|
urwid/widget/grid_flow.py
CHANGED
|
@@ -3,24 +3,29 @@ from __future__ import annotations
|
|
|
3
3
|
import typing
|
|
4
4
|
import warnings
|
|
5
5
|
|
|
6
|
-
from urwid.
|
|
6
|
+
from urwid.split_repr import remove_defaults
|
|
7
7
|
|
|
8
8
|
from .columns import Columns
|
|
9
9
|
from .constants import Align, Sizing, WHSettings
|
|
10
10
|
from .container import WidgetContainerListContentsMixin, WidgetContainerMixin
|
|
11
11
|
from .divider import Divider
|
|
12
|
+
from .monitored_list import MonitoredFocusList, MonitoredList
|
|
12
13
|
from .padding import Padding
|
|
13
14
|
from .pile import Pile
|
|
14
|
-
from .widget import Widget, WidgetError, WidgetWrap
|
|
15
|
+
from .widget import Widget, WidgetError, WidgetWarning, WidgetWrap
|
|
15
16
|
|
|
16
17
|
if typing.TYPE_CHECKING:
|
|
17
|
-
from collections.abc import Iterable, Sequence
|
|
18
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
18
19
|
|
|
19
20
|
from typing_extensions import Literal
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class GridFlowError(WidgetError):
|
|
23
|
-
|
|
24
|
+
"""GridFlow specific error."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class GridFlowWarning(WidgetWarning):
|
|
28
|
+
"""GridFlow specific warning."""
|
|
24
29
|
|
|
25
30
|
|
|
26
31
|
class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
@@ -75,6 +80,37 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
|
|
|
75
80
|
self._cache_maxcol = self._get_maxcol(())
|
|
76
81
|
super().__init__(self.generate_display_widget((self._cache_maxcol,)))
|
|
77
82
|
|
|
83
|
+
def _repr_words(self) -> list[str]:
|
|
84
|
+
if len(self.contents) > 1:
|
|
85
|
+
contents_string = f"({len(self.contents)} items)"
|
|
86
|
+
elif self.contents:
|
|
87
|
+
contents_string = "(1 item)"
|
|
88
|
+
else:
|
|
89
|
+
contents_string = "()"
|
|
90
|
+
return [*super()._repr_words(), contents_string]
|
|
91
|
+
|
|
92
|
+
def _repr_attrs(self) -> dict[str, typing.Any]:
|
|
93
|
+
attrs = {
|
|
94
|
+
**super()._repr_attrs(),
|
|
95
|
+
"cell_width": self.cell_width,
|
|
96
|
+
"h_sep": self.h_sep,
|
|
97
|
+
"v_sep": self.v_sep,
|
|
98
|
+
"align": self.align,
|
|
99
|
+
"focus": self.focus_position if len(self._contents) > 1 else None,
|
|
100
|
+
}
|
|
101
|
+
return remove_defaults(attrs, GridFlow.__init__)
|
|
102
|
+
|
|
103
|
+
def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
|
|
104
|
+
yield "cells", [widget for widget, _ in self.contents]
|
|
105
|
+
yield "cell_width", self.cell_width
|
|
106
|
+
yield "h_sep", self.h_sep
|
|
107
|
+
yield "v_sep", self.v_sep
|
|
108
|
+
yield "align", self.align
|
|
109
|
+
yield "focus", self.focus_position
|
|
110
|
+
|
|
111
|
+
def __len__(self) -> int:
|
|
112
|
+
return len(self._contents)
|
|
113
|
+
|
|
78
114
|
def _invalidate(self) -> None:
|
|
79
115
|
self._cache_maxcol = None
|
|
80
116
|
super()._invalidate()
|
|
@@ -123,7 +159,7 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
|
|
|
123
159
|
)
|
|
124
160
|
focus_position = self.focus_position
|
|
125
161
|
self.contents = [(new, (WHSettings.GIVEN, self._cell_width)) for new in widgets]
|
|
126
|
-
if focus_position < len(widgets):
|
|
162
|
+
if focus_position < len(widgets): # pylint: disable=consider-using-max-builtin # pylint bug
|
|
127
163
|
self.focus_position = focus_position
|
|
128
164
|
|
|
129
165
|
def _get_cells(self):
|
|
@@ -384,6 +420,12 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
|
|
|
384
420
|
def _get_maxcol(self, size: tuple[int] | tuple[()]) -> int:
|
|
385
421
|
if size:
|
|
386
422
|
(maxcol,) = size
|
|
423
|
+
if maxcol < self.cell_width:
|
|
424
|
+
warnings.warn(
|
|
425
|
+
f"Size is smaller than cell width ({maxcol!r} < {self.cell_width!r})",
|
|
426
|
+
GridFlowWarning,
|
|
427
|
+
stacklevel=3,
|
|
428
|
+
)
|
|
387
429
|
else:
|
|
388
430
|
maxcol = len(self) * self.cell_width + (len(self) - 1) * self.h_sep
|
|
389
431
|
return maxcol
|
|
@@ -435,18 +477,15 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
|
|
|
435
477
|
pad.first_position = i
|
|
436
478
|
p.contents.append((pad, p.options()))
|
|
437
479
|
|
|
438
|
-
|
|
480
|
+
# Use width == maxcol in case of maxcol < width amount
|
|
481
|
+
# Columns will use empty widget in case of GIVEN width > maxcol
|
|
482
|
+
c.contents.append((w, c.options(WHSettings.GIVEN, width_amount if width_amount <= maxcol else maxcol)))
|
|
439
483
|
if (i == self.focus_position) or (not column_focused and w.selectable()):
|
|
440
484
|
c.focus_position = len(c.contents) - 1
|
|
441
485
|
column_focused = True
|
|
442
486
|
if i == self.focus_position:
|
|
443
487
|
p.focus_position = len(p.contents) - 1
|
|
444
488
|
used_space = sum(x[1][1] for x in c.contents) + self.h_sep * len(c.contents)
|
|
445
|
-
if width_amount > maxcol:
|
|
446
|
-
# special case: display is too narrow for the given
|
|
447
|
-
# width so we remove the Columns for better behaviour
|
|
448
|
-
# FIXME: determine why this is necessary
|
|
449
|
-
pad.original_widget = w
|
|
450
489
|
pad.width = used_space - self.h_sep
|
|
451
490
|
|
|
452
491
|
if self.v_sep:
|
|
@@ -482,7 +521,11 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
|
|
|
482
521
|
# pad.first_position was set by generate_display_widget() above
|
|
483
522
|
self.focus_position = pile_focus.first_position + col_focus_position
|
|
484
523
|
|
|
485
|
-
def keypress(
|
|
524
|
+
def keypress(
|
|
525
|
+
self,
|
|
526
|
+
size: tuple[int], # type: ignore[override]
|
|
527
|
+
key: str,
|
|
528
|
+
) -> str | None:
|
|
486
529
|
"""
|
|
487
530
|
Pass keypress to display widget for handling.
|
|
488
531
|
Captures focus changes.
|
|
@@ -493,7 +536,11 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
|
|
|
493
536
|
self._set_focus_from_display_widget()
|
|
494
537
|
return key
|
|
495
538
|
|
|
496
|
-
def pack(
|
|
539
|
+
def pack(
|
|
540
|
+
self,
|
|
541
|
+
size: tuple[int] | tuple[()] = (), # type: ignore[override]
|
|
542
|
+
focus: bool = False,
|
|
543
|
+
) -> tuple[int, int]:
|
|
497
544
|
if size:
|
|
498
545
|
return super().pack(size, focus)
|
|
499
546
|
cols = len(self) * self.cell_width + (len(self) - 1) * self.h_sep
|
|
@@ -503,7 +550,11 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
|
|
|
503
550
|
self.get_display_widget(size)
|
|
504
551
|
return super().rows(size, focus=focus)
|
|
505
552
|
|
|
506
|
-
def render(
|
|
553
|
+
def render(
|
|
554
|
+
self,
|
|
555
|
+
size: tuple[int] | tuple[()], # type: ignore[override]
|
|
556
|
+
focus: bool = False,
|
|
557
|
+
):
|
|
507
558
|
self.get_display_widget(size)
|
|
508
559
|
return super().render(size, focus)
|
|
509
560
|
|
|
@@ -521,7 +572,7 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
|
|
|
521
572
|
|
|
522
573
|
def mouse_event(
|
|
523
574
|
self,
|
|
524
|
-
size: tuple[int] | tuple[()],
|
|
575
|
+
size: tuple[int] | tuple[()], # type: ignore[override]
|
|
525
576
|
event: str,
|
|
526
577
|
button: int,
|
|
527
578
|
col: int,
|
|
@@ -29,20 +29,12 @@ from typing_extensions import Protocol, runtime_checkable
|
|
|
29
29
|
|
|
30
30
|
from urwid import signals
|
|
31
31
|
from urwid.canvas import CanvasCombine, SolidCanvas
|
|
32
|
-
|
|
33
|
-
from
|
|
34
|
-
from
|
|
35
|
-
from
|
|
36
|
-
from
|
|
37
|
-
|
|
38
|
-
VAlign,
|
|
39
|
-
WHSettings,
|
|
40
|
-
Widget,
|
|
41
|
-
WidgetContainerMixin,
|
|
42
|
-
calculate_top_bottom_filler,
|
|
43
|
-
nocache_widget_render_instance,
|
|
44
|
-
normalize_valign,
|
|
45
|
-
)
|
|
32
|
+
|
|
33
|
+
from .constants import Sizing, VAlign, WHSettings, normalize_valign
|
|
34
|
+
from .container import WidgetContainerMixin
|
|
35
|
+
from .filler import calculate_top_bottom_filler
|
|
36
|
+
from .monitored_list import MonitoredFocusList, MonitoredList
|
|
37
|
+
from .widget import Widget, nocache_widget_render_instance
|
|
46
38
|
|
|
47
39
|
if typing.TYPE_CHECKING:
|
|
48
40
|
from collections.abc import Callable, Hashable
|
|
@@ -51,6 +43,8 @@ if typing.TYPE_CHECKING:
|
|
|
51
43
|
|
|
52
44
|
from urwid.canvas import Canvas, CompositeCanvas
|
|
53
45
|
|
|
46
|
+
__all__ = ("ListWalkerError", "ListWalker", "SimpleListWalker", "SimpleFocusListWalker", "ListBoxError", "ListBox")
|
|
47
|
+
|
|
54
48
|
_T = typing.TypeVar("_T")
|
|
55
49
|
_K = typing.TypeVar("_K")
|
|
56
50
|
|
|
@@ -362,7 +356,7 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
362
356
|
@body.setter
|
|
363
357
|
def body(self, body: Iterable[Widget] | ListWalker) -> None:
|
|
364
358
|
with suppress(AttributeError):
|
|
365
|
-
disconnect_signal(self._body, "modified", self._invalidate)
|
|
359
|
+
signals.disconnect_signal(self._body, "modified", self._invalidate)
|
|
366
360
|
# _body may be not yet assigned
|
|
367
361
|
|
|
368
362
|
if getattr(body, "get_focus", None):
|
|
@@ -370,7 +364,7 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
370
364
|
else:
|
|
371
365
|
self._body = SimpleListWalker(body)
|
|
372
366
|
try:
|
|
373
|
-
connect_signal(self._body, "modified", self._invalidate)
|
|
367
|
+
signals.connect_signal(self._body, "modified", self._invalidate)
|
|
374
368
|
except NameError:
|
|
375
369
|
# our list walker has no modified signal so we must not
|
|
376
370
|
# cache our canvases because we don't know when our
|
|
@@ -582,7 +576,11 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
582
576
|
|
|
583
577
|
return self._rows_max_cached
|
|
584
578
|
|
|
585
|
-
def render(
|
|
579
|
+
def render(
|
|
580
|
+
self,
|
|
581
|
+
size: tuple[int, int], # type: ignore[override]
|
|
582
|
+
focus: bool = False,
|
|
583
|
+
) -> CompositeCanvas | SolidCanvas:
|
|
586
584
|
"""
|
|
587
585
|
Render ListBox and return canvas.
|
|
588
586
|
|
|
@@ -1180,7 +1178,11 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
1180
1178
|
self.shift_focus((maxcol, maxrow), maxrow - cy - 1)
|
|
1181
1179
|
return
|
|
1182
1180
|
|
|
1183
|
-
def keypress(
|
|
1181
|
+
def keypress(
|
|
1182
|
+
self,
|
|
1183
|
+
size: tuple[int, int], # type: ignore[override]
|
|
1184
|
+
key: str,
|
|
1185
|
+
) -> str | None:
|
|
1184
1186
|
"""Move selection through the list elements scrolling when
|
|
1185
1187
|
necessary. Keystrokes are first passed to widget in focus
|
|
1186
1188
|
in case that widget can handle them.
|
|
@@ -1191,6 +1193,8 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
1191
1193
|
'page up' move cursor up one listbox length (or widget)
|
|
1192
1194
|
'page down' move cursor down one listbox length (or widget)
|
|
1193
1195
|
"""
|
|
1196
|
+
from urwid.command_map import Command
|
|
1197
|
+
|
|
1194
1198
|
(maxcol, maxrow) = size
|
|
1195
1199
|
|
|
1196
1200
|
if self.set_focus_pending or self.set_focus_valign_pending:
|
|
@@ -1773,7 +1777,7 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
1773
1777
|
|
|
1774
1778
|
def mouse_event(
|
|
1775
1779
|
self,
|
|
1776
|
-
size: tuple[int, int],
|
|
1780
|
+
size: tuple[int, int], # type: ignore[override]
|
|
1777
1781
|
event,
|
|
1778
1782
|
button: int,
|
|
1779
1783
|
col: int,
|
|
@@ -1784,6 +1788,8 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
1784
1788
|
Pass the event to the contained widgets.
|
|
1785
1789
|
May change focus on button 1 press.
|
|
1786
1790
|
"""
|
|
1791
|
+
from urwid.util import is_mouse_press
|
|
1792
|
+
|
|
1787
1793
|
(maxcol, maxrow) = size
|
|
1788
1794
|
middle, top, bottom = self.calculate_visible((maxcol, maxrow), focus=True)
|
|
1789
1795
|
if middle is None:
|
|
@@ -1875,8 +1881,7 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
1875
1881
|
"""
|
|
1876
1882
|
positions_fn = getattr(self._body, "positions", None)
|
|
1877
1883
|
if positions_fn:
|
|
1878
|
-
|
|
1879
|
-
yield pos
|
|
1884
|
+
yield from positions_fn()
|
|
1880
1885
|
return
|
|
1881
1886
|
|
|
1882
1887
|
focus_widget, focus_pos = self._body.get_focus()
|
|
@@ -1907,8 +1912,7 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
1907
1912
|
"""
|
|
1908
1913
|
positions_fn = getattr(self._body, "positions", None)
|
|
1909
1914
|
if positions_fn:
|
|
1910
|
-
|
|
1911
|
-
yield pos
|
|
1915
|
+
yield from positions_fn(reverse=True)
|
|
1912
1916
|
return
|
|
1913
1917
|
|
|
1914
1918
|
focus_widget, focus_pos = self._body.get_focus()
|