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/overlay.py
CHANGED
|
@@ -4,6 +4,7 @@ import typing
|
|
|
4
4
|
import warnings
|
|
5
5
|
|
|
6
6
|
from urwid.canvas import CanvasOverlay, CompositeCanvas
|
|
7
|
+
from urwid.split_repr import remove_defaults
|
|
7
8
|
|
|
8
9
|
from .constants import (
|
|
9
10
|
RELATIVE_100,
|
|
@@ -32,6 +33,10 @@ if typing.TYPE_CHECKING:
|
|
|
32
33
|
from typing_extensions import Literal
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
TopWidget = typing.TypeVar("TopWidget")
|
|
37
|
+
BottomWidget = typing.TypeVar("BottomWidget")
|
|
38
|
+
|
|
39
|
+
|
|
35
40
|
class OverlayError(WidgetError):
|
|
36
41
|
"""Overlay specific errors."""
|
|
37
42
|
|
|
@@ -67,7 +72,7 @@ class OverlayOptions(typing.NamedTuple):
|
|
|
67
72
|
bottom: int
|
|
68
73
|
|
|
69
74
|
|
|
70
|
-
class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
75
|
+
class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin, typing.Generic[TopWidget, BottomWidget]):
|
|
71
76
|
"""
|
|
72
77
|
Overlay contains two box widgets and renders one on top of the other
|
|
73
78
|
"""
|
|
@@ -93,8 +98,8 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
93
98
|
|
|
94
99
|
def __init__(
|
|
95
100
|
self,
|
|
96
|
-
top_w:
|
|
97
|
-
bottom_w:
|
|
101
|
+
top_w: TopWidget,
|
|
102
|
+
bottom_w: BottomWidget,
|
|
98
103
|
align: (
|
|
99
104
|
Literal["left", "center", "right"]
|
|
100
105
|
| Align
|
|
@@ -227,7 +232,11 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
227
232
|
|
|
228
233
|
return frozenset(sizing)
|
|
229
234
|
|
|
230
|
-
def pack(
|
|
235
|
+
def pack(
|
|
236
|
+
self,
|
|
237
|
+
size: tuple[()] | tuple[int] | tuple[int, int] = (),
|
|
238
|
+
focus: bool = False,
|
|
239
|
+
) -> tuple[int, int]:
|
|
231
240
|
if size:
|
|
232
241
|
return super().pack(size, focus)
|
|
233
242
|
|
|
@@ -322,8 +331,67 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
322
331
|
f"min_height={self.min_height!r}"
|
|
323
332
|
)
|
|
324
333
|
|
|
325
|
-
def
|
|
326
|
-
|
|
334
|
+
def _repr_attrs(self) -> dict[str, typing.Any]:
|
|
335
|
+
attrs = {
|
|
336
|
+
**super()._repr_attrs(),
|
|
337
|
+
"top_w": self.top_w,
|
|
338
|
+
"bottom_w": self.bottom_w,
|
|
339
|
+
"align": self.align,
|
|
340
|
+
"width": self.width,
|
|
341
|
+
"valign": self.valign,
|
|
342
|
+
"height": self.height,
|
|
343
|
+
"min_width": self.min_width,
|
|
344
|
+
"min_height": self.min_height,
|
|
345
|
+
"left": self.left,
|
|
346
|
+
"right": self.right,
|
|
347
|
+
"top": self.top,
|
|
348
|
+
"bottom": self.bottom,
|
|
349
|
+
}
|
|
350
|
+
return remove_defaults(attrs, Overlay.__init__)
|
|
351
|
+
|
|
352
|
+
def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
|
|
353
|
+
yield "top", self.top_w
|
|
354
|
+
yield "bottom", self.bottom_w
|
|
355
|
+
yield "align", self.align
|
|
356
|
+
yield "width", self.width
|
|
357
|
+
yield "valign", self.valign
|
|
358
|
+
yield "height", self.height
|
|
359
|
+
yield "min_width", self.min_width
|
|
360
|
+
yield "min_height", self.min_height
|
|
361
|
+
yield "left", self.left
|
|
362
|
+
yield "right", self.right
|
|
363
|
+
yield "top", self.top
|
|
364
|
+
yield "bottom", self.bottom
|
|
365
|
+
|
|
366
|
+
@property
|
|
367
|
+
def align(self) -> Align | tuple[Literal[WHSettings.RELATIVE], int]:
|
|
368
|
+
return simplify_align(self.align_type, self.align_amount)
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def width(
|
|
372
|
+
self,
|
|
373
|
+
) -> (
|
|
374
|
+
Literal[WHSettings.CLIP, WHSettings.PACK]
|
|
375
|
+
| int
|
|
376
|
+
| tuple[Literal[WHSettings.RELATIVE], int]
|
|
377
|
+
| tuple[Literal[WHSettings.WEIGHT], int | float]
|
|
378
|
+
):
|
|
379
|
+
return simplify_width(self.width_type, self.width_amount)
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def valign(self) -> VAlign | tuple[Literal[WHSettings.RELATIVE], int]:
|
|
383
|
+
return simplify_valign(self.valign_type, self.valign_amount)
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def height(
|
|
387
|
+
self,
|
|
388
|
+
) -> (
|
|
389
|
+
int
|
|
390
|
+
| Literal[WHSettings.FLOW, WHSettings.PACK]
|
|
391
|
+
| tuple[Literal[WHSettings.RELATIVE], int]
|
|
392
|
+
| tuple[Literal[WHSettings.WEIGHT], int | float]
|
|
393
|
+
):
|
|
394
|
+
return simplify_height(self.height_type, self.height_amount)
|
|
327
395
|
|
|
328
396
|
@staticmethod
|
|
329
397
|
def options(
|
|
@@ -478,13 +546,20 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
478
546
|
"""Return selectable from top_w."""
|
|
479
547
|
return self.top_w.selectable()
|
|
480
548
|
|
|
481
|
-
def keypress(
|
|
549
|
+
def keypress(
|
|
550
|
+
self,
|
|
551
|
+
size: tuple[()] | tuple[int] | tuple[int, int],
|
|
552
|
+
key: str,
|
|
553
|
+
) -> str | None:
|
|
482
554
|
"""Pass keypress to top_w."""
|
|
483
555
|
real_size = self.pack(size, True)
|
|
484
|
-
return self.top_w.keypress(
|
|
556
|
+
return self.top_w.keypress(
|
|
557
|
+
self.top_w_size(real_size, *self.calculate_padding_filler(real_size, True)),
|
|
558
|
+
key,
|
|
559
|
+
)
|
|
485
560
|
|
|
486
561
|
@property
|
|
487
|
-
def focus(self) ->
|
|
562
|
+
def focus(self) -> TopWidget:
|
|
488
563
|
"""
|
|
489
564
|
Read-only property returning the child widget in focus for
|
|
490
565
|
container widgets. This default implementation
|
|
@@ -492,7 +567,7 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
492
567
|
"""
|
|
493
568
|
return self.top_w
|
|
494
569
|
|
|
495
|
-
def _get_focus(self) ->
|
|
570
|
+
def _get_focus(self) -> TopWidget:
|
|
496
571
|
warnings.warn(
|
|
497
572
|
f"method `{self.__class__.__name__}._get_focus` is deprecated, "
|
|
498
573
|
f"please use `{self.__class__.__name__}.focus` property",
|
|
@@ -567,7 +642,7 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
567
642
|
"""
|
|
568
643
|
|
|
569
644
|
# noinspection PyMethodParameters
|
|
570
|
-
class OverlayContents(typing.Sequence[typing.Tuple[
|
|
645
|
+
class OverlayContents(typing.Sequence[typing.Tuple[typing.Union[TopWidget, BottomWidget], OverlayOptions]]):
|
|
571
646
|
# pylint: disable=no-self-argument
|
|
572
647
|
def __len__(inner_self) -> int:
|
|
573
648
|
return 2
|
|
@@ -595,7 +670,10 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
595
670
|
self.contents[0] = new_contents[0]
|
|
596
671
|
self.contents[1] = new_contents[1]
|
|
597
672
|
|
|
598
|
-
def _contents__getitem__(
|
|
673
|
+
def _contents__getitem__(
|
|
674
|
+
self,
|
|
675
|
+
index: Literal[0, 1],
|
|
676
|
+
) -> tuple[TopWidget | BottomWidget, OverlayOptions]:
|
|
599
677
|
if index == 0:
|
|
600
678
|
return (self.bottom_w, self._DEFAULT_BOTTOM_OPTIONS)
|
|
601
679
|
|
|
@@ -621,7 +699,11 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
621
699
|
)
|
|
622
700
|
raise IndexError(f"Overlay.contents has no position {index!r}")
|
|
623
701
|
|
|
624
|
-
def _contents__setitem__(
|
|
702
|
+
def _contents__setitem__(
|
|
703
|
+
self,
|
|
704
|
+
index: Literal[0, 1],
|
|
705
|
+
value: tuple[TopWidget | BottomWidget, OverlayOptions],
|
|
706
|
+
) -> None:
|
|
625
707
|
try:
|
|
626
708
|
value_w, value_options = value
|
|
627
709
|
except (ValueError, TypeError) as exc:
|
|
@@ -675,7 +757,10 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
675
757
|
raise IndexError(f"Overlay.contents has no position {index!r}")
|
|
676
758
|
self._invalidate()
|
|
677
759
|
|
|
678
|
-
def get_cursor_coords(
|
|
760
|
+
def get_cursor_coords(
|
|
761
|
+
self,
|
|
762
|
+
size: tuple[()] | tuple[int] | tuple[int, int],
|
|
763
|
+
) -> tuple[int, int] | None:
|
|
679
764
|
"""Return cursor coords from top_w, if any."""
|
|
680
765
|
if not hasattr(self.top_w, "get_cursor_coords"):
|
|
681
766
|
return None
|
urwid/widget/padding.py
CHANGED
|
@@ -20,8 +20,9 @@ from .constants import (
|
|
|
20
20
|
from .widget_decoration import WidgetDecoration, WidgetError, WidgetWarning
|
|
21
21
|
|
|
22
22
|
if typing.TYPE_CHECKING:
|
|
23
|
-
from
|
|
23
|
+
from collections.abc import Iterator
|
|
24
24
|
|
|
25
|
+
from typing_extensions import Literal
|
|
25
26
|
|
|
26
27
|
WrappedWidget = typing.TypeVar("WrappedWidget")
|
|
27
28
|
|
|
@@ -34,7 +35,7 @@ class PaddingWarning(WidgetWarning):
|
|
|
34
35
|
"""Padding related warnings."""
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
class Padding(WidgetDecoration[WrappedWidget]):
|
|
38
|
+
class Padding(WidgetDecoration[WrappedWidget], typing.Generic[WrappedWidget]):
|
|
38
39
|
def __init__(
|
|
39
40
|
self,
|
|
40
41
|
w: WrappedWidget,
|
|
@@ -185,6 +186,14 @@ class Padding(WidgetDecoration[WrappedWidget]):
|
|
|
185
186
|
}
|
|
186
187
|
return remove_defaults(attrs, Padding.__init__)
|
|
187
188
|
|
|
189
|
+
def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
|
|
190
|
+
yield "w", self.original_widget
|
|
191
|
+
yield "align", self.align
|
|
192
|
+
yield "width", self.width
|
|
193
|
+
yield "min_width", self.min_width
|
|
194
|
+
yield "left", self.left
|
|
195
|
+
yield "right", self.right
|
|
196
|
+
|
|
188
197
|
@property
|
|
189
198
|
def align(
|
|
190
199
|
self,
|
|
@@ -268,7 +277,11 @@ class Padding(WidgetDecoration[WrappedWidget]):
|
|
|
268
277
|
)
|
|
269
278
|
self.width = width
|
|
270
279
|
|
|
271
|
-
def pack(
|
|
280
|
+
def pack(
|
|
281
|
+
self,
|
|
282
|
+
size: tuple[()] | tuple[int] | tuple[int, int] = (),
|
|
283
|
+
focus: bool = False,
|
|
284
|
+
) -> tuple[int, int]:
|
|
272
285
|
if size:
|
|
273
286
|
return super().pack(size, focus)
|
|
274
287
|
if self._width_type == WHSettings.CLIP:
|
urwid/widget/pile.py
CHANGED
|
@@ -6,15 +6,16 @@ from itertools import chain, repeat
|
|
|
6
6
|
|
|
7
7
|
from urwid.canvas import CanvasCombine, CompositeCanvas, SolidCanvas
|
|
8
8
|
from urwid.command_map import Command
|
|
9
|
-
from urwid.
|
|
9
|
+
from urwid.split_repr import remove_defaults
|
|
10
10
|
from urwid.util import is_mouse_press
|
|
11
11
|
|
|
12
12
|
from .constants import Sizing, WHSettings
|
|
13
13
|
from .container import WidgetContainerListContentsMixin, WidgetContainerMixin, _ContainerElementSizingFlag
|
|
14
|
+
from .monitored_list import MonitoredFocusList, MonitoredList
|
|
14
15
|
from .widget import Widget, WidgetError, WidgetWarning
|
|
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
|
|
|
@@ -58,36 +59,42 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
58
59
|
|
|
59
60
|
# BOX-only widget
|
|
60
61
|
>>> Pile((SolidFill("#"),))
|
|
61
|
-
<Pile box widget>
|
|
62
|
+
<Pile box widget (1 item)>
|
|
62
63
|
|
|
63
64
|
# GIVEN BOX -> BOX/FLOW
|
|
64
65
|
>>> Pile(((10, SolidFill("#")),))
|
|
65
|
-
<Pile box/flow widget>
|
|
66
|
+
<Pile box/flow widget (1 item)>
|
|
66
67
|
|
|
67
68
|
# FLOW-only
|
|
68
69
|
>>> Pile((ProgressBar(None, None),))
|
|
69
|
-
<Pile flow widget>
|
|
70
|
+
<Pile flow widget (1 item)>
|
|
70
71
|
|
|
71
72
|
# FIXED -> FIXED
|
|
72
73
|
>>> Pile(((WHSettings.PACK, BigText("0", font)),))
|
|
73
|
-
<Pile fixed widget>
|
|
74
|
+
<Pile fixed widget (1 item)>
|
|
74
75
|
|
|
75
76
|
# FLOW/FIXED -> FLOW/FIXED
|
|
76
77
|
>>> Pile(((WHSettings.PACK, Text("text")),))
|
|
77
|
-
<Pile fixed/flow widget>
|
|
78
|
+
<Pile fixed/flow widget (1 item)>
|
|
78
79
|
|
|
79
80
|
# FLOW + FIXED widgets -> FLOW/FIXED
|
|
80
81
|
>>> Pile((ProgressBar(None, None), (WHSettings.PACK, BigText("0", font))))
|
|
81
|
-
<Pile fixed/flow widget>
|
|
82
|
+
<Pile fixed/flow widget (2 items) focus_item=0>
|
|
82
83
|
|
|
83
84
|
# GIVEN BOX + FIXED widgets -> BOX/FLOW/FIXED (GIVEN BOX allows overriding its height & allows any width)
|
|
84
85
|
>>> Pile(((10, SolidFill("#")), (WHSettings.PACK, BigText("0", font))))
|
|
85
|
-
<Pile widget>
|
|
86
|
+
<Pile widget (2 items) focus_item=0>
|
|
86
87
|
|
|
87
88
|
# Invalid sizing combination -> use fallback settings (and produce warning)
|
|
88
89
|
>>> Pile(((WHSettings.WEIGHT, 1, BigText("0", font)),))
|
|
89
|
-
<Pile box/flow widget>
|
|
90
|
+
<Pile box/flow widget (1 item)>
|
|
91
|
+
|
|
92
|
+
# Special case: empty pile widget sizing is impossible to calculate
|
|
93
|
+
>>> Pile(())
|
|
94
|
+
<Pile box/flow widget ()>
|
|
90
95
|
"""
|
|
96
|
+
if not self.contents:
|
|
97
|
+
return frozenset((Sizing.BOX, Sizing.FLOW))
|
|
91
98
|
strict_box = False
|
|
92
99
|
has_flow = False
|
|
93
100
|
|
|
@@ -225,6 +232,43 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
225
232
|
|
|
226
233
|
self.pref_col = 0
|
|
227
234
|
|
|
235
|
+
def _repr_words(self) -> list[str]:
|
|
236
|
+
if len(self.contents) > 1:
|
|
237
|
+
contents_string = f"({len(self.contents)} items)"
|
|
238
|
+
elif self.contents:
|
|
239
|
+
contents_string = "(1 item)"
|
|
240
|
+
else:
|
|
241
|
+
contents_string = "()"
|
|
242
|
+
return [*super()._repr_words(), contents_string]
|
|
243
|
+
|
|
244
|
+
def _repr_attrs(self) -> dict[str, typing.Any]:
|
|
245
|
+
attrs = {**super()._repr_attrs(), "focus_item": self.focus_position if len(self._contents) > 1 else None}
|
|
246
|
+
return remove_defaults(attrs, Pile.__init__)
|
|
247
|
+
|
|
248
|
+
def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
|
|
249
|
+
widget_list: list[
|
|
250
|
+
Widget
|
|
251
|
+
| tuple[Literal[WHSettings.PACK] | int, Widget]
|
|
252
|
+
| tuple[Literal[WHSettings.WEIGHT], int | float, Widget]
|
|
253
|
+
] = []
|
|
254
|
+
|
|
255
|
+
for w_instance, (sizing, amount) in self._contents:
|
|
256
|
+
if sizing == WHSettings.GIVEN:
|
|
257
|
+
widget_list.append((amount, w_instance))
|
|
258
|
+
elif sizing == WHSettings.PACK:
|
|
259
|
+
widget_list.append((WHSettings.PACK, w_instance))
|
|
260
|
+
elif sizing == WHSettings.WEIGHT:
|
|
261
|
+
if amount == 1:
|
|
262
|
+
widget_list.append(w_instance)
|
|
263
|
+
else:
|
|
264
|
+
widget_list.append((WHSettings.WEIGHT, amount, w_instance))
|
|
265
|
+
|
|
266
|
+
yield "widget_list", widget_list
|
|
267
|
+
yield "focus_item", self.focus_position if self._contents else None
|
|
268
|
+
|
|
269
|
+
def __len__(self) -> int:
|
|
270
|
+
return len(self._contents)
|
|
271
|
+
|
|
228
272
|
def _contents_modified(self) -> None:
|
|
229
273
|
"""Recalculate whether this widget should be selectable whenever the contents has been changed."""
|
|
230
274
|
self._selectable = any(w.selectable() for w, o in self.contents)
|
|
@@ -281,7 +325,7 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
281
325
|
chain(self.contents, repeat((None, (WHSettings.WEIGHT, 1)))),
|
|
282
326
|
)
|
|
283
327
|
]
|
|
284
|
-
if focus_position < len(widgets):
|
|
328
|
+
if focus_position < len(widgets): # pylint: disable=consider-using-max-builtin # pylint bug
|
|
285
329
|
self.focus_position = focus_position
|
|
286
330
|
|
|
287
331
|
@property
|
|
@@ -321,7 +365,7 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
321
365
|
(w, ({Sizing.FIXED: WHSettings.GIVEN, Sizing.FLOW: WHSettings.PACK}.get(new_t, new_t), new_height))
|
|
322
366
|
for ((new_t, new_height), (w, options)) in zip(item_types, self.contents)
|
|
323
367
|
]
|
|
324
|
-
if focus_position < len(item_types):
|
|
368
|
+
if focus_position < len(item_types): # pylint: disable=consider-using-max-builtin # pylint bug
|
|
325
369
|
self.focus_position = focus_position
|
|
326
370
|
|
|
327
371
|
@property
|
|
@@ -715,7 +759,7 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
|
|
|
715
759
|
w_h_args.append((maxcol, height))
|
|
716
760
|
elif f == WHSettings.PACK or len(size) == 1:
|
|
717
761
|
if Sizing.FLOW in w_sizing:
|
|
718
|
-
w_h_arg = (maxcol,)
|
|
762
|
+
w_h_arg: tuple[int] | tuple[()] = (maxcol,)
|
|
719
763
|
elif Sizing.FIXED in w_sizing and f == WHSettings.PACK:
|
|
720
764
|
w_h_arg = ()
|
|
721
765
|
else:
|
urwid/widget/progress_bar.py
CHANGED
|
@@ -106,7 +106,11 @@ class ProgressBar(Widget):
|
|
|
106
106
|
percent = min(100, max(0, int(self.current * 100 / self.done)))
|
|
107
107
|
return f"{percent!s} %"
|
|
108
108
|
|
|
109
|
-
def render(
|
|
109
|
+
def render(
|
|
110
|
+
self,
|
|
111
|
+
size: tuple[int], # type: ignore[override]
|
|
112
|
+
focus: bool = False,
|
|
113
|
+
) -> TextCanvas:
|
|
110
114
|
"""
|
|
111
115
|
Render the progress bar.
|
|
112
116
|
"""
|
urwid/widget/scrollable.py
CHANGED
|
@@ -43,7 +43,7 @@ if typing.TYPE_CHECKING:
|
|
|
43
43
|
__all__ = ("ScrollBar", "Scrollable", "ScrollableError", "ScrollbarSymbols")
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
WrappedWidget = typing.TypeVar("WrappedWidget")
|
|
46
|
+
WrappedWidget = typing.TypeVar("WrappedWidget", bound="SupportsScroll")
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class ScrollableError(WidgetError):
|
|
@@ -153,7 +153,11 @@ class Scrollable(WidgetDecoration[WrappedWidget]):
|
|
|
153
153
|
self.force_forward_keypress = force_forward_keypress
|
|
154
154
|
super().__init__(widget)
|
|
155
155
|
|
|
156
|
-
def render(
|
|
156
|
+
def render(
|
|
157
|
+
self,
|
|
158
|
+
size: tuple[int, int], # type: ignore[override]
|
|
159
|
+
focus: bool = False,
|
|
160
|
+
) -> CompositeCanvas:
|
|
157
161
|
from urwid import canvas
|
|
158
162
|
|
|
159
163
|
maxcol, maxrow = size
|
|
@@ -270,7 +274,11 @@ class Scrollable(WidgetDecoration[WrappedWidget]):
|
|
|
270
274
|
|
|
271
275
|
return canv
|
|
272
276
|
|
|
273
|
-
def keypress(
|
|
277
|
+
def keypress(
|
|
278
|
+
self,
|
|
279
|
+
size: tuple[int, int], # type: ignore[override]
|
|
280
|
+
key: str,
|
|
281
|
+
) -> str | None:
|
|
274
282
|
from urwid.command_map import Command
|
|
275
283
|
|
|
276
284
|
# Maybe offer key to original widget
|
|
@@ -311,7 +319,7 @@ class Scrollable(WidgetDecoration[WrappedWidget]):
|
|
|
311
319
|
|
|
312
320
|
def mouse_event(
|
|
313
321
|
self,
|
|
314
|
-
size: tuple[int, int],
|
|
322
|
+
size: tuple[int, int], # type: ignore[override]
|
|
315
323
|
event: str,
|
|
316
324
|
button: int,
|
|
317
325
|
col: int,
|
|
@@ -375,7 +383,10 @@ class Scrollable(WidgetDecoration[WrappedWidget]):
|
|
|
375
383
|
elif cursrow >= self._trim_top + maxrow:
|
|
376
384
|
self._trim_top = max(0, cursrow - maxrow + 1)
|
|
377
385
|
|
|
378
|
-
def _get_original_widget_size(
|
|
386
|
+
def _get_original_widget_size(
|
|
387
|
+
self,
|
|
388
|
+
size: tuple[int, int], # type: ignore[override]
|
|
389
|
+
) -> tuple[int] | tuple[()]:
|
|
379
390
|
ow = self._original_widget
|
|
380
391
|
sizing = ow.sizing()
|
|
381
392
|
if Sizing.FLOW in sizing:
|
|
@@ -465,7 +476,11 @@ class ScrollBar(WidgetDecoration[WrappedWidget]):
|
|
|
465
476
|
self.scrollbar_width = max(1, width)
|
|
466
477
|
self._original_widget_size = (0, 0)
|
|
467
478
|
|
|
468
|
-
def render(
|
|
479
|
+
def render(
|
|
480
|
+
self,
|
|
481
|
+
size: tuple[int, int], # type: ignore[override]
|
|
482
|
+
focus: bool = False,
|
|
483
|
+
) -> Canvas:
|
|
469
484
|
from urwid import canvas
|
|
470
485
|
|
|
471
486
|
maxcol, maxrow = size
|
|
@@ -566,12 +581,16 @@ class ScrollBar(WidgetDecoration[WrappedWidget]):
|
|
|
566
581
|
|
|
567
582
|
raise ScrollableError(f"Not compatible to be wrapped by ScrollBar: {w!r}")
|
|
568
583
|
|
|
569
|
-
def keypress(
|
|
584
|
+
def keypress(
|
|
585
|
+
self,
|
|
586
|
+
size: tuple[int, int], # type: ignore[override]
|
|
587
|
+
key: str,
|
|
588
|
+
) -> str | None:
|
|
570
589
|
return self._original_widget.keypress(self._original_widget_size, key)
|
|
571
590
|
|
|
572
591
|
def mouse_event(
|
|
573
592
|
self,
|
|
574
|
-
size: tuple[int, int],
|
|
593
|
+
size: tuple[int, int], # type: ignore[override]
|
|
575
594
|
event: str,
|
|
576
595
|
button: int,
|
|
577
596
|
col: int,
|
|
@@ -580,7 +599,7 @@ class ScrollBar(WidgetDecoration[WrappedWidget]):
|
|
|
580
599
|
) -> bool | None:
|
|
581
600
|
ow = self._original_widget
|
|
582
601
|
ow_size = self._original_widget_size
|
|
583
|
-
handled = False
|
|
602
|
+
handled: bool | None = False
|
|
584
603
|
if hasattr(ow, "mouse_event"):
|
|
585
604
|
handled = ow.mouse_event(ow_size, event, button, col, row, focus)
|
|
586
605
|
|
urwid/widget/solid_fill.py
CHANGED
|
@@ -31,7 +31,11 @@ class SolidFill(Widget):
|
|
|
31
31
|
def _repr_words(self) -> list[str]:
|
|
32
32
|
return [*super()._repr_words(), repr(self.fill_char)]
|
|
33
33
|
|
|
34
|
-
def render(
|
|
34
|
+
def render(
|
|
35
|
+
self,
|
|
36
|
+
size: tuple[int, int], # type: ignore[override]
|
|
37
|
+
focus: bool = False,
|
|
38
|
+
) -> SolidCanvas:
|
|
35
39
|
"""
|
|
36
40
|
Render the Fill as a canvas and return it.
|
|
37
41
|
|
urwid/widget/text.py
CHANGED
|
@@ -72,7 +72,7 @@ class Text(Widget):
|
|
|
72
72
|
[('bold', 5)]
|
|
73
73
|
"""
|
|
74
74
|
super().__init__()
|
|
75
|
-
self._cache_maxcol = None
|
|
75
|
+
self._cache_maxcol: int | None = None
|
|
76
76
|
self.set_text(markup)
|
|
77
77
|
self.set_layout(align, wrap, layout)
|
|
78
78
|
|
|
@@ -245,7 +245,11 @@ class Text(Widget):
|
|
|
245
245
|
def layout(self):
|
|
246
246
|
return self._layout
|
|
247
247
|
|
|
248
|
-
def render(
|
|
248
|
+
def render(
|
|
249
|
+
self,
|
|
250
|
+
size: tuple[int] | tuple[()], # type: ignore[override]
|
|
251
|
+
focus: bool = False,
|
|
252
|
+
) -> TextCanvas:
|
|
249
253
|
"""
|
|
250
254
|
Render contents with wrapping and alignment. Return canvas.
|
|
251
255
|
|
|
@@ -312,7 +316,11 @@ class Text(Widget):
|
|
|
312
316
|
self._cache_maxcol = maxcol
|
|
313
317
|
self._cache_translation = self.layout.layout(text, maxcol, self._align_mode, self._wrap_mode)
|
|
314
318
|
|
|
315
|
-
def pack(
|
|
319
|
+
def pack(
|
|
320
|
+
self,
|
|
321
|
+
size: tuple[()] | tuple[int] | None = None, # type: ignore[override]
|
|
322
|
+
focus: bool = False,
|
|
323
|
+
) -> tuple[int, int]:
|
|
316
324
|
"""
|
|
317
325
|
Return the number of screen columns and rows required for
|
|
318
326
|
this Text widget to be displayed without wrapping or
|
|
@@ -32,26 +32,34 @@ from __future__ import annotations
|
|
|
32
32
|
|
|
33
33
|
import typing
|
|
34
34
|
|
|
35
|
-
import
|
|
35
|
+
from .columns import Columns
|
|
36
|
+
from .constants import WHSettings
|
|
37
|
+
from .listbox import ListBox, ListWalker
|
|
38
|
+
from .padding import Padding
|
|
39
|
+
from .text import Text
|
|
40
|
+
from .widget import WidgetWrap
|
|
41
|
+
from .wimp import SelectableIcon
|
|
36
42
|
|
|
37
43
|
if typing.TYPE_CHECKING:
|
|
38
44
|
from collections.abc import Hashable, Sequence
|
|
39
45
|
|
|
46
|
+
__all__ = ("TreeWidgetError", "TreeWidget", "TreeNode", "ParentNode", "TreeWalker", "TreeListBox")
|
|
47
|
+
|
|
40
48
|
|
|
41
49
|
class TreeWidgetError(RuntimeError):
|
|
42
50
|
pass
|
|
43
51
|
|
|
44
52
|
|
|
45
|
-
class TreeWidget(
|
|
53
|
+
class TreeWidget(WidgetWrap[Padding[typing.Union[Text, Columns]]]):
|
|
46
54
|
"""A widget representing something in a nested tree display."""
|
|
47
55
|
|
|
48
56
|
indent_cols = 3
|
|
49
|
-
unexpanded_icon =
|
|
50
|
-
expanded_icon =
|
|
57
|
+
unexpanded_icon = SelectableIcon("+", 0)
|
|
58
|
+
expanded_icon = SelectableIcon("-", 0)
|
|
51
59
|
|
|
52
60
|
def __init__(self, node: TreeNode) -> None:
|
|
53
61
|
self._node = node
|
|
54
|
-
self._innerwidget:
|
|
62
|
+
self._innerwidget: Text | None = None
|
|
55
63
|
self.is_leaf = not hasattr(node, "get_first_child")
|
|
56
64
|
self.expanded = True
|
|
57
65
|
widget = self.get_indented_widget()
|
|
@@ -63,31 +71,32 @@ class TreeWidget(urwid.WidgetWrap[urwid.Padding]):
|
|
|
63
71
|
"""
|
|
64
72
|
return not self.is_leaf
|
|
65
73
|
|
|
66
|
-
def get_indented_widget(self) ->
|
|
74
|
+
def get_indented_widget(self) -> Padding[Text | Columns]:
|
|
67
75
|
widget = self.get_inner_widget()
|
|
68
76
|
if not self.is_leaf:
|
|
69
|
-
widget =
|
|
77
|
+
widget = Columns(
|
|
70
78
|
[(1, [self.unexpanded_icon, self.expanded_icon][self.expanded]), widget],
|
|
71
79
|
dividechars=1,
|
|
72
80
|
)
|
|
73
81
|
indent_cols = self.get_indent_cols()
|
|
74
|
-
return
|
|
82
|
+
return Padding(widget, width=(WHSettings.RELATIVE, 100), left=indent_cols)
|
|
75
83
|
|
|
76
84
|
def update_expanded_icon(self) -> None:
|
|
77
85
|
"""Update display widget text for parent widgets"""
|
|
78
86
|
# icon is first element in columns indented widget
|
|
79
|
-
|
|
87
|
+
icon = [self.unexpanded_icon, self.expanded_icon][self.expanded]
|
|
88
|
+
self._w.original_widget.contents[0] = (icon, (WHSettings.GIVEN, 1, False))
|
|
80
89
|
|
|
81
90
|
def get_indent_cols(self) -> int:
|
|
82
91
|
return self.indent_cols * self.get_node().get_depth()
|
|
83
92
|
|
|
84
|
-
def get_inner_widget(self) ->
|
|
93
|
+
def get_inner_widget(self) -> Text:
|
|
85
94
|
if self._innerwidget is None:
|
|
86
95
|
self._innerwidget = self.load_inner_widget()
|
|
87
96
|
return self._innerwidget
|
|
88
97
|
|
|
89
|
-
def load_inner_widget(self) ->
|
|
90
|
-
return
|
|
98
|
+
def load_inner_widget(self) -> Text:
|
|
99
|
+
return Text(self.get_display_text())
|
|
91
100
|
|
|
92
101
|
def get_node(self) -> TreeNode:
|
|
93
102
|
return self._node
|
|
@@ -141,7 +150,11 @@ class TreeWidget(urwid.WidgetWrap[urwid.Padding]):
|
|
|
141
150
|
prev_node = this_node.get_parent()
|
|
142
151
|
return prev_node.get_widget()
|
|
143
152
|
|
|
144
|
-
def keypress(
|
|
153
|
+
def keypress(
|
|
154
|
+
self,
|
|
155
|
+
size: tuple[int] | tuple[()],
|
|
156
|
+
key: str,
|
|
157
|
+
) -> str | None:
|
|
145
158
|
"""Handle expand & collapse requests (non-leaf nodes)"""
|
|
146
159
|
if self.is_leaf:
|
|
147
160
|
return key
|
|
@@ -227,7 +240,7 @@ class TreeNode:
|
|
|
227
240
|
self._parent = parent
|
|
228
241
|
self._value = value
|
|
229
242
|
self._depth = depth
|
|
230
|
-
self._widget = None
|
|
243
|
+
self._widget: TreeWidget | None = None
|
|
231
244
|
|
|
232
245
|
def get_widget(self, reload: bool = False) -> TreeWidget:
|
|
233
246
|
"""Return the widget for this node."""
|
|
@@ -403,7 +416,7 @@ class ParentNode(TreeNode):
|
|
|
403
416
|
return len(self.get_child_keys()) > 0
|
|
404
417
|
|
|
405
418
|
|
|
406
|
-
class TreeWalker(
|
|
419
|
+
class TreeWalker(ListWalker):
|
|
407
420
|
"""ListWalker-compatible class for displaying TreeWidgets
|
|
408
421
|
|
|
409
422
|
positions are TreeNodes."""
|
|
@@ -438,12 +451,16 @@ class TreeWalker(urwid.ListWalker):
|
|
|
438
451
|
# pylint: enable=arguments-renamed
|
|
439
452
|
|
|
440
453
|
|
|
441
|
-
class TreeListBox(
|
|
454
|
+
class TreeListBox(ListBox):
|
|
442
455
|
"""A ListBox with special handling for navigation and
|
|
443
456
|
collapsing of TreeWidgets"""
|
|
444
457
|
|
|
445
|
-
def keypress(
|
|
446
|
-
|
|
458
|
+
def keypress(
|
|
459
|
+
self,
|
|
460
|
+
size: tuple[int, int], # type: ignore[override]
|
|
461
|
+
key: str,
|
|
462
|
+
) -> str | None:
|
|
463
|
+
key: str | None = super().keypress(size, key)
|
|
447
464
|
return self.unhandled_input(size, key)
|
|
448
465
|
|
|
449
466
|
def unhandled_input(self, size: tuple[int, int], data: str) -> str | None:
|