urwid 2.6.15__py3-none-any.whl → 3.0.0__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 +1 -4
- urwid/canvas.py +27 -46
- urwid/command_map.py +6 -4
- urwid/container.py +1 -1
- urwid/decoration.py +1 -1
- urwid/display/_posix_raw_display.py +16 -4
- urwid/display/_raw_display_base.py +8 -5
- urwid/display/_win32_raw_display.py +16 -17
- urwid/display/common.py +26 -55
- urwid/display/curses.py +1 -1
- urwid/display/escape.py +15 -12
- urwid/display/lcd.py +4 -6
- urwid/display/web.py +7 -12
- urwid/event_loop/asyncio_loop.py +1 -2
- urwid/event_loop/main_loop.py +13 -18
- urwid/event_loop/tornado_loop.py +4 -5
- urwid/event_loop/trio_loop.py +1 -1
- urwid/font.py +13 -18
- urwid/signals.py +2 -1
- urwid/str_util.py +15 -18
- urwid/text_layout.py +6 -7
- urwid/util.py +7 -18
- urwid/version.py +9 -4
- urwid/vterm.py +18 -45
- urwid/widget/__init__.py +0 -6
- urwid/widget/attr_wrap.py +8 -10
- urwid/widget/bar_graph.py +3 -8
- urwid/widget/big_text.py +9 -7
- urwid/widget/box_adapter.py +4 -4
- urwid/widget/columns.py +52 -83
- urwid/widget/container.py +29 -75
- urwid/widget/edit.py +8 -8
- urwid/widget/filler.py +6 -6
- urwid/widget/frame.py +28 -37
- urwid/widget/grid_flow.py +25 -110
- urwid/widget/line_box.py +13 -0
- urwid/widget/listbox.py +12 -50
- urwid/widget/monitored_list.py +6 -4
- urwid/widget/overlay.py +4 -37
- urwid/widget/padding.py +11 -48
- urwid/widget/pile.py +179 -158
- urwid/widget/popup.py +2 -2
- urwid/widget/progress_bar.py +10 -11
- urwid/widget/scrollable.py +25 -33
- urwid/widget/treetools.py +27 -48
- urwid/widget/widget.py +7 -124
- urwid/widget/widget_decoration.py +4 -33
- urwid/wimp.py +1 -1
- {urwid-2.6.15.dist-info → urwid-3.0.0.dist-info}/METADATA +18 -18
- urwid-3.0.0.dist-info/RECORD +74 -0
- {urwid-2.6.15.dist-info → urwid-3.0.0.dist-info}/WHEEL +1 -1
- urwid-2.6.15.dist-info/RECORD +0 -74
- {urwid-2.6.15.dist-info → urwid-3.0.0.dist-info/licenses}/COPYING +0 -0
- {urwid-2.6.15.dist-info → urwid-3.0.0.dist-info}/top_level.txt +0 -0
urwid/display/escape.py
CHANGED
|
@@ -32,7 +32,7 @@ from collections.abc import MutableMapping, Sequence
|
|
|
32
32
|
from urwid import str_util
|
|
33
33
|
|
|
34
34
|
if typing.TYPE_CHECKING:
|
|
35
|
-
from collections.abc import
|
|
35
|
+
from collections.abc import Iterable
|
|
36
36
|
|
|
37
37
|
# NOTE: because of circular imports (urwid.util -> urwid.escape -> urwid.util)
|
|
38
38
|
# from urwid.util import is_mouse_event -- will not work here
|
|
@@ -85,6 +85,8 @@ input_sequences = [
|
|
|
85
85
|
("[F", "end"),
|
|
86
86
|
("[G", "5"),
|
|
87
87
|
("[H", "home"),
|
|
88
|
+
("[I", "focus in"),
|
|
89
|
+
("[O", "focus out"),
|
|
88
90
|
("[1~", "home"),
|
|
89
91
|
("[2~", "insert"),
|
|
90
92
|
("[3~", "delete"),
|
|
@@ -214,10 +216,10 @@ class KeyqueueTrie:
|
|
|
214
216
|
root[ord(s)] = result
|
|
215
217
|
|
|
216
218
|
def get(self, keys, more_available: bool):
|
|
217
|
-
result
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
return
|
|
219
|
+
if result := self.get_recurse(self.data, keys, more_available):
|
|
220
|
+
return result
|
|
221
|
+
|
|
222
|
+
return self.read_cursor_position(keys, more_available)
|
|
221
223
|
|
|
222
224
|
def get_recurse(
|
|
223
225
|
self,
|
|
@@ -225,7 +227,7 @@ class KeyqueueTrie:
|
|
|
225
227
|
MutableMapping[int, str | MutableMapping[int, str | MutableMapping[int, str]]]
|
|
226
228
|
| typing.Literal["mouse", "sgrmouse"]
|
|
227
229
|
),
|
|
228
|
-
keys:
|
|
230
|
+
keys: Sequence[int],
|
|
229
231
|
more_available: bool,
|
|
230
232
|
):
|
|
231
233
|
if not isinstance(root, MutableMapping):
|
|
@@ -245,7 +247,7 @@ class KeyqueueTrie:
|
|
|
245
247
|
return None
|
|
246
248
|
return self.get_recurse(root[keys[0]], keys[1:], more_available)
|
|
247
249
|
|
|
248
|
-
def read_mouse_info(self, keys:
|
|
250
|
+
def read_mouse_info(self, keys: Sequence[int], more_available: bool):
|
|
249
251
|
if len(keys) < 3:
|
|
250
252
|
if more_available:
|
|
251
253
|
raise MoreInputRequired()
|
|
@@ -284,7 +286,7 @@ class KeyqueueTrie:
|
|
|
284
286
|
|
|
285
287
|
return ((f"{prefix}mouse {action}", button, x, y), keys[3:])
|
|
286
288
|
|
|
287
|
-
def read_sgrmouse_info(self, keys:
|
|
289
|
+
def read_sgrmouse_info(self, keys: Sequence[int], more_available: bool):
|
|
288
290
|
# Helpful links:
|
|
289
291
|
# https://stackoverflow.com/questions/5966903/how-to-get-mousemove-and-mouseclick-in-bash
|
|
290
292
|
# http://invisible-island.net/xterm/ctlseqs/ctlseqs.pdf
|
|
@@ -538,9 +540,7 @@ def process_keyqueue(codes: Sequence[int], more_available: bool) -> tuple[list[s
|
|
|
538
540
|
if code != 27:
|
|
539
541
|
return [f"<{code:d}>"], codes[1:]
|
|
540
542
|
|
|
541
|
-
result
|
|
542
|
-
|
|
543
|
-
if result is not None:
|
|
543
|
+
if (result := input_trie.get(codes[1:], more_available)) is not None:
|
|
544
544
|
result, remaining_codes = result
|
|
545
545
|
return [result], remaining_codes
|
|
546
546
|
|
|
@@ -551,7 +551,7 @@ def process_keyqueue(codes: Sequence[int], more_available: bool) -> tuple[list[s
|
|
|
551
551
|
return ["esc", *run], remaining_codes
|
|
552
552
|
if run[0] == "esc" or run[0].find("meta ") >= 0:
|
|
553
553
|
return ["esc", *run], remaining_codes
|
|
554
|
-
return [f"meta {run[0]}"
|
|
554
|
+
return [f"meta {run[0]}", *run[1:]], remaining_codes
|
|
555
555
|
|
|
556
556
|
return ["esc"], codes[1:]
|
|
557
557
|
|
|
@@ -574,6 +574,9 @@ RESTORE_NORMAL_BUFFER = f"{ESC}[?1049l"
|
|
|
574
574
|
ENABLE_BRACKETED_PASTE_MODE = f"{ESC}[?2004h"
|
|
575
575
|
DISABLE_BRACKETED_PASTE_MODE = f"{ESC}[?2004l"
|
|
576
576
|
|
|
577
|
+
ENABLE_FOCUS_REPORTING = f"{ESC}[?1004h"
|
|
578
|
+
DISABLE_FOCUS_REPORTING = f"{ESC}[?1004l"
|
|
579
|
+
|
|
577
580
|
# RESET_SCROLL_REGION = ESC+"[;r"
|
|
578
581
|
# RESET = ESC+"c"
|
|
579
582
|
|
urwid/display/lcd.py
CHANGED
|
@@ -218,10 +218,9 @@ class CFLCDScreen(LCDScreen, abc.ABC):
|
|
|
218
218
|
raise cls.MoreDataRequired
|
|
219
219
|
|
|
220
220
|
data_end = 2 + packet_len
|
|
221
|
-
crc
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
raise cls.InvalidPacket("CRC doesn't match")
|
|
221
|
+
if (crc := cls.get_crc(data[:data_end])) != (packet_crc := data[data_end : data_end + 2]):
|
|
222
|
+
raise cls.InvalidPacket(f"CRC doesn't match ({crc=}, {packet_crc=})")
|
|
223
|
+
|
|
225
224
|
return command, data[2:data_end], data[data_end + 2 :]
|
|
226
225
|
|
|
227
226
|
|
|
@@ -401,8 +400,7 @@ class CF635Screen(CFLCDScreen):
|
|
|
401
400
|
|
|
402
401
|
packet = self._read_packet()
|
|
403
402
|
|
|
404
|
-
next_repeat
|
|
405
|
-
if next_repeat:
|
|
403
|
+
if next_repeat := self.key_repeat.next_event():
|
|
406
404
|
timeout, key = next_repeat
|
|
407
405
|
if not timeout:
|
|
408
406
|
data_input.append(key)
|
urwid/display/web.py
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"""
|
|
22
22
|
Urwid web application display module
|
|
23
23
|
"""
|
|
24
|
+
|
|
24
25
|
from __future__ import annotations
|
|
25
26
|
|
|
26
27
|
import dataclasses
|
|
@@ -216,7 +217,7 @@ class Screen(BaseScreen):
|
|
|
216
217
|
sys.stdout.write("Status: 503 Sever Busy\r\n\r\n")
|
|
217
218
|
sys.exit(0)
|
|
218
219
|
|
|
219
|
-
urwid_id = f"{random.randrange(10
|
|
220
|
+
urwid_id = f"{random.randrange(10**9):09d}{random.randrange(10**9):09d}" # noqa: S311
|
|
220
221
|
self.pipe_name = os.path.join(_prefs.pipe_dir, f"urwid{urwid_id}")
|
|
221
222
|
os.mkfifo(f"{self.pipe_name}.in", 0o600)
|
|
222
223
|
signal.signal(signal.SIGTERM, self._cleanup_pipe)
|
|
@@ -224,11 +225,7 @@ class Screen(BaseScreen):
|
|
|
224
225
|
self.input_fd = os.open(f"{self.pipe_name}.in", os.O_NONBLOCK | os.O_RDONLY)
|
|
225
226
|
self.input_tail = ""
|
|
226
227
|
self.content_head = (
|
|
227
|
-
"Content-type: "
|
|
228
|
-
"multipart/x-mixed-replace;boundary=ZZ\r\n"
|
|
229
|
-
"X-Urwid-ID: " + urwid_id + "\r\n"
|
|
230
|
-
"\r\n\r\n"
|
|
231
|
-
"--ZZ\r\n"
|
|
228
|
+
f"Content-type: multipart/x-mixed-replace;boundary=ZZ\r\nX-Urwid-ID: {urwid_id}\r\n\r\n\r\n--ZZ\r\n"
|
|
232
229
|
)
|
|
233
230
|
if self.update_method == "polling":
|
|
234
231
|
self.content_head = f"Content-type: text/plain\r\nX-Urwid-ID: {urwid_id}\r\n\r\n\r\n"
|
|
@@ -328,8 +325,8 @@ class Screen(BaseScreen):
|
|
|
328
325
|
if y == cy:
|
|
329
326
|
sig = (*sig, cx)
|
|
330
327
|
new_screen[sig] = [*new_screen.get(sig, []), y]
|
|
331
|
-
|
|
332
|
-
if old_line_numbers is not None:
|
|
328
|
+
|
|
329
|
+
if (old_line_numbers := self.last_screen.get(sig, None)) is not None:
|
|
333
330
|
if y in old_line_numbers:
|
|
334
331
|
old_line = y
|
|
335
332
|
else:
|
|
@@ -609,16 +606,14 @@ def daemonize(errfile: str) -> None:
|
|
|
609
606
|
"""
|
|
610
607
|
Detach process and become a daemon.
|
|
611
608
|
"""
|
|
612
|
-
|
|
613
|
-
if pid:
|
|
609
|
+
if os.fork():
|
|
614
610
|
os._exit(0)
|
|
615
611
|
|
|
616
612
|
os.setsid()
|
|
617
613
|
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
|
618
614
|
os.umask(0)
|
|
619
615
|
|
|
620
|
-
|
|
621
|
-
if pid:
|
|
616
|
+
if os.fork():
|
|
622
617
|
os._exit(0)
|
|
623
618
|
|
|
624
619
|
os.chdir("/")
|
urwid/event_loop/asyncio_loop.py
CHANGED
|
@@ -205,8 +205,7 @@ class AsyncioEventLoop(EventLoop):
|
|
|
205
205
|
return True
|
|
206
206
|
|
|
207
207
|
def _exception_handler(self, loop: asyncio.AbstractEventLoop, context):
|
|
208
|
-
exc
|
|
209
|
-
if exc:
|
|
208
|
+
if exc := context.get("exception"):
|
|
210
209
|
loop.stop()
|
|
211
210
|
|
|
212
211
|
if self._idle_asyncio_handle:
|
urwid/event_loop/main_loop.py
CHANGED
|
@@ -172,7 +172,8 @@ class MainLoop:
|
|
|
172
172
|
def _set_widget(self, widget: Widget) -> None:
|
|
173
173
|
warnings.warn(
|
|
174
174
|
f"method `{self.__class__.__name__}._set_widget` is deprecated, "
|
|
175
|
-
f"please use `{self.__class__.__name__}.widget` property"
|
|
175
|
+
f"please use `{self.__class__.__name__}.widget` property."
|
|
176
|
+
"API will be removed in version 4.0.",
|
|
176
177
|
DeprecationWarning,
|
|
177
178
|
stacklevel=2,
|
|
178
179
|
)
|
|
@@ -193,7 +194,8 @@ class MainLoop:
|
|
|
193
194
|
def _set_pop_ups(self, pop_ups: bool) -> None:
|
|
194
195
|
warnings.warn(
|
|
195
196
|
f"method `{self.__class__.__name__}._set_pop_ups` is deprecated, "
|
|
196
|
-
f"please use `{self.__class__.__name__}.pop_ups` property"
|
|
197
|
+
f"please use `{self.__class__.__name__}.pop_ups` property."
|
|
198
|
+
"API will be removed in version 4.0.",
|
|
197
199
|
DeprecationWarning,
|
|
198
200
|
stacklevel=2,
|
|
199
201
|
)
|
|
@@ -460,9 +462,7 @@ class MainLoop:
|
|
|
460
462
|
widget.mouse_event((15, 5), 'mouse press', 1, 5, 4, focus=True)
|
|
461
463
|
>>> ml._update([], [])
|
|
462
464
|
"""
|
|
463
|
-
keys
|
|
464
|
-
|
|
465
|
-
if keys:
|
|
465
|
+
if keys := self.input_filter(keys, raw):
|
|
466
466
|
self.process_input(keys)
|
|
467
467
|
if "window resize" in keys:
|
|
468
468
|
self.screen_size = None
|
|
@@ -493,19 +493,14 @@ class MainLoop:
|
|
|
493
493
|
else:
|
|
494
494
|
self.screen.set_input_timeouts(None)
|
|
495
495
|
keys, raw = self.screen.get_input(True)
|
|
496
|
-
if not keys and next_alarm:
|
|
497
|
-
|
|
498
|
-
if sec <= 0:
|
|
499
|
-
break
|
|
500
|
-
|
|
501
|
-
keys = self.input_filter(keys, raw)
|
|
496
|
+
if not keys and next_alarm and next_alarm[0] - time.time() <= 0:
|
|
497
|
+
break
|
|
502
498
|
|
|
503
|
-
if keys:
|
|
499
|
+
if keys := self.input_filter(keys, raw):
|
|
504
500
|
self.process_input(keys)
|
|
505
501
|
|
|
506
502
|
while next_alarm:
|
|
507
|
-
|
|
508
|
-
if sec > 0:
|
|
503
|
+
if (next_alarm[0] - time.time()) > 0:
|
|
509
504
|
break
|
|
510
505
|
_tm, _tie_break, callback = next_alarm
|
|
511
506
|
callback()
|
|
@@ -563,13 +558,13 @@ class MainLoop:
|
|
|
563
558
|
|
|
564
559
|
if isinstance(key, str):
|
|
565
560
|
if self._topmost_widget.selectable():
|
|
566
|
-
handled_key
|
|
567
|
-
|
|
561
|
+
if handled_key := self._topmost_widget.keypress(self.screen_size, key):
|
|
562
|
+
key = handled_key # noqa: PLW2901
|
|
563
|
+
|
|
564
|
+
else:
|
|
568
565
|
something_handled = True
|
|
569
566
|
continue
|
|
570
567
|
|
|
571
|
-
key = handled_key # noqa: PLW2901
|
|
572
|
-
|
|
573
568
|
elif is_mouse_event(key):
|
|
574
569
|
event, button, col, row = key
|
|
575
570
|
if hasattr(self._topmost_widget, "mouse_event") and self._topmost_widget.mouse_event(
|
urwid/event_loop/tornado_loop.py
CHANGED
|
@@ -155,12 +155,11 @@ class TornadoEventLoop(EventLoop):
|
|
|
155
155
|
return handle
|
|
156
156
|
|
|
157
157
|
def remove_watch_file(self, handle: int) -> bool:
|
|
158
|
-
fd
|
|
159
|
-
|
|
160
|
-
return
|
|
158
|
+
if (fd := self._watch_handles.pop(handle, None)) is not None:
|
|
159
|
+
self._loop.remove_handler(fd)
|
|
160
|
+
return True
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
return True
|
|
162
|
+
return False
|
|
164
163
|
|
|
165
164
|
def enter_idle(self, callback: Callable[[], typing.Any]) -> int:
|
|
166
165
|
"""
|
urwid/event_loop/trio_loop.py
CHANGED
urwid/font.py
CHANGED
|
@@ -29,7 +29,7 @@ from urwid.display.escape import SAFE_ASCII_DEC_SPECIAL_RE
|
|
|
29
29
|
from urwid.util import apply_target_encoding, str_util
|
|
30
30
|
|
|
31
31
|
if typing.TYPE_CHECKING:
|
|
32
|
-
from collections.abc import
|
|
32
|
+
from collections.abc import Iterator, Sequence
|
|
33
33
|
|
|
34
34
|
from typing_extensions import Literal
|
|
35
35
|
|
|
@@ -192,9 +192,9 @@ class Font(metaclass=FontRegistry):
|
|
|
192
192
|
|
|
193
193
|
__slots__ = ("canvas", "char", "utf8_required")
|
|
194
194
|
|
|
195
|
-
height: int
|
|
196
|
-
data: Sequence[str]
|
|
197
|
-
name: str
|
|
195
|
+
height: int # pylint: disable=declare-non-slot
|
|
196
|
+
data: Sequence[str] # pylint: disable=declare-non-slot
|
|
197
|
+
name: str # pylint: disable=declare-non-slot
|
|
198
198
|
|
|
199
199
|
def __init__(self) -> None:
|
|
200
200
|
if not self.height:
|
|
@@ -206,12 +206,10 @@ class Font(metaclass=FontRegistry):
|
|
|
206
206
|
self.canvas: dict[str, TextCanvas] = {}
|
|
207
207
|
self.utf8_required = False
|
|
208
208
|
if isinstance(self.data, str):
|
|
209
|
-
self.add_glyphs(self.
|
|
209
|
+
self.add_glyphs(self.data)
|
|
210
210
|
|
|
211
211
|
else:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
for gdata in data:
|
|
212
|
+
for gdata in self.data:
|
|
215
213
|
self.add_glyphs(gdata)
|
|
216
214
|
|
|
217
215
|
def __repr__(self) -> str:
|
|
@@ -223,22 +221,19 @@ class Font(metaclass=FontRegistry):
|
|
|
223
221
|
|
|
224
222
|
@staticmethod
|
|
225
223
|
def _to_text(
|
|
226
|
-
obj: str
|
|
224
|
+
obj: str,
|
|
227
225
|
encoding: str = "utf-8",
|
|
228
226
|
errors: Literal["strict", "ignore", "replace"] = "strict",
|
|
229
227
|
) -> str:
|
|
228
|
+
warnings.warn(
|
|
229
|
+
"_to_text is deprecated: only text fonts are supported. API will be removed in version 4.0.",
|
|
230
|
+
DeprecationWarning,
|
|
231
|
+
stacklevel=3,
|
|
232
|
+
)
|
|
230
233
|
if isinstance(obj, str):
|
|
231
234
|
return obj
|
|
232
235
|
|
|
233
|
-
|
|
234
|
-
warnings.warn(
|
|
235
|
-
"Bytes based fonts are deprecated, please switch to the text one",
|
|
236
|
-
DeprecationWarning,
|
|
237
|
-
stacklevel=3,
|
|
238
|
-
)
|
|
239
|
-
return obj.decode(encoding, errors)
|
|
240
|
-
|
|
241
|
-
raise TypeError(f"{obj!r} is not str|bytes")
|
|
236
|
+
raise TypeError(f"{obj!r} is not str")
|
|
242
237
|
|
|
243
238
|
def add_glyphs(self, gdata: str) -> None:
|
|
244
239
|
d, utf8_required = separate_glyphs(gdata, self.height)
|
urwid/signals.py
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
from __future__ import annotations
|
|
22
22
|
|
|
23
|
+
import abc
|
|
23
24
|
import itertools
|
|
24
25
|
import typing
|
|
25
26
|
import warnings
|
|
@@ -29,7 +30,7 @@ if typing.TYPE_CHECKING:
|
|
|
29
30
|
from collections.abc import Callable, Collection, Container, Hashable, Iterable
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
class MetaSignals(
|
|
33
|
+
class MetaSignals(abc.ABCMeta):
|
|
33
34
|
"""
|
|
34
35
|
register the list of signals in the class variable signals,
|
|
35
36
|
including signals in superclasses.
|
urwid/str_util.py
CHANGED
|
@@ -29,17 +29,17 @@ import wcwidth
|
|
|
29
29
|
if typing.TYPE_CHECKING:
|
|
30
30
|
from typing_extensions import Literal
|
|
31
31
|
|
|
32
|
-
SAFE_ASCII_RE = re.compile("^[ -~]*$")
|
|
33
|
-
SAFE_ASCII_BYTES_RE = re.compile(
|
|
32
|
+
SAFE_ASCII_RE = re.compile(r"^[ -~]*$")
|
|
33
|
+
SAFE_ASCII_BYTES_RE = re.compile(rb"^[ -~]*$")
|
|
34
34
|
|
|
35
35
|
_byte_encoding: Literal["utf8", "narrow", "wide"] = "narrow"
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def get_char_width(char: str) -> Literal[0, 1, 2]:
|
|
39
|
-
width
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
39
|
+
if (width := wcwidth.wcwidth(char)) >= 0:
|
|
40
|
+
return width
|
|
41
|
+
|
|
42
|
+
return 0
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def get_width(o: int) -> Literal[0, 1, 2]:
|
|
@@ -86,10 +86,9 @@ def decode_one(text: bytes | str, pos: int) -> tuple[int, int]:
|
|
|
86
86
|
if b1 & 0xE0 == 0xC0:
|
|
87
87
|
if b2 & 0xC0 != 0x80:
|
|
88
88
|
return error
|
|
89
|
-
o
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return o, pos + 2
|
|
89
|
+
if (o := ((b1 & 0x1F) << 6) | (b2 & 0x3F)) >= 0x80:
|
|
90
|
+
return o, pos + 2
|
|
91
|
+
return error
|
|
93
92
|
if lt < 3:
|
|
94
93
|
return error
|
|
95
94
|
if b1 & 0xF0 == 0xE0:
|
|
@@ -97,10 +96,9 @@ def decode_one(text: bytes | str, pos: int) -> tuple[int, int]:
|
|
|
97
96
|
return error
|
|
98
97
|
if b3 & 0xC0 != 0x80:
|
|
99
98
|
return error
|
|
100
|
-
o
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return o, pos + 3
|
|
99
|
+
if (o := ((b1 & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F)) >= 0x800:
|
|
100
|
+
return o, pos + 3
|
|
101
|
+
return error
|
|
104
102
|
if lt < 4:
|
|
105
103
|
return error
|
|
106
104
|
if b1 & 0xF8 == 0xF0:
|
|
@@ -110,10 +108,9 @@ def decode_one(text: bytes | str, pos: int) -> tuple[int, int]:
|
|
|
110
108
|
return error
|
|
111
109
|
if b4 & 0xC0 != 0x80:
|
|
112
110
|
return error
|
|
113
|
-
o
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return o, pos + 4
|
|
111
|
+
if (o := ((b1 & 0x07) << 18) | ((b2 & 0x3F) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F)) >= 0x10000:
|
|
112
|
+
return o, pos + 4
|
|
113
|
+
return error
|
|
117
114
|
return error
|
|
118
115
|
|
|
119
116
|
|
urwid/text_layout.py
CHANGED
|
@@ -461,7 +461,7 @@ def shift_line(
|
|
|
461
461
|
# existing shift
|
|
462
462
|
amount += segs[0][0]
|
|
463
463
|
if amount:
|
|
464
|
-
return [(amount, None)
|
|
464
|
+
return [(amount, None), *segs[1:]]
|
|
465
465
|
return segs[1:]
|
|
466
466
|
|
|
467
467
|
if amount:
|
|
@@ -581,8 +581,7 @@ def calc_pos(
|
|
|
581
581
|
if row < 0 or row >= len(layout):
|
|
582
582
|
raise ValueError("calculate_pos: out of layout row range")
|
|
583
583
|
|
|
584
|
-
pos
|
|
585
|
-
if pos is not None:
|
|
584
|
+
if (pos := calc_line_pos(text, layout[row], pref_col)) is not None:
|
|
586
585
|
return pos
|
|
587
586
|
|
|
588
587
|
rows_above = list(range(row - 1, -1, -1))
|
|
@@ -590,14 +589,14 @@ def calc_pos(
|
|
|
590
589
|
while rows_above and rows_below:
|
|
591
590
|
if rows_above:
|
|
592
591
|
r = rows_above.pop(0)
|
|
593
|
-
pos
|
|
594
|
-
if pos is not None:
|
|
592
|
+
if (pos := calc_line_pos(text, layout[r], pref_col)) is not None:
|
|
595
593
|
return pos
|
|
594
|
+
|
|
596
595
|
if rows_below:
|
|
597
596
|
r = rows_below.pop(0)
|
|
598
|
-
pos
|
|
599
|
-
if pos is not None:
|
|
597
|
+
if (pos := calc_line_pos(text, layout[r], pref_col)) is not None:
|
|
600
598
|
return pos
|
|
599
|
+
|
|
601
600
|
return 0
|
|
602
601
|
|
|
603
602
|
|
urwid/util.py
CHANGED
|
@@ -143,7 +143,7 @@ def get_encoding() -> str:
|
|
|
143
143
|
|
|
144
144
|
|
|
145
145
|
@contextlib.contextmanager
|
|
146
|
-
def set_temporary_encoding(encoding_name: str) -> Generator[None
|
|
146
|
+
def set_temporary_encoding(encoding_name: str) -> Generator[None]:
|
|
147
147
|
"""Internal helper for encoding specific validation in unittests/doctests.
|
|
148
148
|
|
|
149
149
|
Not exported globally.
|
|
@@ -493,24 +493,13 @@ def is_mouse_press(ev: str) -> bool:
|
|
|
493
493
|
|
|
494
494
|
|
|
495
495
|
class MetaSuper(type):
|
|
496
|
-
"""
|
|
496
|
+
"""Deprecated metaclass.
|
|
497
497
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
raise AttributeError("Class has same name as one of its super classes")
|
|
502
|
-
|
|
503
|
-
@property
|
|
504
|
-
def _super(self):
|
|
505
|
-
warnings.warn(
|
|
506
|
-
f"`{name}.__super` was a deprecated feature for old python versions."
|
|
507
|
-
f"Please use `super()` call instead.",
|
|
508
|
-
DeprecationWarning,
|
|
509
|
-
stacklevel=3,
|
|
510
|
-
)
|
|
511
|
-
return super(cls, self)
|
|
498
|
+
Present only for code compatibility, all logic has been removed.
|
|
499
|
+
Please move to the last position in the class bases to allow future changes.
|
|
500
|
+
"""
|
|
512
501
|
|
|
513
|
-
|
|
502
|
+
__slots__ = ()
|
|
514
503
|
|
|
515
504
|
|
|
516
505
|
def int_scale(val: int, val_range: int, out_range: int) -> int:
|
|
@@ -534,7 +523,7 @@ def int_scale(val: int, val_range: int, out_range: int) -> int:
|
|
|
534
523
|
return num // dem
|
|
535
524
|
|
|
536
525
|
|
|
537
|
-
class StoppingContext(
|
|
526
|
+
class StoppingContext(contextlib.AbstractContextManager["StoppingContext"]):
|
|
538
527
|
"""Context manager that calls ``stop`` on a given object on exit. Used to
|
|
539
528
|
make the ``start`` method on `MainLoop` and `BaseScreen` optionally act as
|
|
540
529
|
context managers.
|
urwid/version.py
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
# file generated by
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
|
5
|
+
|
|
3
6
|
TYPE_CHECKING = False
|
|
4
7
|
if TYPE_CHECKING:
|
|
5
|
-
from typing import Tuple
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
6
11
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
12
|
else:
|
|
8
13
|
VERSION_TUPLE = object
|
|
@@ -12,5 +17,5 @@ __version__: str
|
|
|
12
17
|
__version_tuple__: VERSION_TUPLE
|
|
13
18
|
version_tuple: VERSION_TUPLE
|
|
14
19
|
|
|
15
|
-
__version__ = version = '
|
|
16
|
-
__version_tuple__ = version_tuple = (
|
|
20
|
+
__version__ = version = '3.0.0'
|
|
21
|
+
__version_tuple__ = version_tuple = (3, 0, 0)
|
urwid/vterm.py
CHANGED
|
@@ -35,7 +35,6 @@ import termios
|
|
|
35
35
|
import time
|
|
36
36
|
import traceback
|
|
37
37
|
import typing
|
|
38
|
-
import warnings
|
|
39
38
|
from collections import deque
|
|
40
39
|
from contextlib import suppress
|
|
41
40
|
from dataclasses import dataclass
|
|
@@ -248,8 +247,7 @@ class TermCharset:
|
|
|
248
247
|
|
|
249
248
|
def apply_mapping(self, char: bytes) -> bytes:
|
|
250
249
|
if self._sgr_mapping or self._g[self.active] == "ibmpc":
|
|
251
|
-
dec_pos
|
|
252
|
-
if dec_pos >= 0:
|
|
250
|
+
if (dec_pos := DEC_SPECIAL_CHARS.find(char.decode("cp437"))) >= 0:
|
|
253
251
|
self.current = "0"
|
|
254
252
|
return ALT_DEC_SPECIAL_CHARS[dec_pos].encode("cp437")
|
|
255
253
|
|
|
@@ -259,39 +257,6 @@ class TermCharset:
|
|
|
259
257
|
return char
|
|
260
258
|
|
|
261
259
|
|
|
262
|
-
class TermScroller(list):
|
|
263
|
-
"""
|
|
264
|
-
List subclass that handles the terminal scrollback buffer,
|
|
265
|
-
truncating it as necessary.
|
|
266
|
-
"""
|
|
267
|
-
|
|
268
|
-
SCROLLBACK_LINES = 10000
|
|
269
|
-
|
|
270
|
-
def __init__(self, iterable: Iterable[typing.Any]) -> None:
|
|
271
|
-
warnings.warn(
|
|
272
|
-
"`TermScroller` is deprecated. Please use `collections.deque` with non-zero `maxlen` instead.",
|
|
273
|
-
DeprecationWarning,
|
|
274
|
-
stacklevel=3,
|
|
275
|
-
)
|
|
276
|
-
super().__init__(iterable)
|
|
277
|
-
|
|
278
|
-
def trunc(self) -> None:
|
|
279
|
-
if len(self) >= self.SCROLLBACK_LINES:
|
|
280
|
-
self.pop(0)
|
|
281
|
-
|
|
282
|
-
def append(self, obj) -> None:
|
|
283
|
-
self.trunc()
|
|
284
|
-
super().append(obj)
|
|
285
|
-
|
|
286
|
-
def insert(self, idx: typing.SupportsIndex, obj) -> None:
|
|
287
|
-
self.trunc()
|
|
288
|
-
super().insert(idx, obj)
|
|
289
|
-
|
|
290
|
-
def extend(self, seq) -> None:
|
|
291
|
-
self.trunc()
|
|
292
|
-
super().extend(seq)
|
|
293
|
-
|
|
294
|
-
|
|
295
260
|
class TermCanvas(Canvas):
|
|
296
261
|
cacheable = False
|
|
297
262
|
|
|
@@ -375,7 +340,7 @@ class TermCanvas(Canvas):
|
|
|
375
340
|
lines = self.height // 2
|
|
376
341
|
|
|
377
342
|
if not up:
|
|
378
|
-
lines = -lines
|
|
343
|
+
lines = -lines
|
|
379
344
|
|
|
380
345
|
maxscroll = len(self.scrollback_buffer)
|
|
381
346
|
self.scrolling_up += lines
|
|
@@ -489,8 +454,7 @@ class TermCanvas(Canvas):
|
|
|
489
454
|
continue
|
|
490
455
|
|
|
491
456
|
# adjust x axis of scrollback buffer to the current width
|
|
492
|
-
padding
|
|
493
|
-
if padding > 0:
|
|
457
|
+
if (padding := self.width - len(last_line)) > 0:
|
|
494
458
|
last_line += [self.empty_char()] * padding
|
|
495
459
|
else:
|
|
496
460
|
last_line = last_line[: self.width]
|
|
@@ -549,8 +513,7 @@ class TermCanvas(Canvas):
|
|
|
549
513
|
|
|
550
514
|
escbuf.append(num)
|
|
551
515
|
|
|
552
|
-
cmd_
|
|
553
|
-
if cmd_ is not None:
|
|
516
|
+
if (cmd_ := CSI_COMMANDS[char]) is not None:
|
|
554
517
|
if isinstance(cmd_, CSIAlias):
|
|
555
518
|
csi_cmd: CSICommand = CSI_COMMANDS[cmd_.alias] # type: ignore[assignment]
|
|
556
519
|
elif isinstance(cmd_, CSICommand):
|
|
@@ -699,11 +662,13 @@ class TermCanvas(Canvas):
|
|
|
699
662
|
|
|
700
663
|
# end multibyte sequence
|
|
701
664
|
self.utf8_eat_bytes = None
|
|
702
|
-
|
|
703
|
-
if
|
|
665
|
+
|
|
666
|
+
if sequence := (self.utf8_buffer + bytes([byte])).decode("utf-8", "ignore"):
|
|
667
|
+
char = sequence.encode(util.get_encoding(), "replace")
|
|
668
|
+
|
|
669
|
+
else:
|
|
704
670
|
# invalid multibyte sequence, stop processing
|
|
705
671
|
return
|
|
706
|
-
char = sequence.encode(util.get_encoding(), "replace")
|
|
707
672
|
else:
|
|
708
673
|
self.utf8_eat_bytes = None
|
|
709
674
|
char = bytes([byte])
|
|
@@ -1118,6 +1083,14 @@ class TermCanvas(Canvas):
|
|
|
1118
1083
|
elif 40 <= attr <= 47:
|
|
1119
1084
|
bg = attr - 40
|
|
1120
1085
|
colors = max(16, colors)
|
|
1086
|
+
# AIXTERM bright color spec
|
|
1087
|
+
# https://en.wikipedia.org/wiki/ANSI_escape_code
|
|
1088
|
+
elif 90 <= attr <= 97:
|
|
1089
|
+
fg = attr - 90 + 8
|
|
1090
|
+
colors = max(16, colors)
|
|
1091
|
+
elif 100 <= attr <= 107:
|
|
1092
|
+
bg = attr - 100 + 8
|
|
1093
|
+
colors = max(16, colors)
|
|
1121
1094
|
elif attr in {38, 48}:
|
|
1122
1095
|
if idx + 2 < len(attrs) and attrs[idx + 1] == 5:
|
|
1123
1096
|
# 8 bit color specification
|
|
@@ -1264,7 +1237,7 @@ class TermCanvas(Canvas):
|
|
|
1264
1237
|
for x in range(self.width):
|
|
1265
1238
|
char = self.term[y][x]
|
|
1266
1239
|
attrs = self.reverse_attrspec(char[0], undo=undo)
|
|
1267
|
-
self.term[y][x] = (attrs,
|
|
1240
|
+
self.term[y][x] = (attrs, *char[1:])
|
|
1268
1241
|
|
|
1269
1242
|
def set_mode(
|
|
1270
1243
|
self,
|