urwid 2.6.9__py3-none-any.whl → 2.6.11__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.
- urwid/version.py +2 -2
- urwid/widget/listbox.py +25 -5
- urwid/widget/monitored_list.py +65 -19
- urwid/widget/padding.py +4 -2
- urwid/widget/popup.py +16 -13
- urwid/widget/scrollable.py +22 -10
- urwid/widget/widget.py +1 -1
- {urwid-2.6.9.dist-info → urwid-2.6.11.dist-info}/METADATA +1 -1
- {urwid-2.6.9.dist-info → urwid-2.6.11.dist-info}/RECORD +12 -12
- {urwid-2.6.9.dist-info → urwid-2.6.11.dist-info}/COPYING +0 -0
- {urwid-2.6.9.dist-info → urwid-2.6.11.dist-info}/WHEEL +0 -0
- {urwid-2.6.9.dist-info → urwid-2.6.11.dist-info}/top_level.txt +0 -0
urwid/version.py
CHANGED
urwid/widget/listbox.py
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
from __future__ import annotations
|
|
22
22
|
|
|
23
|
+
import operator
|
|
23
24
|
import typing
|
|
24
25
|
import warnings
|
|
25
26
|
from collections.abc import Iterable, Sized
|
|
@@ -77,6 +78,18 @@ class ScrollSupportingBody(Protocol):
|
|
|
77
78
|
def get_prev(self, position: _K) -> tuple[Widget, _K] | tuple[None, None]: ...
|
|
78
79
|
|
|
79
80
|
|
|
81
|
+
@runtime_checkable
|
|
82
|
+
class EstimatedSized(Protocol):
|
|
83
|
+
"""Widget can estimate it's size.
|
|
84
|
+
|
|
85
|
+
PEP 424 defines API for memory-efficiency.
|
|
86
|
+
For the ListBox it's a sign of the limited body length.
|
|
87
|
+
The main use-case is lazy-load, where real length calculation is expensive.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __length_hint__(self) -> int: ...
|
|
91
|
+
|
|
92
|
+
|
|
80
93
|
class ListWalker(metaclass=signals.MetaSignals): # pylint: disable=no-member, unsubscriptable-object
|
|
81
94
|
# mixin not named as mixin
|
|
82
95
|
signals: typing.ClassVar[list[str]] = ["modified"]
|
|
@@ -431,11 +444,18 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
431
444
|
)
|
|
432
445
|
self.body = body
|
|
433
446
|
|
|
434
|
-
|
|
447
|
+
@property
|
|
448
|
+
def __len__(self) -> Callable[[], int]:
|
|
435
449
|
if isinstance(self._body, Sized):
|
|
436
|
-
return
|
|
450
|
+
return self._body.__len__
|
|
437
451
|
raise AttributeError(f"{self._body.__class__.__name__} is not Sized")
|
|
438
452
|
|
|
453
|
+
@property
|
|
454
|
+
def __length_hint__(self) -> Callable[[], int]: # pylint: disable=invalid-length-hint-returned
|
|
455
|
+
if isinstance(self._body, (Sized, EstimatedSized)):
|
|
456
|
+
return lambda: operator.length_hint(self._body)
|
|
457
|
+
raise AttributeError(f'{self._body.__class__.__name__} is not Sized and do not implement "__length_hint__"')
|
|
458
|
+
|
|
439
459
|
def calculate_visible(
|
|
440
460
|
self,
|
|
441
461
|
size: tuple[int, int],
|
|
@@ -577,9 +597,9 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
577
597
|
if not isinstance(self._body, ScrollSupportingBody):
|
|
578
598
|
raise ListBoxError(f"{self} body do not implement methods required for scrolling protocol")
|
|
579
599
|
|
|
580
|
-
if not isinstance(self._body, (Sized, TreeWalker)):
|
|
600
|
+
if not isinstance(self._body, (Sized, EstimatedSized, TreeWalker)):
|
|
581
601
|
raise ListBoxError(
|
|
582
|
-
f"{self} body is not a Sized and not a TreeWalker."
|
|
602
|
+
f"{self} body is not a Sized, can not estimate it's size and not a TreeWalker."
|
|
583
603
|
f"Scroll is not allowed due to risk of infinite cycle of widgets load."
|
|
584
604
|
)
|
|
585
605
|
|
|
@@ -643,7 +663,7 @@ class ListBox(Widget, WidgetContainerMixin):
|
|
|
643
663
|
|
|
644
664
|
def require_relative_scroll(self, size: tuple[int, int], focus: bool = False) -> bool:
|
|
645
665
|
"""Widget require relative scroll due to performance limitations of real lines count calculation."""
|
|
646
|
-
return isinstance(self._body, Sized) and (size[1] * 3 <
|
|
666
|
+
return isinstance(self._body, (Sized, EstimatedSized)) and (size[1] * 3 < operator.length_hint(self.body))
|
|
647
667
|
|
|
648
668
|
def get_first_visible_pos(self, size: tuple[int, int], focus: bool = False) -> int:
|
|
649
669
|
self._check_support_scrolling()
|
urwid/widget/monitored_list.py
CHANGED
|
@@ -24,9 +24,9 @@ import typing
|
|
|
24
24
|
import warnings
|
|
25
25
|
|
|
26
26
|
if typing.TYPE_CHECKING:
|
|
27
|
-
from collections.abc import Callable, Collection, Iterator
|
|
27
|
+
from collections.abc import Callable, Collection, Iterable, Iterator
|
|
28
28
|
|
|
29
|
-
from typing_extensions import Concatenate, ParamSpec
|
|
29
|
+
from typing_extensions import Concatenate, ParamSpec, Self
|
|
30
30
|
|
|
31
31
|
ArgSpec = ParamSpec("ArgSpec")
|
|
32
32
|
Ret = typing.TypeVar("Ret")
|
|
@@ -88,23 +88,69 @@ class MonitoredList(typing.List[_T], typing.Generic[_T]):
|
|
|
88
88
|
for item in self:
|
|
89
89
|
yield None, item
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
91
|
+
@_call_modified
|
|
92
|
+
def __add__(self, __value: list[_T]) -> Self:
|
|
93
|
+
return super().__add__(__value)
|
|
94
|
+
|
|
95
|
+
@_call_modified
|
|
96
|
+
def __delitem__(self, __key: typing.SupportsIndex | slice) -> None:
|
|
97
|
+
super().__delitem__(__key)
|
|
98
|
+
|
|
99
|
+
@_call_modified
|
|
100
|
+
def __iadd__(self, __value: Iterable[_T]) -> Self:
|
|
101
|
+
return super().__iadd__(__value)
|
|
102
|
+
|
|
103
|
+
@_call_modified
|
|
104
|
+
def __rmul__(self, __value: typing.SupportsIndex) -> Self:
|
|
105
|
+
return super().__rmul__(__value)
|
|
106
|
+
|
|
107
|
+
@_call_modified
|
|
108
|
+
def __imul__(self, __value: typing.SupportsIndex) -> Self:
|
|
109
|
+
return super().__imul__(__value)
|
|
110
|
+
|
|
111
|
+
@typing.overload
|
|
112
|
+
@_call_modified
|
|
113
|
+
def __setitem__(self, __key: typing.SupportsIndex, __value: _T) -> None: ...
|
|
114
|
+
|
|
115
|
+
@typing.overload
|
|
116
|
+
@_call_modified
|
|
117
|
+
def __setitem__(self, __key: slice, __value: Iterable[_T]) -> None: ...
|
|
118
|
+
|
|
119
|
+
@_call_modified
|
|
120
|
+
def __setitem__(self, __key: typing.SupportsIndex | slice, __value: _T | Iterable[_T]) -> None:
|
|
121
|
+
super().__setitem__(__key, __value)
|
|
122
|
+
|
|
123
|
+
@_call_modified
|
|
124
|
+
def append(self, __object: _T) -> None:
|
|
125
|
+
super().append(__object)
|
|
126
|
+
|
|
127
|
+
@_call_modified
|
|
128
|
+
def extend(self, __iterable: Iterable[_T]) -> None:
|
|
129
|
+
super().extend(__iterable)
|
|
130
|
+
|
|
131
|
+
@_call_modified
|
|
132
|
+
def pop(self, __index: typing.SupportsIndex = -1) -> _T:
|
|
133
|
+
return super().pop(__index)
|
|
134
|
+
|
|
135
|
+
@_call_modified
|
|
136
|
+
def insert(self, __index: typing.SupportsIndex, __object: _T) -> None:
|
|
137
|
+
super().insert(__index, __object)
|
|
138
|
+
|
|
139
|
+
@_call_modified
|
|
140
|
+
def remove(self, __value: _T) -> None:
|
|
141
|
+
super().remove(__value)
|
|
142
|
+
|
|
143
|
+
@_call_modified
|
|
144
|
+
def reverse(self) -> None:
|
|
145
|
+
super().reverse()
|
|
146
|
+
|
|
147
|
+
@_call_modified
|
|
148
|
+
def sort(self, *, key: Callable[[_T], typing.Any] | None = None, reverse: bool = False) -> None:
|
|
149
|
+
super().sort(key=key, reverse=reverse)
|
|
150
|
+
|
|
151
|
+
@_call_modified
|
|
152
|
+
def clear(self) -> None:
|
|
153
|
+
super().clear()
|
|
108
154
|
|
|
109
155
|
|
|
110
156
|
class MonitoredFocusList(MonitoredList[_T], typing.Generic[_T]):
|
urwid/widget/padding.py
CHANGED
|
@@ -40,12 +40,14 @@ class Padding(WidgetDecoration[WrappedWidget], typing.Generic[WrappedWidget]):
|
|
|
40
40
|
self,
|
|
41
41
|
w: WrappedWidget,
|
|
42
42
|
align: (
|
|
43
|
-
Literal["left", "center", "right"]
|
|
43
|
+
Literal["left", "center", "right"]
|
|
44
|
+
| Align
|
|
45
|
+
| tuple[Literal["relative", WHSettings.RELATIVE, "fixed left", "fixed right"], int]
|
|
44
46
|
) = Align.LEFT,
|
|
45
47
|
width: (
|
|
46
48
|
int
|
|
47
49
|
| Literal["pack", "clip", WHSettings.PACK, WHSettings.CLIP]
|
|
48
|
-
| tuple[Literal["relative", WHSettings.RELATIVE], int]
|
|
50
|
+
| tuple[Literal["relative", WHSettings.RELATIVE, "fixed left", "fixed right"], int]
|
|
49
51
|
) = RELATIVE_100,
|
|
50
52
|
min_width: int | None = None,
|
|
51
53
|
left: int = 0,
|
urwid/widget/popup.py
CHANGED
|
@@ -24,7 +24,7 @@ import typing
|
|
|
24
24
|
|
|
25
25
|
from urwid.canvas import CompositeCanvas
|
|
26
26
|
|
|
27
|
-
from .constants import Sizing
|
|
27
|
+
from .constants import Align, Sizing, VAlign
|
|
28
28
|
from .overlay import Overlay
|
|
29
29
|
from .widget import delegate_to_widget_mixin
|
|
30
30
|
from .widget_decoration import WidgetDecoration
|
|
@@ -86,8 +86,7 @@ class PopUpLauncher(delegate_to_widget_mixin("_original_widget"), WidgetDecorati
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
class PopUpTarget(WidgetDecoration[WrappedWidget]):
|
|
89
|
-
# FIXME: this whole class is a terrible hack and must be fixed
|
|
90
|
-
# when layout and rendering are separated
|
|
89
|
+
# FIXME: this whole class is a terrible hack and must be fixed when layout and rendering are separated
|
|
91
90
|
_sizing = frozenset((Sizing.BOX,))
|
|
92
91
|
_selectable = True
|
|
93
92
|
|
|
@@ -105,19 +104,23 @@ class PopUpTarget(WidgetDecoration[WrappedWidget]):
|
|
|
105
104
|
if self._pop_up != w:
|
|
106
105
|
self._pop_up = w
|
|
107
106
|
self._current_widget = Overlay(
|
|
108
|
-
w,
|
|
109
|
-
self._original_widget,
|
|
110
|
-
|
|
111
|
-
overlay_width,
|
|
112
|
-
|
|
113
|
-
overlay_height,
|
|
107
|
+
top_w=w,
|
|
108
|
+
bottom_w=self._original_widget,
|
|
109
|
+
align=Align.LEFT,
|
|
110
|
+
width=overlay_width,
|
|
111
|
+
valign=VAlign.TOP,
|
|
112
|
+
height=overlay_height,
|
|
113
|
+
left=left,
|
|
114
|
+
top=top,
|
|
114
115
|
)
|
|
115
116
|
else:
|
|
116
117
|
self._current_widget.set_overlay_parameters(
|
|
117
|
-
|
|
118
|
-
overlay_width,
|
|
119
|
-
|
|
120
|
-
overlay_height,
|
|
118
|
+
align=Align.LEFT,
|
|
119
|
+
width=overlay_width,
|
|
120
|
+
valign=VAlign.TOP,
|
|
121
|
+
height=overlay_height,
|
|
122
|
+
left=left,
|
|
123
|
+
top=top,
|
|
121
124
|
)
|
|
122
125
|
else:
|
|
123
126
|
self._pop_up = None
|
urwid/widget/scrollable.py
CHANGED
|
@@ -130,8 +130,6 @@ class SupportsScroll(WidgetProto, Protocol):
|
|
|
130
130
|
class SupportsRelativeScroll(WidgetProto, Protocol):
|
|
131
131
|
"""Relative scroll-specific methods."""
|
|
132
132
|
|
|
133
|
-
def __len__(self) -> int: ...
|
|
134
|
-
|
|
135
133
|
def require_relative_scroll(self, size: tuple[int, int], focus: bool = False) -> bool: ...
|
|
136
134
|
|
|
137
135
|
def get_first_visible_pos(self, size: tuple[int, int], focus: bool = False) -> int: ...
|
|
@@ -516,18 +514,32 @@ class ScrollBar(WidgetDecoration[WrappedWidget]):
|
|
|
516
514
|
ow = self._original_widget
|
|
517
515
|
ow_base = self.scrolling_base_widget
|
|
518
516
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
517
|
+
# Use hasattr instead of protocol: hasattr will return False in case of getattr raise AttributeError
|
|
518
|
+
# Use __length_hint__ first since it's less resource intensive
|
|
519
|
+
use_relative = (
|
|
520
|
+
isinstance(ow_base, SupportsRelativeScroll)
|
|
521
|
+
and any(hasattr(ow_base, attrib) for attrib in ("__length_hint__", "__len__"))
|
|
522
|
+
and ow_base.require_relative_scroll(size, focus)
|
|
523
|
+
)
|
|
523
524
|
|
|
525
|
+
if use_relative:
|
|
526
|
+
# `operator.length_hint` is Protocol (Spec) over class based and can end false-negative on the instance
|
|
527
|
+
# use length_hint-like approach with safe `AttributeError` handling
|
|
528
|
+
ow_len = getattr(ow_base, "__len__", getattr(ow_base, "__length_hint__", lambda: 0))()
|
|
524
529
|
ow_canv = render_for_scrollbar()
|
|
525
|
-
visible_amount =
|
|
530
|
+
visible_amount = ow_base.get_visible_amount(ow_size, focus)
|
|
526
531
|
pos = ow_base.get_first_visible_pos(ow_size, focus)
|
|
527
|
-
posmax = len(ow) - visible_amount
|
|
528
|
-
thumb_weight = min(1.0, visible_amount / max(1, len(ow)))
|
|
529
532
|
|
|
530
|
-
|
|
533
|
+
# in the case of estimated length, it can be smaller than real widget length
|
|
534
|
+
ow_len = max(ow_len, visible_amount, pos)
|
|
535
|
+
posmax = ow_len - visible_amount
|
|
536
|
+
thumb_weight = min(1.0, visible_amount / max(1, ow_len))
|
|
537
|
+
|
|
538
|
+
if ow_len == visible_amount:
|
|
539
|
+
# Corner case: formally all contents indexes should be visible, but this does not mean all rows
|
|
540
|
+
use_relative = False
|
|
541
|
+
|
|
542
|
+
if not use_relative:
|
|
531
543
|
ow_rows_max = ow_base.rows_max(size, focus)
|
|
532
544
|
if ow_rows_max <= maxrow:
|
|
533
545
|
# Canvas fits without scrolling - no scrollbar needed
|
urwid/widget/widget.py
CHANGED
|
@@ -11,7 +11,7 @@ urwid/split_repr.py,sha256=pXzuddzQ4RnfIl537Gvoe8PVaBRHCPnxgdYvKK0qm8k,3899
|
|
|
11
11
|
urwid/str_util.py,sha256=1ss-LHAhjXTynTVBcoSgYWIpBeN_RirqHYhP7fq7MrA,10844
|
|
12
12
|
urwid/text_layout.py,sha256=lHiGfo7clmwHt5dMl_AaQSs2ov9IbY9JySTATZBm7h0,22821
|
|
13
13
|
urwid/util.py,sha256=pGXnma03_IBx3v6_qN9cDWPXPj_YjkhQ9IxLjm_0TpU,15747
|
|
14
|
-
urwid/version.py,sha256=
|
|
14
|
+
urwid/version.py,sha256=xlDSe3Xh6QX74LV4NLD5AhuGgpD8erExdJU0ZVYLBLM,413
|
|
15
15
|
urwid/vterm.py,sha256=q9-8dxJpVr_7a0Bj_z_FYDiRgzos3IS17BjhYcy1RDw,58410
|
|
16
16
|
urwid/wimp.py,sha256=o1YsjL_tBLXba8ZRr1MTHH0poLSlXT_atY3-uD_Ualg,567
|
|
17
17
|
urwid/display/__init__.py,sha256=y90WbHPNRHlimPrXhzsSfFsBbBHy8QErmB1y4Xza-xo,1732
|
|
@@ -53,22 +53,22 @@ urwid/widget/filler.py,sha256=EEnyAawdKXuwf79pErfNuvqsU1SVTutcMUrwWkU4wfo,14665
|
|
|
53
53
|
urwid/widget/frame.py,sha256=AOe4FwjvwcIwrpXqyZkCwSZWwAALNl1XRMAKx6ZXYWs,21834
|
|
54
54
|
urwid/widget/grid_flow.py,sha256=otujeOTWYDr4gwuDikBm9zZd8SUgma9yvQ1hDWI-dGk,21514
|
|
55
55
|
urwid/widget/line_box.py,sha256=V750xiZtkw6_uRXLhNY91ER3pXwwrZstVv_IJUZd_YY,6884
|
|
56
|
-
urwid/widget/listbox.py,sha256=
|
|
57
|
-
urwid/widget/monitored_list.py,sha256=
|
|
56
|
+
urwid/widget/listbox.py,sha256=EaGo13fpt4Oy9V00n27U-2PhOWa7-nmsTgnddKv9GII,74392
|
|
57
|
+
urwid/widget/monitored_list.py,sha256=mZUPYB1yhuKN6su6_VjmoiE0LlTXsYxkH14pbif0tbc,19264
|
|
58
58
|
urwid/widget/overlay.py,sha256=7EtTHOomIF4xzwYiY7SiMCQZF2IHrmO88YeosFOMFo0,34505
|
|
59
|
-
urwid/widget/padding.py,sha256=
|
|
59
|
+
urwid/widget/padding.py,sha256=pBXQsnkySV1GXgveg2Ivm6SCqHiGa2YkYSjXaoCUIHk,21334
|
|
60
60
|
urwid/widget/pile.py,sha256=aIzKYNrSQtbHVk_bn2PElHUiSdsO_NKnL4mTu-sXm4I,38628
|
|
61
|
-
urwid/widget/popup.py,sha256=
|
|
61
|
+
urwid/widget/popup.py,sha256=vw9_S1UC6UMy5Roc4qK8NXgH5_dDGxoHmpDAi_u9wsI,5974
|
|
62
62
|
urwid/widget/progress_bar.py,sha256=HRnIu2V2_4wgsnAdrhK-GVVToyLaXRH0gtlkWllA4Mo,4767
|
|
63
|
-
urwid/widget/scrollable.py,sha256=
|
|
63
|
+
urwid/widget/scrollable.py,sha256=yB3S3k5dOdZ-wrQMlgxQH-1AZONSf5X6Y2accIa9tpk,24279
|
|
64
64
|
urwid/widget/solid_fill.py,sha256=NZRDSRK0lP4r7gXcKDgVaKEoeOcWvtrY5k2aueQEEL4,1260
|
|
65
65
|
urwid/widget/text.py,sha256=jy15hL6rCBoJdZviq2jJ-i9eO6vEcxvzCIVAZs12AUw,12187
|
|
66
66
|
urwid/widget/treetools.py,sha256=5s3Dnp2PCz4wQccutmdvsTTbAROJMLehXe4moB5POY0,17387
|
|
67
|
-
urwid/widget/widget.py,sha256=
|
|
67
|
+
urwid/widget/widget.py,sha256=A0ZjvDjLqhdzelPFC96Ozo1voIoGkxftNmJh8X1IyNI,29557
|
|
68
68
|
urwid/widget/widget_decoration.py,sha256=aHP9Y7JKVCI2dw2OdRrzMj9p--C1pMIbxSJskWBLIgc,5633
|
|
69
69
|
urwid/widget/wimp.py,sha256=4wfzTQBLMbhicSlL64hBPb-1W5FAFrIKfb43i10MKSY,27305
|
|
70
|
-
urwid-2.6.
|
|
71
|
-
urwid-2.6.
|
|
72
|
-
urwid-2.6.
|
|
73
|
-
urwid-2.6.
|
|
74
|
-
urwid-2.6.
|
|
70
|
+
urwid-2.6.11.dist-info/COPYING,sha256=NrbT-keRaUP9X-wxPFhHhJRgR-wTN6eLRA5ZkstZX4k,26434
|
|
71
|
+
urwid-2.6.11.dist-info/METADATA,sha256=BdPqGaA0WgGq3ISj7YQYEMzmQKAGoFn6OBAVpJE1Fl0,11043
|
|
72
|
+
urwid-2.6.11.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
73
|
+
urwid-2.6.11.dist-info/top_level.txt,sha256=AwxQA43kNkjHbhYELXHBKrQ01X5CR2KnDzU07cVqilY,6
|
|
74
|
+
urwid-2.6.11.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|