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.

Files changed (46) hide show
  1. urwid/__init__.py +46 -33
  2. urwid/canvas.py +4 -6
  3. urwid/display/_posix_raw_display.py +3 -4
  4. urwid/display/_raw_display_base.py +4 -5
  5. urwid/display/_win32_raw_display.py +1 -2
  6. urwid/display/curses.py +1 -1
  7. urwid/display/html_fragment.py +1 -1
  8. urwid/event_loop/__init__.py +5 -5
  9. urwid/event_loop/main_loop.py +26 -21
  10. urwid/event_loop/zmq_loop.py +2 -2
  11. urwid/numedit.py +5 -1
  12. urwid/signals.py +1 -1
  13. urwid/str_util.py +1 -2
  14. urwid/text_layout.py +8 -4
  15. urwid/version.py +2 -2
  16. urwid/vterm.py +10 -10
  17. urwid/widget/__init__.py +19 -1
  18. urwid/widget/bar_graph.py +8 -4
  19. urwid/widget/big_text.py +14 -4
  20. urwid/widget/box_adapter.py +11 -3
  21. urwid/widget/columns.py +104 -34
  22. urwid/widget/constants.py +43 -76
  23. urwid/widget/container.py +2 -2
  24. urwid/widget/divider.py +5 -1
  25. urwid/widget/edit.py +20 -14
  26. urwid/widget/filler.py +11 -4
  27. urwid/widget/frame.py +91 -28
  28. urwid/widget/grid_flow.py +66 -15
  29. urwid/{listbox.py → widget/listbox.py} +27 -23
  30. urwid/{monitored_list.py → widget/monitored_list.py} +2 -0
  31. urwid/widget/overlay.py +99 -14
  32. urwid/widget/padding.py +16 -3
  33. urwid/widget/pile.py +57 -13
  34. urwid/widget/progress_bar.py +5 -1
  35. urwid/widget/scrollable.py +28 -9
  36. urwid/widget/solid_fill.py +5 -1
  37. urwid/widget/text.py +11 -3
  38. urwid/{treetools.py → widget/treetools.py} +35 -18
  39. urwid/widget/widget.py +17 -9
  40. urwid/widget/wimp.py +8 -4
  41. {urwid-2.6.4.dist-info → urwid-2.6.6.dist-info}/METADATA +1 -1
  42. urwid-2.6.6.dist-info/RECORD +74 -0
  43. urwid-2.6.4.dist-info/RECORD +0 -74
  44. {urwid-2.6.4.dist-info → urwid-2.6.6.dist-info}/COPYING +0 -0
  45. {urwid-2.6.4.dist-info → urwid-2.6.6.dist-info}/WHEEL +0 -0
  46. {urwid-2.6.4.dist-info → urwid-2.6.6.dist-info}/top_level.txt +0 -0
@@ -90,7 +90,11 @@ class BoxAdapter(WidgetDecoration[WrappedWidget]):
90
90
  return None
91
91
  return self._original_widget.get_pref_col((maxcol, self.height))
92
92
 
93
- def keypress(self, size: tuple[int], key: str) -> str | None:
93
+ def keypress(
94
+ self,
95
+ size: tuple[int], # type: ignore[override]
96
+ key: str,
97
+ ) -> str | None:
94
98
  (maxcol,) = size
95
99
  return self._original_widget.keypress((maxcol, self.height), key)
96
100
 
@@ -102,7 +106,7 @@ class BoxAdapter(WidgetDecoration[WrappedWidget]):
102
106
 
103
107
  def mouse_event(
104
108
  self,
105
- size: tuple[int],
109
+ size: tuple[int], # type: ignore[override]
106
110
  event: str,
107
111
  button: int,
108
112
  col: int,
@@ -114,7 +118,11 @@ class BoxAdapter(WidgetDecoration[WrappedWidget]):
114
118
  return False
115
119
  return self._original_widget.mouse_event((maxcol, self.height), event, button, col, row, focus)
116
120
 
117
- def render(self, size: tuple[int], focus: bool = False) -> CompositeCanvas:
121
+ def render(
122
+ self,
123
+ size: tuple[int], # type: ignore[override]
124
+ focus: bool = False,
125
+ ) -> CompositeCanvas:
118
126
  (maxcol,) = size
119
127
  canv = CompositeCanvas(self._original_widget.render((maxcol, self.height), focus))
120
128
  return canv
urwid/widget/columns.py CHANGED
@@ -7,15 +7,16 @@ from itertools import chain, repeat
7
7
  import urwid
8
8
  from urwid.canvas import Canvas, CanvasJoin, CompositeCanvas, SolidCanvas
9
9
  from urwid.command_map import Command
10
- from urwid.monitored_list import MonitoredFocusList, MonitoredList
10
+ from urwid.split_repr import remove_defaults
11
11
  from urwid.util import is_mouse_press
12
12
 
13
13
  from .constants import Align, Sizing, WHSettings
14
14
  from .container import WidgetContainerListContentsMixin, WidgetContainerMixin, _ContainerElementSizingFlag
15
+ from .monitored_list import MonitoredFocusList, MonitoredList
15
16
  from .widget import Widget, WidgetError, WidgetWarning
16
17
 
17
18
  if typing.TYPE_CHECKING:
18
- from collections.abc import Iterable, Sequence
19
+ from collections.abc import Iterable, Iterator, Sequence
19
20
 
20
21
  from typing_extensions import Literal
21
22
 
@@ -66,40 +67,47 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
66
67
 
67
68
  # BOX-only widget
68
69
  >>> Columns((SolidFill("#"),))
69
- <Columns box widget>
70
+ <Columns box widget (1 item)>
70
71
 
71
72
  # BOX-only widget with "get height from max"
72
73
  >>> Columns((SolidFill("#"),), box_columns=(0,))
73
- <Columns box widget>
74
+ <Columns box widget (1 item)>
74
75
 
75
76
  # FLOW-only
76
77
  >>> Columns((Edit(),))
77
- <Columns selectable flow widget>
78
+ <Columns selectable flow widget (1 item)>
78
79
 
79
80
  # FLOW allowed by "box_columns"
80
81
  >>> Columns((Edit(), SolidFill("#")), box_columns=(1,))
81
- <Columns selectable flow widget>
82
+ <Columns selectable flow widget (2 items) focus_column=0>
82
83
 
83
84
  # FLOW/FIXED
84
85
  >>> Columns((Text("T"),))
85
- <Columns fixed/flow widget>
86
+ <Columns fixed/flow widget (1 item)>
86
87
 
87
88
  # GIVEN BOX only -> BOX only
88
89
  >>> Columns(((5, SolidFill("#")),), box_columns=(0,))
89
- <Columns box widget>
90
+ <Columns box widget (1 item)>
90
91
 
91
92
  # No FLOW - BOX only
92
93
  >>> Columns(((5, SolidFill("#")), SolidFill("*")), box_columns=(0, 1))
93
- <Columns box widget>
94
+ <Columns box widget (2 items) focus_column=0>
94
95
 
95
- # FIXED only -> FIXED
96
+ # FIXED only -> FIXED (and FLOW via drop/expand)
96
97
  >>> Columns(((WHSettings.PACK, BigText("1", font)),))
97
- <Columns fixed widget>
98
+ <Columns fixed/flow widget (1 item)>
98
99
 
99
100
  # Invalid sizing combination -> use fallback settings (and produce warning)
100
101
  >>> Columns(((WHSettings.PACK, SolidFill("#")),))
101
- <Columns box/flow widget>
102
+ <Columns box/flow widget (1 item)>
103
+
104
+ # Special case: empty columns widget sizing is impossible to calculate
105
+ >>> Columns(())
106
+ <Columns box/flow widget ()>
102
107
  """
108
+ if not self.contents:
109
+ return frozenset((urwid.BOX, urwid.FLOW))
110
+
103
111
  strict_box = False
104
112
  has_flow = False
105
113
 
@@ -176,6 +184,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
176
184
  supported.add(Sizing.FLOW)
177
185
 
178
186
  if has_fixed and not block_fixed:
187
+ supported.add(Sizing.FLOW)
179
188
  supported.add(Sizing.FIXED)
180
189
 
181
190
  if not supported:
@@ -238,10 +247,12 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
238
247
  self._cache_column_widths: list[int] = []
239
248
  super().__init__()
240
249
  self._contents: MonitoredFocusList[
241
- Widget,
242
- tuple[Literal[WHSettings.PACK], None, bool]
243
- | tuple[Literal[WHSettings.GIVEN], int, bool]
244
- | tuple[Literal[WHSettings.WEIGHT], int | float, bool],
250
+ tuple[
251
+ Widget,
252
+ tuple[Literal[WHSettings.PACK], None, bool]
253
+ | tuple[Literal[WHSettings.GIVEN], int, bool]
254
+ | tuple[Literal[WHSettings.WEIGHT], int | float, bool],
255
+ ],
245
256
  ] = MonitoredFocusList()
246
257
  self._contents.set_modified_callback(self._contents_modified)
247
258
  self._contents.set_focus_changed_callback(lambda f: self._invalidate())
@@ -278,6 +289,53 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
278
289
  self.min_width = min_width
279
290
  self._cache_maxcol = None
280
291
 
292
+ def _repr_words(self) -> list[str]:
293
+ if len(self.contents) > 1:
294
+ contents_string = f"({len(self.contents)} items)"
295
+ elif self.contents:
296
+ contents_string = "(1 item)"
297
+ else:
298
+ contents_string = "()"
299
+ return [*super()._repr_words(), contents_string]
300
+
301
+ def _repr_attrs(self) -> dict[str, typing.Any]:
302
+ attrs = {
303
+ **super()._repr_attrs(),
304
+ "dividechars": self.dividechars,
305
+ "focus_column": self.focus_position if len(self._contents) > 1 else None,
306
+ "min_width": self.min_width,
307
+ }
308
+ return remove_defaults(attrs, Columns.__init__)
309
+
310
+ def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
311
+ widget_list: list[
312
+ Widget
313
+ | tuple[Literal[WHSettings.PACK] | int, Widget]
314
+ | tuple[Literal[WHSettings.WEIGHT], int | float, Widget]
315
+ ] = []
316
+ box_columns: list[int] = []
317
+ for idx, (w_instance, (sizing, amount, is_box)) in enumerate(self._contents):
318
+ if sizing == WHSettings.GIVEN:
319
+ widget_list.append((amount, w_instance))
320
+ elif sizing == WHSettings.PACK:
321
+ widget_list.append((WHSettings.PACK, w_instance))
322
+ elif amount == 1:
323
+ widget_list.append(w_instance)
324
+ else:
325
+ widget_list.append((WHSettings.WEIGHT, amount, w_instance))
326
+
327
+ if is_box:
328
+ box_columns.append(idx)
329
+
330
+ yield "widget_list", widget_list
331
+ yield "dividechars", self.dividechars
332
+ yield "focus_column", self.focus_position if self._contents else None
333
+ yield "min_width", self.min_width
334
+ yield "box_columns", box_columns
335
+
336
+ def __len__(self) -> int:
337
+ return len(self._contents)
338
+
281
339
  def _contents_modified(self) -> None:
282
340
  """
283
341
  Recalculate whether this widget should be selectable whenever the
@@ -342,7 +400,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
342
400
  chain(self.contents, repeat((None, (WHSettings.WEIGHT, 1, False)))),
343
401
  )
344
402
  ]
345
- if focus_position < len(widgets):
403
+ if focus_position < len(widgets): # pylint: disable=consider-using-max-builtin # pylint bug
346
404
  self.focus_position = focus_position
347
405
 
348
406
  @property
@@ -383,7 +441,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
383
441
  (w, ({Sizing.FIXED: WHSettings.GIVEN, Sizing.FLOW: WHSettings.PACK}.get(new_t, new_t), new_n, b))
384
442
  for ((new_t, new_n), (w, (t, n, b))) in zip(column_types, self.contents)
385
443
  ]
386
- if focus_position < len(column_types):
444
+ if focus_position < len(column_types): # pylint: disable=consider-using-max-builtin # pylint bug
387
445
  self.focus_position = focus_position
388
446
 
389
447
  @property
@@ -442,10 +500,12 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
442
500
  def contents(
443
501
  self,
444
502
  ) -> MonitoredFocusList[
445
- Widget,
446
- tuple[Literal[WHSettings.PACK], None, bool]
447
- | tuple[Literal[WHSettings.GIVEN], int, bool]
448
- | tuple[Literal[WHSettings.WEIGHT], int | float, bool],
503
+ tuple[
504
+ Widget,
505
+ tuple[Literal[WHSettings.PACK], None, bool]
506
+ | tuple[Literal[WHSettings.GIVEN], int, bool]
507
+ | tuple[Literal[WHSettings.WEIGHT], int | float, bool],
508
+ ],
449
509
  ]:
450
510
  """
451
511
  The contents of this Columns as a list of `(widget, options)` tuples.
@@ -886,11 +946,17 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
886
946
  box.append(i)
887
947
 
888
948
  elif Sizing.FLOW in w_sizing:
889
- heights[i] = widget.rows((width,), focus and i == self.focus_position)
949
+ if width > 0:
950
+ heights[i] = widget.rows((width,), focus and i == self.focus_position)
951
+ else:
952
+ heights[i] = 0
890
953
  w_h_args[i] = (width,)
891
954
 
892
955
  elif size_kind == WHSettings.PACK:
893
- heights[i] = widget.pack((), focus and i == self.focus_position)[1]
956
+ if width > 0:
957
+ heights[i] = widget.pack((), focus and i == self.focus_position)[1]
958
+ else:
959
+ heights[i] = 0
894
960
  w_h_args[i] = ()
895
961
 
896
962
  else:
@@ -922,7 +988,11 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
922
988
  tuple(w_h_args[idx] for idx in range(len(w_h_args))),
923
989
  )
924
990
 
925
- def pack(self, size: tuple[()] | tuple[int] | tuple[int, int] = (), focus: bool = False) -> tuple[int, int]:
991
+ def pack(
992
+ self,
993
+ size: tuple[()] | tuple[int] | tuple[int, int] = (),
994
+ focus: bool = False,
995
+ ) -> tuple[int, int]:
926
996
  """Get packed sized for widget."""
927
997
  if size:
928
998
  return super().pack(size, focus)
@@ -1113,16 +1183,16 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
1113
1183
 
1114
1184
  see :meth:`Widget.rows` for details
1115
1185
  """
1116
- widths = self.column_widths(size, focus)
1186
+ _, heights, _ = self.get_column_sizes(size, focus)
1187
+ if heights:
1188
+ return max(1, *heights)
1189
+ return 1
1117
1190
 
1118
- rows = 1
1119
- for i, (mc, (w, (_t, _n, b))) in enumerate(zip(widths, self.contents)):
1120
- if b or mc <= 0:
1121
- continue
1122
- rows = max(rows, w.rows((mc,), focus=focus and self.focus_position == i))
1123
- return rows
1124
-
1125
- def keypress(self, size: tuple[()] | tuple[int] | tuple[int, int], key: str) -> str | None:
1191
+ def keypress(
1192
+ self,
1193
+ size: tuple[()] | tuple[int] | tuple[int, int],
1194
+ key: str,
1195
+ ) -> str | None:
1126
1196
  """
1127
1197
  Pass keypress to the focus column.
1128
1198
 
urwid/widget/constants.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import dataclasses
3
4
  import enum
4
5
  import typing
5
6
 
@@ -454,91 +455,51 @@ def simplify_height(
454
455
  return (WHSettings(height_type), height_amount)
455
456
 
456
457
 
457
- class _LightBoxSymbols(typing.NamedTuple):
458
- """Box symbols for drawing.
458
+ @dataclasses.dataclass(frozen=True)
459
+ class _BoxSymbols:
460
+ """Box symbols for drawing."""
459
461
 
460
- The Thin version includes extra symbols.
461
- Symbols are ordered as in Unicode except dashes.
462
- """
462
+ HORIZONTAL: str
463
+ VERTICAL: str
464
+ TOP_LEFT: str
465
+ TOP_RIGHT: str
466
+ BOTTOM_LEFT: str
467
+ BOTTOM_RIGHT: str
468
+ # Joints for tables making
469
+ LEFT_T: str
470
+ RIGHT_T: str
471
+ TOP_T: str
472
+ BOTTOM_T: str
473
+ CROSS: str
463
474
 
464
- # fmt: off
465
475
 
466
- HORIZONTAL: str = "─"
467
- VERTICAL: str = "│"
468
- TOP_LEFT: str = "┌"
469
- TOP_RIGHT: str = "┐"
470
- BOTTOM_LEFT: str = "└"
471
- BOTTOM_RIGHT: str = "┘"
472
- # Joints for tables making
473
- LEFT_T: str = "├"
474
- RIGHT_T: str = "┤"
475
- TOP_T: str = "┬"
476
- BOTTOM_T: str = "┴"
477
- CROSS: str = "┼"
478
- # Dashes
479
- HORIZONTAL_4_DASHES: str = "┈"
480
- HORIZONTAL_3_DASHES: str = "┄"
481
- HORIZONTAL_2_DASHES: str = "╌"
482
- VERTICAL_2_DASH: str = "╎"
483
- VERTICAL_3_DASH: str = "┆"
484
- VERTICAL_4_DASH: str = "┊"
485
- # Unique for light only
486
- TOP_LEFT_ROUNDED: str = "╭"
487
- TOP_RIGHT_ROUNDED: str = "╮"
488
- BOTTOM_RIGHT_ROUNDED: str = "╯"
489
- BOTTOM_LEFT_ROUNDED: str = "╰"
490
-
491
-
492
- class _HeavyBoxSymbols(typing.NamedTuple):
476
+ @dataclasses.dataclass(frozen=True)
477
+ class _BoxSymbolsWithDashes(_BoxSymbols):
493
478
  """Box symbols for drawing.
494
479
 
495
- The Heavy version includes extra symbols.
496
- Symbols are ordered as in Unicode except dashes.
480
+ Extra dashes symbols.
497
481
  """
498
482
 
499
- # fmt: off
483
+ HORIZONTAL_4_DASHES: str
484
+ HORIZONTAL_3_DASHES: str
485
+ HORIZONTAL_2_DASHES: str
486
+ VERTICAL_2_DASH: str
487
+ VERTICAL_3_DASH: str
488
+ VERTICAL_4_DASH: str
500
489
 
501
- HORIZONTAL: str = "━"
502
- VERTICAL: str = "┃"
503
- TOP_LEFT: str = "┏"
504
- TOP_RIGHT: str = "┓"
505
- BOTTOM_LEFT: str = "┗"
506
- BOTTOM_RIGHT: str = "┛"
507
- # Joints for tables making
508
- LEFT_T: str = "┣"
509
- RIGHT_T: str = "┫"
510
- TOP_T: str = "┳"
511
- BOTTOM_T: str = "┻"
512
- CROSS: str = "╋"
513
- # Dashes
514
- HORIZONTAL_4_DASHES: str = "┉"
515
- HORIZONTAL_3_DASHES: str = "┅"
516
- HORIZONTAL_2_DASHES: str = "╍"
517
- VERTICAL_2_DASH: str = "╏"
518
- VERTICAL_3_DASH: str = "┇"
519
- VERTICAL_4_DASH: str = "┋"
520
-
521
-
522
- class _DoubleBoxSymbols(typing.NamedTuple):
490
+
491
+ @dataclasses.dataclass(frozen=True)
492
+ class _LightBoxSymbols(_BoxSymbolsWithDashes):
523
493
  """Box symbols for drawing.
524
494
 
525
- Symbols are ordered as in Unicode.
495
+ The Thin version includes extra symbols.
496
+ Symbols are ordered as in Unicode except dashes.
526
497
  """
527
498
 
528
- # fmt: off
529
-
530
- HORIZONTAL: str = "═"
531
- VERTICAL: str = "║"
532
- TOP_LEFT: str = "╔"
533
- TOP_RIGHT: str = "╗"
534
- BOTTOM_LEFT: str = "╚"
535
- BOTTOM_RIGHT: str = "╝"
536
- # Joints for tables making
537
- LEFT_T: str = "╠"
538
- RIGHT_T: str = "╣"
539
- TOP_T: str = "╦"
540
- BOTTOM_T: str = "╩"
541
- CROSS: str = "╬"
499
+ TOP_LEFT_ROUNDED: str
500
+ TOP_RIGHT_ROUNDED: str
501
+ BOTTOM_LEFT_ROUNDED: str
502
+ BOTTOM_RIGHT_ROUNDED: str
542
503
 
543
504
 
544
505
  class _BoxSymbolsCollection(typing.NamedTuple):
@@ -551,9 +512,15 @@ class _BoxSymbolsCollection(typing.NamedTuple):
551
512
 
552
513
  # fmt: off
553
514
 
554
- LIGHT: _LightBoxSymbols = _LightBoxSymbols()
555
- HEAVY: _HeavyBoxSymbols = _HeavyBoxSymbols()
556
- DOUBLE: _DoubleBoxSymbols = _DoubleBoxSymbols()
515
+ LIGHT: _LightBoxSymbols = _LightBoxSymbols(
516
+ "─", "│", "┌", "┐", "└", "┘", "├", "┤", "┬", "┴", "┼", "┈", "┄", "╌", "╎", "┆", "┊", "╭", "╮", "╰", "╯"
517
+ )
518
+ HEAVY: _BoxSymbolsWithDashes = _BoxSymbolsWithDashes(
519
+ "━", "┃", "┏", "┓", "┗", "┛", "┣", "┫", "┳", "┻", "╋", "┉", "┅", "╍", "╏", "┇", "┋"
520
+ )
521
+ DOUBLE: _BoxSymbols = _BoxSymbols(
522
+ "═", "║", "╔", "╗", "╚", "╝", "╠", "╣", "╦", "╩", "╬"
523
+ )
557
524
 
558
525
 
559
526
  BOX_SYMBOLS = _BoxSymbolsCollection()
urwid/widget/container.py CHANGED
@@ -97,11 +97,11 @@ class WidgetContainerMixin:
97
97
 
98
98
  positions -- sequence of positions
99
99
  """
100
- w = self
100
+ w: Widget = self
101
101
  for p in positions:
102
102
  if p != w.focus_position:
103
103
  w.focus_position = p # modifies w.focus
104
- w = w.focus.base_widget
104
+ w = w.focus.base_widget # type: ignore[assignment]
105
105
 
106
106
  def get_focus_widgets(self) -> list[Widget]:
107
107
  """
urwid/widget/divider.py CHANGED
@@ -92,7 +92,11 @@ class Divider(Widget):
92
92
  (_maxcol,) = size
93
93
  return self.top + 1 + self.bottom
94
94
 
95
- def render(self, size: tuple[int], focus: bool = False) -> CompositeCanvas:
95
+ def render(
96
+ self,
97
+ size: tuple[int], # type: ignore[override]
98
+ focus: bool = False,
99
+ ) -> CompositeCanvas:
96
100
  """
97
101
  Render the divider as a canvas and return it.
98
102
 
urwid/widget/edit.py CHANGED
@@ -153,7 +153,7 @@ class Edit(Text):
153
153
 
154
154
  return self._caption + (self._mask * len(self._edit_text)), self._attrib
155
155
 
156
- def set_text(self, markup: tuple[str, list[tuple[Hashable, int]]]) -> None:
156
+ def set_text(self, markup: str | tuple[Hashable, str] | list[str | tuple[Hashable, str]]) -> None:
157
157
  """
158
158
  Not supported by Edit widget.
159
159
 
@@ -308,8 +308,8 @@ class Edit(Text):
308
308
  self._emit("change", text)
309
309
  old_text = self._edit_text
310
310
  self._edit_text = text
311
- if self.edit_pos > len(text):
312
- self.edit_pos = len(text)
311
+ self.edit_pos = min(self.edit_pos, len(text))
312
+
313
313
  self._emit("postchange", old_text)
314
314
  self._invalidate()
315
315
 
@@ -359,10 +359,7 @@ class Edit(Text):
359
359
  self.highlight = None
360
360
 
361
361
  def _normalize_to_caption(self, text: str | bytes) -> str | bytes:
362
- """
363
- Return text converted to the same type as self.caption
364
- (bytes or unicode)
365
- """
362
+ """Return text converted to the same type as self.caption (bytes or unicode)"""
366
363
  tu = isinstance(text, str)
367
364
  cu = isinstance(self._caption, str)
368
365
  if tu == cu:
@@ -400,7 +397,11 @@ class Edit(Text):
400
397
  result_pos += len(text)
401
398
  return (result_text, result_pos)
402
399
 
403
- def keypress(self, size: tuple[int], key: str) -> str | None:
400
+ def keypress(
401
+ self,
402
+ size: tuple[int], # type: ignore[override]
403
+ key: str,
404
+ ) -> str | None:
404
405
  """
405
406
  Handle editing keystrokes, return others.
406
407
 
@@ -421,9 +422,6 @@ class Edit(Text):
421
422
  pos = self.edit_pos
422
423
  if self.valid_char(key):
423
424
  if isinstance(key, str) and not isinstance(self._caption, str):
424
- # screen is sending us unicode input, must be using utf-8
425
- # encoding because that's all we support, so convert it
426
- # to bytes to match our caption's type
427
425
  key = key.encode("utf-8")
428
426
  self.insert_text(key)
429
427
  return None
@@ -546,7 +544,7 @@ class Edit(Text):
546
544
 
547
545
  def mouse_event(
548
546
  self,
549
- size: tuple[int],
547
+ size: tuple[int], # type: ignore[override]
550
548
  event: str,
551
549
  button: int,
552
550
  col: int,
@@ -581,7 +579,11 @@ class Edit(Text):
581
579
  self.highlight = None
582
580
  return True
583
581
 
584
- def render(self, size: tuple[int], focus: bool = False) -> TextCanvas | CompositeCanvas:
582
+ def render(
583
+ self,
584
+ size: tuple[int], # type: ignore[override]
585
+ focus: bool = False,
586
+ ) -> TextCanvas | CompositeCanvas:
585
587
  """
586
588
  Render edit widget and return canvas. Include cursor when in
587
589
  focus.
@@ -679,7 +681,11 @@ class IntEdit(Edit):
679
681
  val = ""
680
682
  super().__init__(caption, val)
681
683
 
682
- def keypress(self, size: tuple[int], key: str) -> str | None:
684
+ def keypress(
685
+ self,
686
+ size: tuple[int], # type: ignore[override]
687
+ key: str,
688
+ ) -> str | None:
683
689
  """
684
690
  Handle editing keystrokes. Remove leading zeros.
685
691
 
urwid/widget/filler.py CHANGED
@@ -22,7 +22,6 @@ from .widget_decoration import WidgetDecoration, WidgetError
22
22
  if typing.TYPE_CHECKING:
23
23
  from typing_extensions import Literal
24
24
 
25
-
26
25
  WrappedWidget = typing.TypeVar("WrappedWidget")
27
26
 
28
27
 
@@ -231,7 +230,11 @@ class Filler(WidgetDecoration[WrappedWidget]):
231
230
  self.bottom,
232
231
  )
233
232
 
234
- def render(self, size: tuple[int, int] | tuple[int], focus: bool = False) -> CompositeCanvas:
233
+ def render(
234
+ self,
235
+ size: tuple[int, int] | tuple[int], # type: ignore[override]
236
+ focus: bool = False,
237
+ ) -> CompositeCanvas:
235
238
  """Render self.original_widget with space above and/or below."""
236
239
  maxcol, maxrow = self.pack(size, focus)
237
240
  top, bottom = self.filler_values(size, focus)
@@ -252,7 +255,11 @@ class Filler(WidgetDecoration[WrappedWidget]):
252
255
  canv.pad_trim_top_bottom(top, bottom)
253
256
  return canv
254
257
 
255
- def keypress(self, size: tuple[int, int] | tuple[()], key: str) -> str | None:
258
+ def keypress(
259
+ self,
260
+ size: tuple[int, int] | tuple[()], # type: ignore[override]
261
+ key: str,
262
+ ) -> str | None:
256
263
  """Pass keypress to self.original_widget."""
257
264
  maxcol, maxrow = self.pack(size, True)
258
265
  if self.height_type == WHSettings.PACK:
@@ -309,7 +316,7 @@ class Filler(WidgetDecoration[WrappedWidget]):
309
316
 
310
317
  def mouse_event(
311
318
  self,
312
- size: tuple[int, int] | tuple[int],
319
+ size: tuple[int, int] | tuple[int], # type: ignore[override]
313
320
  event,
314
321
  button: int,
315
322
  col: int,