urwid 4.2.2__tar.gz → 4.2.4__tar.gz

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 (118) hide show
  1. {urwid-4.2.2/urwid.egg-info → urwid-4.2.4}/PKG-INFO +1 -1
  2. {urwid-4.2.2 → urwid-4.2.4}/urwid/version.py +3 -3
  3. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/columns.py +9 -3
  4. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/grid_flow.py +0 -3
  5. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/listbox.py +8 -1
  6. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/pile.py +9 -3
  7. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/text.py +22 -4
  8. {urwid-4.2.2 → urwid-4.2.4/urwid.egg-info}/PKG-INFO +1 -1
  9. urwid-4.2.4/urwid.egg-info/scm_version.json +8 -0
  10. urwid-4.2.2/urwid.egg-info/scm_version.json +0 -8
  11. {urwid-4.2.2 → urwid-4.2.4}/.devcontainer/dev.dockerfile +0 -0
  12. {urwid-4.2.2 → urwid-4.2.4}/.devcontainer/devcontainer.json +0 -0
  13. {urwid-4.2.2 → urwid-4.2.4}/.dockerignore +0 -0
  14. {urwid-4.2.2 → urwid-4.2.4}/.editorconfig +0 -0
  15. {urwid-4.2.2 → urwid-4.2.4}/.github/CODE_OF_CONDUCT.md +0 -0
  16. {urwid-4.2.2 → urwid-4.2.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  17. {urwid-4.2.2 → urwid-4.2.4}/.github/ISSUE_TEMPLATE/docs.md +0 -0
  18. {urwid-4.2.2 → urwid-4.2.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  19. {urwid-4.2.2 → urwid-4.2.4}/.github/ISSUE_TEMPLATE/question.md +0 -0
  20. {urwid-4.2.2 → urwid-4.2.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  21. {urwid-4.2.2 → urwid-4.2.4}/.github/dependabot.yml +0 -0
  22. {urwid-4.2.2 → urwid-4.2.4}/.github/labeler.yml +0 -0
  23. {urwid-4.2.2 → urwid-4.2.4}/.github/release.yml +0 -0
  24. {urwid-4.2.2 → urwid-4.2.4}/.github/workflows/documentation.yml +0 -0
  25. {urwid-4.2.2 → urwid-4.2.4}/.github/workflows/isolated_mypy.yml +0 -0
  26. {urwid-4.2.2 → urwid-4.2.4}/.github/workflows/isolated_pylint.yml +0 -0
  27. {urwid-4.2.2 → urwid-4.2.4}/.github/workflows/labels.yml +0 -0
  28. {urwid-4.2.2 → urwid-4.2.4}/.github/workflows/pythonpackage.yml +0 -0
  29. {urwid-4.2.2 → urwid-4.2.4}/.github/workflows/yamllint.yml +0 -0
  30. {urwid-4.2.2 → urwid-4.2.4}/.gitignore +0 -0
  31. {urwid-4.2.2 → urwid-4.2.4}/.pre-commit-config.yaml +0 -0
  32. {urwid-4.2.2 → urwid-4.2.4}/.yamllint.yml +0 -0
  33. {urwid-4.2.2 → urwid-4.2.4}/COPYING +0 -0
  34. {urwid-4.2.2 → urwid-4.2.4}/MANIFEST.in +0 -0
  35. {urwid-4.2.2 → urwid-4.2.4}/README.rst +0 -0
  36. {urwid-4.2.2 → urwid-4.2.4}/SECURITY.md +0 -0
  37. {urwid-4.2.2 → urwid-4.2.4}/classifiers.txt +0 -0
  38. {urwid-4.2.2 → urwid-4.2.4}/conftest.py +0 -0
  39. {urwid-4.2.2 → urwid-4.2.4}/mypy-requirements.txt +0 -0
  40. {urwid-4.2.2 → urwid-4.2.4}/pylint-requirements.txt +0 -0
  41. {urwid-4.2.2 → urwid-4.2.4}/pyproject.toml +0 -0
  42. {urwid-4.2.2 → urwid-4.2.4}/pytest_requirements.txt +0 -0
  43. {urwid-4.2.2 → urwid-4.2.4}/requirements.txt +0 -0
  44. {urwid-4.2.2 → urwid-4.2.4}/setup.cfg +0 -0
  45. {urwid-4.2.2 → urwid-4.2.4}/tox.ini +0 -0
  46. {urwid-4.2.2 → urwid-4.2.4}/urwid/__init__.py +0 -0
  47. {urwid-4.2.2 → urwid-4.2.4}/urwid/ansi_parser.py +0 -0
  48. {urwid-4.2.2 → urwid-4.2.4}/urwid/canvas.py +0 -0
  49. {urwid-4.2.2 → urwid-4.2.4}/urwid/command_map.py +0 -0
  50. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/__init__.py +0 -0
  51. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/_posix_raw_display.py +0 -0
  52. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/_raw_display_base.py +0 -0
  53. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/_web.css +0 -0
  54. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/_web.js +0 -0
  55. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/_win32.py +0 -0
  56. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/_win32_raw_display.py +0 -0
  57. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/common.py +0 -0
  58. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/curses.py +0 -0
  59. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/escape.py +0 -0
  60. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/html_fragment.py +0 -0
  61. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/lcd.py +0 -0
  62. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/raw.py +0 -0
  63. {urwid-4.2.2 → urwid-4.2.4}/urwid/display/web.py +0 -0
  64. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/__init__.py +0 -0
  65. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/abstract_loop.py +0 -0
  66. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/asyncio_loop.py +0 -0
  67. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/glib_loop.py +0 -0
  68. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/main_loop.py +0 -0
  69. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/select_loop.py +0 -0
  70. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/tornado_loop.py +0 -0
  71. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/trio_loop.py +0 -0
  72. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/twisted_loop.py +0 -0
  73. {urwid-4.2.2 → urwid-4.2.4}/urwid/event_loop/zmq_loop.py +0 -0
  74. {urwid-4.2.2 → urwid-4.2.4}/urwid/font.py +0 -0
  75. {urwid-4.2.2 → urwid-4.2.4}/urwid/graphics.py +0 -0
  76. {urwid-4.2.2 → urwid-4.2.4}/urwid/numedit.py +0 -0
  77. {urwid-4.2.2 → urwid-4.2.4}/urwid/py.typed +0 -0
  78. {urwid-4.2.2 → urwid-4.2.4}/urwid/signals.py +0 -0
  79. {urwid-4.2.2 → urwid-4.2.4}/urwid/split_repr.py +0 -0
  80. {urwid-4.2.2 → urwid-4.2.4}/urwid/str_util.py +0 -0
  81. {urwid-4.2.2 → urwid-4.2.4}/urwid/text_layout.py +0 -0
  82. {urwid-4.2.2 → urwid-4.2.4}/urwid/util.py +0 -0
  83. {urwid-4.2.2 → urwid-4.2.4}/urwid/vterm.py +0 -0
  84. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/__init__.py +0 -0
  85. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/ansi_text.py +0 -0
  86. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/attr_map.py +0 -0
  87. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/attr_wrap.py +0 -0
  88. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/bar_graph.py +0 -0
  89. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/big_text.py +0 -0
  90. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/box_adapter.py +0 -0
  91. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/constants.py +0 -0
  92. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/container.py +0 -0
  93. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/deque_walker.py +0 -0
  94. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/divider.py +0 -0
  95. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/edit.py +0 -0
  96. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/filler.py +0 -0
  97. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/frame.py +0 -0
  98. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/line_box.py +0 -0
  99. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/list_walker.py +0 -0
  100. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/monitored_deque.py +0 -0
  101. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/monitored_list.py +0 -0
  102. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/overlay.py +0 -0
  103. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/padding.py +0 -0
  104. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/popup.py +0 -0
  105. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/progress_bar.py +0 -0
  106. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/scrollable.py +0 -0
  107. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/solid_fill.py +0 -0
  108. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/treetools.py +0 -0
  109. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/widget.py +0 -0
  110. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/widget_decoration.py +0 -0
  111. {urwid-4.2.2 → urwid-4.2.4}/urwid/widget/wimp.py +0 -0
  112. {urwid-4.2.2 → urwid-4.2.4}/urwid.egg-info/SOURCES.txt +0 -0
  113. {urwid-4.2.2 → urwid-4.2.4}/urwid.egg-info/dependency_links.txt +0 -0
  114. {urwid-4.2.2 → urwid-4.2.4}/urwid.egg-info/not-zip-safe +0 -0
  115. {urwid-4.2.2 → urwid-4.2.4}/urwid.egg-info/requires.txt +0 -0
  116. {urwid-4.2.2 → urwid-4.2.4}/urwid.egg-info/scm_file_list.json +0 -0
  117. {urwid-4.2.2 → urwid-4.2.4}/urwid.egg-info/top_level.txt +0 -0
  118. {urwid-4.2.2 → urwid-4.2.4}/yamllint-requirements.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: urwid
3
- Version: 4.2.2
3
+ Version: 4.2.4
4
4
  Summary: A full-featured console (xterm et al.) user interface library
5
5
  Author-email: Ian Ward <ian@excess.org>
6
6
  License-Expression: LGPL-2.1-only
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '4.2.2'
22
- __version_tuple__ = version_tuple = (4, 2, 2)
21
+ __version__ = version = '4.2.4'
22
+ __version_tuple__ = version_tuple = (4, 2, 4)
23
23
 
24
- __commit_id__ = commit_id = 'gd131ddace'
24
+ __commit_id__ = commit_id = 'gb84a230c2'
@@ -275,7 +275,6 @@ class Columns(
275
275
  *box_columns* will be displayed with this calculated number of rows,
276
276
  filling the full height.
277
277
  """
278
- self._selectable = False
279
278
  self._cache_column_widths: list[int] = []
280
279
  self._cache_pack_widths: dict[int, int] = {}
281
280
  super().__init__()
@@ -401,10 +400,17 @@ class Columns(
401
400
  return len(self._contents)
402
401
 
403
402
  def _contents_modified(self) -> None:
404
- """Recalculate whether this widget should be selectable whenever the contents has been changed."""
405
- self._selectable = any(w.selectable() for w, o in self.contents)
403
+ """Invalidate this widget whenever the contents has been changed."""
406
404
  self._invalidate()
407
405
 
406
+ def selectable(self) -> bool:
407
+ """Return whether any child widget is selectable.
408
+
409
+ Asked on every call rather than cached,
410
+ since a child such as :class:`~urwid.WidgetPlaceholder` may change its selectability after it was added.
411
+ """
412
+ return any(w.selectable() for w, _ in self.contents)
413
+
408
414
  def _validate_contents_modified(
409
415
  self,
410
416
  slc: tuple[int, int, int],
@@ -475,9 +475,6 @@ class GridFlow(
475
475
  if self.v_sep:
476
476
  # remove first divider
477
477
  del p.contents[:1]
478
- else:
479
- # Ensure p __selectable is updated
480
- p._contents_modified() # pylint: disable=protected-access
481
478
 
482
479
  return p
483
480
 
@@ -900,7 +900,14 @@ class ListBox(Widget, WidgetContainerMixin[_K]):
900
900
  raise TypeError(f"{type(self._body)}.set_focus is not implemented.")
901
901
 
902
902
  # restore old focus temporarily
903
- self._body.set_focus(focus_pos)
903
+ try:
904
+ self._body.set_focus(focus_pos)
905
+ except (IndexError, KeyError):
906
+ # A focus that did move means a "modified" signal callback raised, not that the position is gone.
907
+ if self._body.get_focus()[1] != position:
908
+ raise
909
+ # The body lost the old focus position after ListBox.set_focus().
910
+ # The walker still focuses the new position, and the placement below starts from it.
904
911
 
905
912
  middle, top, bottom = self.calculate_visible((maxcol, maxrow), focus)
906
913
  if middle is None or top is None or bottom is None:
@@ -223,7 +223,6 @@ class Pile(
223
223
  .. note:: If the Pile is treated as a box widget there must be at least
224
224
  one ``'weight'`` tuple in :attr:`widget_list`.
225
225
  """
226
- self._selectable = False
227
226
  super().__init__()
228
227
  self._contents: MonitoredFocusList[
229
228
  tuple[
@@ -344,10 +343,17 @@ class Pile(
344
343
  return len(self._contents)
345
344
 
346
345
  def _contents_modified(self) -> None:
347
- """Recalculate whether this widget should be selectable whenever the contents has been changed."""
348
- self._selectable = any(w.selectable() for w, o in self.contents)
346
+ """Invalidate this widget whenever the contents has been changed."""
349
347
  self._invalidate()
350
348
 
349
+ def selectable(self) -> bool:
350
+ """Return whether any child widget is selectable.
351
+
352
+ Asked on every call rather than cached,
353
+ since a child such as :class:`~urwid.WidgetPlaceholder` may change its selectability after it was added.
354
+ """
355
+ return any(w.selectable() for w, _ in self.contents)
356
+
351
357
  def _validate_contents_modified(
352
358
  self,
353
359
  slc: tuple[int, int, int],
@@ -316,6 +316,17 @@ class Text(Widget):
316
316
  """Return the number of screen columns and rows required for this Text widget.
317
317
 
318
318
  The widget is expected to be displayed without wrapping or clipping, as a single element tuple.
319
+ With *maxcol* the size is taken from the layout at that width.
320
+ Without it the width is searched for::
321
+
322
+ maxcol = text width + 1, with StandardTextLayout tabs at their widest
323
+ |
324
+ v
325
+ lay out at maxcol --> layout has pack()? --no--> widest line, line count
326
+ ^ |
327
+ | yes
328
+ | v
329
+ double maxcol <--no-- layout.pack() < maxcol? --yes--> pack() columns, line count
319
330
 
320
331
  :param size: ``None`` or ``()`` for unlimited screen columns (like FIXED sizing)
321
332
  or (*maxcol*,) to specify a maximum column size
@@ -354,12 +365,19 @@ class Text(Widget):
354
365
  cols = self.layout.pack(maxcol, trans)
355
366
  return (cols, len(trans))
356
367
 
357
- # no line is wider than the whole text, except when tabs (counted here as 0 columns) push it further
368
+ layout = self.layout
369
+ # No line is wider than the whole text with every tab taking the widest gap between tab stops.
370
+ # A custom layout may still widen a line.
358
371
  maxcol = calc_width(text, 0, len(text)) + 1
372
+ tabs = text.count("\t") if isinstance(text, str) else text.count(b"\t")
373
+ if tabs and isinstance(layout, text_layout.StandardTextLayout):
374
+ stops = (0, *layout.tab_stops)
375
+ widest_tab = max((layout.tab_stop_every, *(end - start for start, end in zip(stops, stops[1:]))))
376
+ maxcol += widest_tab * tabs
359
377
  while True:
360
- trans = self.layout.layout(text, maxcol, self._align_mode, self._wrap_mode)
361
- if not hasattr(self.layout, "pack"):
378
+ trans = layout.layout(text, maxcol, self._align_mode, self._wrap_mode)
379
+ if not hasattr(layout, "pack"):
362
380
  return max(text_layout.line_width(line) for line in trans), len(trans)
363
- if (cols := self.layout.pack(maxcol, trans)) < maxcol:
381
+ if (cols := layout.pack(maxcol, trans)) < maxcol:
364
382
  return cols, len(trans)
365
383
  maxcol *= 2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: urwid
3
- Version: 4.2.2
3
+ Version: 4.2.4
4
4
  Summary: A full-featured console (xterm et al.) user interface library
5
5
  Author-email: Ian Ward <ian@excess.org>
6
6
  License-Expression: LGPL-2.1-only
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "4.2.4",
3
+ "distance": 0,
4
+ "node": "gb84a230c2b7d3de4a96480668002c34db0ff6e95",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-01"
8
+ }
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "4.2.2",
3
- "distance": 0,
4
- "node": "gd131ddacee0bce743eefcb49d6d0ee2873ae3f6e",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-09-30"
8
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes