urwid 2.6.15__py3-none-any.whl → 3.0.5__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.
Files changed (62) hide show
  1. urwid/__init__.py +30 -20
  2. urwid/canvas.py +34 -53
  3. urwid/command_map.py +6 -4
  4. urwid/container.py +1 -1
  5. urwid/decoration.py +1 -1
  6. urwid/display/__init__.py +53 -48
  7. urwid/display/_posix_raw_display.py +20 -8
  8. urwid/display/_raw_display_base.py +21 -16
  9. urwid/display/_win32_raw_display.py +16 -17
  10. urwid/display/common.py +45 -74
  11. urwid/display/curses.py +3 -5
  12. urwid/display/escape.py +28 -13
  13. urwid/display/lcd.py +8 -10
  14. urwid/display/web.py +11 -16
  15. urwid/event_loop/asyncio_loop.py +35 -15
  16. urwid/event_loop/main_loop.py +18 -23
  17. urwid/event_loop/tornado_loop.py +4 -5
  18. urwid/event_loop/trio_loop.py +1 -1
  19. urwid/font.py +19 -22
  20. urwid/numedit.py +65 -65
  21. urwid/signals.py +19 -27
  22. urwid/split_repr.py +9 -3
  23. urwid/str_util.py +105 -60
  24. urwid/text_layout.py +14 -13
  25. urwid/util.py +8 -19
  26. urwid/version.py +22 -4
  27. urwid/vterm.py +20 -47
  28. urwid/widget/__init__.py +0 -6
  29. urwid/widget/attr_map.py +10 -10
  30. urwid/widget/attr_wrap.py +11 -13
  31. urwid/widget/bar_graph.py +3 -8
  32. urwid/widget/big_text.py +8 -9
  33. urwid/widget/box_adapter.py +6 -6
  34. urwid/widget/columns.py +52 -83
  35. urwid/widget/container.py +29 -75
  36. urwid/widget/divider.py +6 -6
  37. urwid/widget/edit.py +50 -50
  38. urwid/widget/filler.py +14 -14
  39. urwid/widget/frame.py +31 -40
  40. urwid/widget/grid_flow.py +25 -110
  41. urwid/widget/line_box.py +31 -18
  42. urwid/widget/listbox.py +16 -51
  43. urwid/widget/monitored_list.py +75 -49
  44. urwid/widget/overlay.py +4 -37
  45. urwid/widget/padding.py +31 -68
  46. urwid/widget/pile.py +179 -158
  47. urwid/widget/popup.py +2 -2
  48. urwid/widget/progress_bar.py +17 -18
  49. urwid/widget/scrollable.py +26 -34
  50. urwid/widget/solid_fill.py +3 -3
  51. urwid/widget/text.py +44 -30
  52. urwid/widget/treetools.py +27 -48
  53. urwid/widget/widget.py +13 -130
  54. urwid/widget/widget_decoration.py +6 -35
  55. urwid/widget/wimp.py +61 -61
  56. urwid/wimp.py +1 -1
  57. {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info}/METADATA +24 -24
  58. urwid-3.0.5.dist-info/RECORD +74 -0
  59. {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info}/WHEEL +1 -1
  60. urwid-2.6.15.dist-info/RECORD +0 -74
  61. {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info/licenses}/COPYING +0 -0
  62. {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info}/top_level.txt +0 -0
@@ -25,8 +25,6 @@ import contextlib
25
25
  import enum
26
26
  import typing
27
27
 
28
- from typing_extensions import Protocol, runtime_checkable
29
-
30
28
  from .constants import BOX_SYMBOLS, SHADE_SYMBOLS, Sizing
31
29
  from .widget_decoration import WidgetDecoration, WidgetError
32
30
 
@@ -84,8 +82,8 @@ class ScrollbarSymbols(str, enum.Enum):
84
82
  DRAWING_DOUBLE = BOX_SYMBOLS.DOUBLE.VERTICAL
85
83
 
86
84
 
87
- @runtime_checkable
88
- class WidgetProto(Protocol):
85
+ @typing.runtime_checkable
86
+ class WidgetProto(typing.Protocol):
89
87
  """Protocol for widget.
90
88
 
91
89
  Due to protocol cannot inherit non-protocol bases, define several obligatory Widget methods.
@@ -117,8 +115,8 @@ class WidgetProto(Protocol):
117
115
  def render(self, size: tuple[int, int], focus: bool = False) -> Canvas: ...
118
116
 
119
117
 
120
- @runtime_checkable
121
- class SupportsScroll(WidgetProto, Protocol):
118
+ @typing.runtime_checkable
119
+ class SupportsScroll(WidgetProto, typing.Protocol):
122
120
  """Scroll specific methods."""
123
121
 
124
122
  def get_scrollpos(self, size: tuple[int, int], focus: bool = False) -> int: ...
@@ -126,8 +124,8 @@ class SupportsScroll(WidgetProto, Protocol):
126
124
  def rows_max(self, size: tuple[int, int] | None = None, focus: bool = False) -> int: ...
127
125
 
128
126
 
129
- @runtime_checkable
130
- class SupportsRelativeScroll(WidgetProto, Protocol):
127
+ @typing.runtime_checkable
128
+ class SupportsRelativeScroll(WidgetProto, typing.Protocol):
131
129
  """Relative scroll-specific methods."""
132
130
 
133
131
  def require_relative_scroll(self, size: tuple[int, int], focus: bool = False) -> bool: ...
@@ -140,10 +138,7 @@ class SupportsRelativeScroll(WidgetProto, Protocol):
140
138
  def orig_iter(w: Widget) -> Iterator[Widget]:
141
139
  visited = {w}
142
140
  yield w
143
- while hasattr(w, "original_widget"):
144
- w = w.original_widget
145
- if w in visited:
146
- break
141
+ while (w := getattr(w, "original_widget", w)) not in visited:
147
142
  visited.add(w)
148
143
  yield w
149
144
 
@@ -194,8 +189,8 @@ class Scrollable(WidgetDecoration[WrappedWidget]):
194
189
  first_visible = False
195
190
  for pwi, (w, _o) in enumerate(ow.contents):
196
191
  wcanv = w.render((maxcol,))
197
- wh = wcanv.rows()
198
- if wh:
192
+
193
+ if wh := wcanv.rows():
199
194
  ch += wh
200
195
 
201
196
  if not last_hidden and ch >= self._trim_top:
@@ -235,17 +230,13 @@ class Scrollable(WidgetDecoration[WrappedWidget]):
235
230
  canv = canvas.CompositeCanvas(canv_full)
236
231
  canv_cols, canv_rows = canv.cols(), canv.rows()
237
232
 
238
- if canv_cols <= maxcol:
239
- pad_width = maxcol - canv_cols
240
- if pad_width > 0:
241
- # Canvas is narrower than available horizontal space
242
- canv.pad_trim_left_right(0, pad_width)
233
+ if canv_cols <= maxcol and (pad_width := maxcol - canv_cols) > 0:
234
+ # Canvas is narrower than available horizontal space
235
+ canv.pad_trim_left_right(0, pad_width)
243
236
 
244
- if canv_rows <= maxrow:
245
- fill_height = maxrow - canv_rows
246
- if fill_height > 0:
247
- # Canvas is lower than available vertical space
248
- canv.pad_trim_top_bottom(0, fill_height)
237
+ if canv_rows <= maxrow and (fill_height := maxrow - canv_rows) > 0:
238
+ # Canvas is lower than available vertical space
239
+ canv.pad_trim_top_bottom(0, fill_height)
249
240
 
250
241
  if canv_cols <= maxcol and canv_rows <= maxrow:
251
242
  # Canvas is small enough to fit without trimming
@@ -537,7 +528,7 @@ class ScrollBar(WidgetDecoration[WrappedWidget]):
537
528
  if use_relative:
538
529
  # `operator.length_hint` is Protocol (Spec) over class based and can end false-negative on the instance
539
530
  # use length_hint-like approach with safe `AttributeError` handling
540
- ow_len = getattr(ow_base, "__len__", getattr(ow_base, "__length_hint__", lambda: 0))()
531
+ ow_len = getattr(ow_base, "__len__", getattr(ow_base, "__length_hint__", int))()
541
532
  ow_canv = render_for_scrollbar()
542
533
  visible_amount = ow_base.get_visible_amount(ow_size, focus)
543
534
  pos = ow_base.get_first_visible_pos(ow_size, focus)
@@ -552,17 +543,18 @@ class ScrollBar(WidgetDecoration[WrappedWidget]):
552
543
  use_relative = False
553
544
 
554
545
  if not use_relative:
555
- ow_rows_max = ow_base.rows_max(size, focus)
556
- if ow_rows_max <= maxrow:
546
+ if ow_base.rows_max(size, focus) > maxrow:
547
+ ow_canv = render_for_scrollbar()
548
+ # re-calculate using wrapped size
549
+ ow_rows_max = ow_base.rows_max(ow_size, focus)
550
+ pos = ow_base.get_scrollpos(ow_size, focus)
551
+ posmax = ow_rows_max - maxrow
552
+ thumb_weight = min(1.0, maxrow / max(1, ow_rows_max))
553
+
554
+ else:
557
555
  # Canvas fits without scrolling - no scrollbar needed
558
556
  return render_no_scrollbar()
559
557
 
560
- ow_canv = render_for_scrollbar()
561
- ow_rows_max = ow_base.rows_max(ow_size, focus)
562
- pos = ow_base.get_scrollpos(ow_size, focus)
563
- posmax = ow_rows_max - maxrow
564
- thumb_weight = min(1.0, maxrow / max(1, ow_rows_max))
565
-
566
558
  # Thumb shrinks/grows according to the ratio of <number of visible lines> / <number of total lines>
567
559
  thumb_height = max(1, round(thumb_weight * maxrow)) # pylint: disable=possibly-used-before-assignment
568
560
 
@@ -570,7 +562,7 @@ class ScrollBar(WidgetDecoration[WrappedWidget]):
570
562
  top_weight = float(pos) / max(1, posmax) # pylint: disable=possibly-used-before-assignment
571
563
  top_height = int((maxrow - thumb_height) * top_weight)
572
564
  if top_height == 0 and top_weight > 0:
573
- top_height = 1
565
+ top_height = min(1, maxrow - thumb_height)
574
566
 
575
567
  # Bottom part is remaining space
576
568
  bottom_height = maxrow - thumb_height - top_height
@@ -22,7 +22,7 @@ class SolidFill(Widget):
22
22
  :param fill_char: character to fill area with
23
23
  :type fill_char: bytes or unicode
24
24
 
25
- >>> SolidFill(u'8')
25
+ >>> SolidFill("8")
26
26
  <SolidFill box widget '8'>
27
27
  """
28
28
  super().__init__()
@@ -39,9 +39,9 @@ class SolidFill(Widget):
39
39
  """
40
40
  Render the Fill as a canvas and return it.
41
41
 
42
- >>> SolidFill().render((4,2)).text # ... = b in Python 3
42
+ >>> SolidFill().render((4, 2)).text # ... = b in Python 3
43
43
  [...' ', ...' ']
44
- >>> SolidFill('#').render((5,3)).text
44
+ >>> SolidFill("#").render((5, 3)).text
45
45
  [...'#####', ...'#####', ...'#####']
46
46
  """
47
47
  maxcol, maxrow = size
urwid/widget/text.py CHANGED
@@ -61,9 +61,9 @@ class Text(Widget):
61
61
  :param layout: defaults to a shared :class:`StandardTextLayout` instance
62
62
  :type layout: text layout instance
63
63
 
64
- >>> Text(u"Hello")
64
+ >>> Text("Hello")
65
65
  <Text fixed/flow widget 'Hello'>
66
- >>> t = Text(('bold', u"stuff"), 'right', 'any')
66
+ >>> t = Text(("bold", "stuff"), "right", "any")
67
67
  >>> t
68
68
  <Text fixed/flow widget 'stuff' align='right' wrap='any'>
69
69
  >>> print(t.text)
@@ -73,6 +73,7 @@ class Text(Widget):
73
73
  """
74
74
  super().__init__()
75
75
  self._cache_maxcol: int | None = None
76
+ self._layout: text_layout.TextLayout = layout or text_layout.default_layout
76
77
  self.set_text(markup)
77
78
  self.set_layout(align, wrap, layout)
78
79
 
@@ -108,13 +109,13 @@ class Text(Widget):
108
109
  :param markup: see :class:`Text` for description.
109
110
  :type markup: text markup
110
111
 
111
- >>> t = Text(u"foo")
112
+ >>> t = Text("foo")
112
113
  >>> print(t.text)
113
114
  foo
114
- >>> t.set_text(u"bar")
115
+ >>> t.set_text("bar")
115
116
  >>> print(t.text)
116
117
  bar
117
- >>> t.text = u"baz" # not supported because text stores text but set_text() takes markup
118
+ >>> t.text = "baz" # not supported because text stores text but set_text() takes markup
118
119
  Traceback (most recent call last):
119
120
  AttributeError: can't set attribute
120
121
  """
@@ -132,11 +133,11 @@ class Text(Widget):
132
133
  run length encoded display attributes for *text*, eg.
133
134
  ``[('attr1', 10), ('attr2', 5)]``
134
135
 
135
- >>> Text(u"Hello").get_text() # ... = u in Python 2
136
+ >>> Text("Hello").get_text() # ... = u in Python 2
136
137
  (...'Hello', [])
137
- >>> Text(('bright', u"Headline")).get_text()
138
+ >>> Text(("bright", "Headline")).get_text()
138
139
  (...'Headline', [('bright', 8)])
139
- >>> Text([('a', u"one"), u"two", ('b', u"three")]).get_text()
140
+ >>> Text([("a", "one"), "two", ("b", "three")]).get_text()
140
141
  (...'onetwothree', [('a', 3), (None, 3), ('b', 5)])
141
142
  """
142
143
  return self._text, self._attrib
@@ -165,16 +166,16 @@ class Text(Widget):
165
166
  :param mode: typically ``'left'``, ``'center'`` or ``'right'``
166
167
  :type mode: text alignment mode
167
168
 
168
- >>> t = Text(u"word")
169
- >>> t.set_align_mode('right')
169
+ >>> t = Text("word")
170
+ >>> t.set_align_mode("right")
170
171
  >>> t.align
171
172
  'right'
172
- >>> t.render((10,)).text # ... = b in Python 3
173
+ >>> t.render((10,)).text # ... = b in Python 3
173
174
  [...' word']
174
- >>> t.align = 'center'
175
+ >>> t.align = "center"
175
176
  >>> t.render((10,)).text
176
177
  [...' word ']
177
- >>> t.align = 'somewhere'
178
+ >>> t.align = "somewhere"
178
179
  Traceback (most recent call last):
179
180
  TextError: Alignment mode 'somewhere' not supported.
180
181
  """
@@ -191,18 +192,18 @@ class Text(Widget):
191
192
  :param mode: typically ``'space'``, ``'any'``, ``'clip'`` or ``'ellipsis'``
192
193
  :type mode: text wrapping mode
193
194
 
194
- >>> t = Text(u"some words")
195
- >>> t.render((6,)).text # ... = b in Python 3
195
+ >>> t = Text("some words")
196
+ >>> t.render((6,)).text # ... = b in Python 3
196
197
  [...'some ', ...'words ']
197
- >>> t.set_wrap_mode('clip')
198
+ >>> t.set_wrap_mode("clip")
198
199
  >>> t.wrap
199
200
  'clip'
200
201
  >>> t.render((6,)).text
201
202
  [...'some w']
202
- >>> t.wrap = 'any' # Urwid 0.9.9 or later
203
+ >>> t.wrap = "any" # Urwid 0.9.9 or later
203
204
  >>> t.render((6,)).text
204
205
  [...'some w', ...'ords ']
205
- >>> t.wrap = 'somehow'
206
+ >>> t.wrap = "somehow"
206
207
  Traceback (most recent call last):
207
208
  TextError: Wrap mode 'somehow' not supported.
208
209
  """
@@ -227,8 +228,8 @@ class Text(Widget):
227
228
  :param layout: defaults to a shared :class:`StandardTextLayout` instance
228
229
  :type layout: text layout instance
229
230
 
230
- >>> t = Text(u"hi")
231
- >>> t.set_layout('right', 'clip')
231
+ >>> t = Text("hi")
232
+ >>> t.set_layout("right", "clip")
232
233
  >>> t
233
234
  <Text fixed/flow widget 'hi' align='right' wrap='clip'>
234
235
  """
@@ -255,9 +256,9 @@ class Text(Widget):
255
256
 
256
257
  See :meth:`Widget.render` for parameter details.
257
258
 
258
- >>> Text(u"important things").render((18,)).text
259
+ >>> Text("important things").render((18,)).text
259
260
  [b'important things ']
260
- >>> Text(u"important things").render((11,)).text
261
+ >>> Text("important things").render((11,)).text
261
262
  [b'important ', b'things ']
262
263
  >>> Text("demo text").render(()).text
263
264
  [b'demo text']
@@ -277,9 +278,9 @@ class Text(Widget):
277
278
 
278
279
  See :meth:`Widget.rows` for parameter details.
279
280
 
280
- >>> Text(u"important things").rows((18,))
281
+ >>> Text("important things").rows((18,))
281
282
  1
282
- >>> Text(u"important things").rows((11,))
283
+ >>> Text("important things").rows((11,))
283
284
  2
284
285
  """
285
286
  (maxcol,) = size
@@ -332,14 +333,25 @@ class Text(Widget):
332
333
  :param focus: widget is focused on
333
334
  :type focus: bool
334
335
 
335
- >>> Text(u"important things").pack()
336
+ >>> Text("important things").pack()
336
337
  (16, 1)
337
- >>> Text(u"important things").pack((15,))
338
+ >>> Text("important things").pack((15,))
338
339
  (9, 2)
339
- >>> Text(u"important things").pack((8,))
340
+ >>> Text("important things").pack((8,))
340
341
  (8, 2)
341
- >>> Text(u"important things").pack(())
342
+ >>> Text("important things").pack(())
342
343
  (16, 1)
344
+ >>> not_common_separated_text = "Line feed\\nLine Separator\\u2028Paragraph Separator\\u2029"
345
+ >>> # \u2028 (Line Separator) and \u2029 (Paragraph Separator) are not splitted by StandardTextLayout
346
+ >>> not_common_separated = Text(not_common_separated_text)
347
+
348
+ >>> not_common_separated.pack()
349
+ (33, 2)
350
+ >>> not_common_separated_canv = not_common_separated.render(())
351
+ >>> not_common_separated_canv.cols()
352
+ 33
353
+ >>> not_common_separated_canv.rows()
354
+ 2
343
355
  """
344
356
  text, attr = self.get_text()
345
357
 
@@ -356,8 +368,10 @@ class Text(Widget):
356
368
  if isinstance(text, bytes):
357
369
  text = text.decode(get_encoding())
358
370
 
371
+ split_text = text.split("\n")
372
+
359
373
  return (
360
- max(calc_width(line, 0, len(line)) for line in text.splitlines(keepends=False)),
361
- text.count("\n") + 1,
374
+ max(calc_width(line, 0, len(line)) for line in split_text),
375
+ len(split_text),
362
376
  )
363
377
  return 0, 1
urwid/widget/treetools.py CHANGED
@@ -27,7 +27,6 @@ Features:
27
27
  - custom list walker for displaying widgets in a tree fashion
28
28
  """
29
29
 
30
-
31
30
  from __future__ import annotations
32
31
 
33
32
  import typing
@@ -107,8 +106,8 @@ class TreeWidget(WidgetWrap[Padding[typing.Union[Text, Columns]]]):
107
106
  def next_inorder(self) -> TreeWidget | None:
108
107
  """Return the next TreeWidget depth first from this one."""
109
108
  # first check if there's a child widget
110
- first_child = self.first_child()
111
- if first_child is not None:
109
+
110
+ if (first_child := self.first_child()) is not None:
112
111
  return first_child
113
112
 
114
113
  # now we need to hunt for the next sibling
@@ -131,24 +130,21 @@ class TreeWidget(WidgetWrap[Padding[typing.Union[Text, Columns]]]):
131
130
  def prev_inorder(self) -> TreeWidget | None:
132
131
  """Return the previous TreeWidget depth first from this one."""
133
132
  this_node = self._node
134
- prev_node = this_node.prev_sibling()
135
- if prev_node is not None:
133
+
134
+ if (prev_node := this_node.prev_sibling()) is not None:
136
135
  # we need to find the last child of the previous widget if its
137
136
  # expanded
138
137
  prev_widget = prev_node.get_widget()
139
- last_child = prev_widget.last_child()
140
- if last_child is None:
141
- return prev_widget
138
+ if (last_child := prev_widget.last_child()) is not None:
139
+ return last_child
142
140
 
143
- return last_child
141
+ return prev_widget
144
142
 
145
143
  # need to hunt for the parent
146
- depth = this_node.get_depth()
147
- if prev_node is None and depth == 0:
144
+ if this_node.get_depth() == 0:
148
145
  return None
149
- if prev_node is None:
150
- prev_node = this_node.get_parent()
151
- return prev_node.get_widget()
146
+
147
+ return this_node.get_parent().get_widget()
152
148
 
153
149
  def keypress(
154
150
  self,
@@ -212,11 +208,10 @@ class TreeWidget(WidgetWrap[Padding[typing.Union[Text, Columns]]]):
212
208
  else:
213
209
  return None
214
210
  # recursively search down for the last descendant
215
- last_descendant = last_child.last_child()
216
- if last_descendant is None:
217
- return last_child
211
+ if (last_descendant := last_child.last_child()) is not None:
212
+ return last_descendant
218
213
 
219
- return last_descendant
214
+ return last_child
220
215
 
221
216
 
222
217
  class TreeNode:
@@ -372,32 +367,18 @@ class ParentNode(TreeNode):
372
367
 
373
368
  def next_child(self, key: Hashable) -> TreeNode | None:
374
369
  """Return the next child node in index order from the given key."""
375
-
376
- index = self.get_child_index(key)
377
- # the given node may have just been deleted
378
- if index is None:
379
- return None
380
- index += 1
381
-
382
- child_keys = self.get_child_keys()
383
- if index < len(child_keys):
370
+ if (index := self.get_child_index(key)) is not None and (index + 1) < len(child_keys := self.get_child_keys()):
384
371
  # get the next item at same level
385
- return self.get_child_node(child_keys[index])
372
+ return self.get_child_node(child_keys[index + 1])
386
373
 
374
+ # the given node may have just been deleted
387
375
  return None
388
376
 
389
377
  def prev_child(self, key: Hashable) -> TreeNode | None:
390
378
  """Return the previous child node in index order from the given key."""
391
- index = self.get_child_index(key)
392
- if index is None:
393
- return None
394
-
395
- child_keys = self.get_child_keys()
396
- index -= 1
397
-
398
- if index >= 0:
399
- # get the previous item at same level
400
- return self.get_child_node(child_keys[index])
379
+ if (index := self.get_child_index(key)) is not None and index >= 1:
380
+ # get the previous item at the same level
381
+ return self.get_child_node(self.get_child_keys()[index - 1])
401
382
 
402
383
  return None
403
384
 
@@ -435,18 +416,16 @@ class TreeWalker(ListWalker):
435
416
 
436
417
  # pylint: disable=arguments-renamed # its bad, but we should not change API
437
418
  def get_next(self, start_from) -> tuple[TreeWidget, TreeNode] | tuple[None, None]:
438
- target = start_from.get_widget().next_inorder()
439
- if target is None:
440
- return None, None
419
+ if (target := start_from.get_widget().next_inorder()) is not None:
420
+ return target, target.get_node()
441
421
 
442
- return target, target.get_node()
422
+ return None, None
443
423
 
444
424
  def get_prev(self, start_from) -> tuple[TreeWidget, TreeNode] | tuple[None, None]:
445
- target = start_from.get_widget().prev_inorder()
446
- if target is None:
447
- return None, None
425
+ if (target := start_from.get_widget().prev_inorder()) is not None:
426
+ return target, target.get_node()
448
427
 
449
- return target, target.get_node()
428
+ return None, None
450
429
 
451
430
  # pylint: enable=arguments-renamed
452
431
 
@@ -525,8 +504,8 @@ class TreeListBox(ListBox):
525
504
 
526
505
  maxrow, _maxcol = size
527
506
  _widget, pos = self.body.get_focus()
528
- lastwidget = pos.get_root().get_widget().last_child()
529
- if lastwidget:
507
+
508
+ if lastwidget := pos.get_root().get_widget().last_child():
530
509
  lastnode = lastwidget.get_node()
531
510
 
532
511
  self.change_focus(size, lastnode, maxrow - 1)
urwid/widget/widget.py CHANGED
@@ -41,7 +41,7 @@ WrappedWidget = typing.TypeVar("WrappedWidget")
41
41
  LOGGER = logging.getLogger(__name__)
42
42
 
43
43
 
44
- class WidgetMeta(MetaSuper, signals.MetaSignals):
44
+ class WidgetMeta(signals.MetaSignals, MetaSuper):
45
45
  """
46
46
  Bases: :class:`MetaSuper`, :class:`MetaSignals`
47
47
 
@@ -105,8 +105,8 @@ def cache_widget_render(cls):
105
105
  @functools.wraps(fn)
106
106
  def cached_render(self, size, focus=False):
107
107
  focus = focus and not ignore_focus
108
- canv = CanvasCache.fetch(self, cls, size, focus)
109
- if canv:
108
+
109
+ if canv := CanvasCache.fetch(self, cls, size, focus):
110
110
  return canv
111
111
 
112
112
  canv = fn(self, size, focus=focus)
@@ -174,8 +174,8 @@ def cache_widget_rows(cls):
174
174
  @functools.wraps(fn)
175
175
  def cached_rows(self, size: tuple[int], focus: bool = False) -> int:
176
176
  focus = focus and not ignore_focus
177
- canv = CanvasCache.fetch(self, cls, size, focus)
178
- if canv:
177
+
178
+ if canv := CanvasCache.fetch(self, cls, size, focus):
179
179
  return canv.rows()
180
180
 
181
181
  return fn(self, size, focus)
@@ -565,133 +565,16 @@ class Widget(metaclass=WidgetMeta):
565
565
  raise NotImplementedError
566
566
 
567
567
 
568
- class FlowWidget(Widget):
569
- """
570
- Deprecated. Inherit from Widget and add:
571
-
572
- _sizing = frozenset(['flow'])
573
-
574
- at the top of your class definition instead.
575
-
576
- Base class of widgets that determine their rows from the number of
577
- columns available.
578
- """
579
-
580
- _sizing = frozenset([Sizing.FLOW])
581
-
582
- def __init__(self, *args, **kwargs):
583
- warnings.warn(
584
- """
585
- FlowWidget is deprecated. Inherit from Widget and add:
586
-
587
- _sizing = frozenset(['flow'])
588
-
589
- at the top of your class definition instead.""",
590
- DeprecationWarning,
591
- stacklevel=3,
592
- )
593
- super().__init__()
594
-
595
- def rows(self, size: tuple[int], focus: bool = False) -> int:
596
- """
597
- All flow widgets must implement this function.
598
- """
599
- raise NotImplementedError()
600
-
601
- def render(self, size: tuple[int], focus: bool = False) -> Canvas: # type: ignore[override]
602
- """
603
- All widgets must implement this function.
604
- """
605
- raise NotImplementedError()
606
-
607
-
608
- class BoxWidget(Widget):
609
- """
610
- Deprecated. Inherit from Widget and add:
611
-
612
- _sizing = frozenset(['box'])
613
- _selectable = True
614
-
615
- at the top of your class definition instead.
616
-
617
- Base class of width and height constrained widgets such as
618
- the top level widget attached to the display object
619
- """
620
-
621
- _selectable = True
622
- _sizing = frozenset([Sizing.BOX])
623
-
624
- def __init__(self, *args, **kwargs):
625
- warnings.warn(
626
- """
627
- BoxWidget is deprecated. Inherit from Widget and add:
628
-
629
- _sizing = frozenset(['box'])
630
- _selectable = True
631
-
632
- at the top of your class definition instead.""",
633
- DeprecationWarning,
634
- stacklevel=3,
635
- )
636
- super().__init__()
637
-
638
- def render(self, size: tuple[int, int], focus: bool = False) -> Canvas: # type: ignore[override]
639
- """
640
- All widgets must implement this function.
641
- """
642
- raise NotImplementedError()
643
-
644
-
645
568
  def fixed_size(size: tuple[()]) -> None:
646
569
  """
647
570
  raise ValueError if size != ().
648
571
 
649
572
  Used by FixedWidgets to test size parameter.
650
573
  """
651
- if size != ():
574
+ if size:
652
575
  raise ValueError(f"FixedWidget takes only () for size.passed: {size!r}")
653
576
 
654
577
 
655
- class FixedWidget(Widget):
656
- """
657
- Deprecated. Inherit from Widget and add:
658
-
659
- _sizing = frozenset(['fixed'])
660
-
661
- at the top of your class definition instead.
662
-
663
- Base class of widgets that know their width and height and
664
- cannot be resized
665
- """
666
-
667
- _sizing = frozenset([Sizing.FIXED])
668
-
669
- def __init__(self, *args, **kwargs):
670
- warnings.warn(
671
- """
672
- FixedWidget is deprecated. Inherit from Widget and add:
673
-
674
- _sizing = frozenset(['fixed'])
675
-
676
- at the top of your class definition instead.""",
677
- DeprecationWarning,
678
- stacklevel=3,
679
- )
680
- super().__init__()
681
-
682
- def render(self, size: tuple[()], focus: bool = False) -> Canvas: # type: ignore[override]
683
- """
684
- All widgets must implement this function.
685
- """
686
- raise NotImplementedError()
687
-
688
- def pack(self, size: tuple[()] = (), focus: bool = False) -> tuple[int, int]: # type: ignore[override]
689
- """
690
- All fixed widgets must implement this function.
691
- """
692
- raise NotImplementedError()
693
-
694
-
695
578
  def delegate_to_widget_mixin(attribute_name: str) -> type[Widget]:
696
579
  """
697
580
  Return a mixin class that delegates all standard widget methods
@@ -795,12 +678,12 @@ class WidgetWrap(delegate_to_widget_mixin("_wrapped_widget"), typing.Generic[Wra
795
678
  only by subclasses.
796
679
 
797
680
  >>> size = (10,)
798
- >>> ww = WidgetWrap(Edit("hello? ","hi"))
799
- >>> ww.render(size).text # ... = b in Python 3
681
+ >>> ww = WidgetWrap(Edit("hello? ", "hi"))
682
+ >>> ww.render(size).text # ... = b in Python 3
800
683
  [...'hello? hi ']
801
684
  >>> ww.selectable()
802
685
  True
803
- >>> ww._w = Text("goodbye") # calls _set_w()
686
+ >>> ww._w = Text("goodbye") # calls _set_w()
804
687
  >>> ww.render(size).text
805
688
  [...'goodbye ']
806
689
  >>> ww.selectable()
@@ -815,19 +698,19 @@ class WidgetWrap(delegate_to_widget_mixin("_wrapped_widget"), typing.Generic[Wra
815
698
  only by subclasses.
816
699
  >>> from urwid import Edit, Text
817
700
  >>> size = (10,)
818
- >>> ww = WidgetWrap(Edit("hello? ","hi"))
819
- >>> ww.render(size).text # ... = b in Python 3
701
+ >>> ww = WidgetWrap(Edit("hello? ", "hi"))
702
+ >>> ww.render(size).text # ... = b in Python 3
820
703
  [...'hello? hi ']
821
704
  >>> ww.selectable()
822
705
  True
823
- >>> ww._w = Text("goodbye") # calls _set_w()
706
+ >>> ww._w = Text("goodbye") # calls _set_w()
824
707
  >>> ww.render(size).text
825
708
  [...'goodbye ']
826
709
  >>> ww.selectable()
827
710
  False
828
711
  """
829
712
  warnings.warn(
830
- "_set_w is deprecated. Please use 'WidgetWrap._w' property directly",
713
+ "_set_w is deprecated. Please use 'WidgetWrap._w' property directly. API will be removed in version 5.0.",
831
714
  DeprecationWarning,
832
715
  stacklevel=2,
833
716
  )