diff options
Diffstat (limited to 'contrib/python/textual')
| -rw-r--r-- | contrib/python/textual/.dist-info/METADATA | 2 | ||||
| -rw-r--r-- | contrib/python/textual/textual/_compositor.py | 7 | ||||
| -rw-r--r-- | contrib/python/textual/textual/_xterm_parser.py | 86 | ||||
| -rw-r--r-- | contrib/python/textual/textual/pilot.py | 2 | ||||
| -rw-r--r-- | contrib/python/textual/textual/screen.py | 15 | ||||
| -rw-r--r-- | contrib/python/textual/textual/widgets/_input.py | 12 | ||||
| -rw-r--r-- | contrib/python/textual/textual/widgets/_text_area.py | 6 | ||||
| -rw-r--r-- | contrib/python/textual/ya.make | 2 |
8 files changed, 85 insertions, 47 deletions
diff --git a/contrib/python/textual/.dist-info/METADATA b/contrib/python/textual/.dist-info/METADATA index f3bb97ae79a..bdcd6666e66 100644 --- a/contrib/python/textual/.dist-info/METADATA +++ b/contrib/python/textual/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: textual -Version: 8.2.7 +Version: 8.2.8 Summary: Modern Text User Interface framework License: MIT License-File: LICENSE diff --git a/contrib/python/textual/textual/_compositor.py b/contrib/python/textual/textual/_compositor.py index ee4c231f240..9af38a23c7e 100644 --- a/contrib/python/textual/textual/_compositor.py +++ b/contrib/python/textual/textual/_compositor.py @@ -923,11 +923,8 @@ class Compositor: x -= region.x + gutter_left y -= region.y + gutter_right - if y < 0: - return None, None - - if x < 0: - return widget, Offset(0, y) + if x < 0 or y < 0: + return widget, Offset(max(0, x), max(0, y)) visible_screen_stack.set(widget.app._background_screens) line = widget.render_line(y) diff --git a/contrib/python/textual/textual/_xterm_parser.py b/contrib/python/textual/textual/_xterm_parser.py index eafa7414ecb..df9112370cd 100644 --- a/contrib/python/textual/textual/_xterm_parser.py +++ b/contrib/python/textual/textual/_xterm_parser.py @@ -39,7 +39,7 @@ SPECIAL_SEQUENCES = {BRACKETED_PASTE_START, BRACKETED_PASTE_END, FOCUSIN, FOCUSO """Set of special sequences.""" _re_extended_key: Final[re.Pattern[str]] = re.compile( - r"\x1b\[((?:\d*;?){2,3})([u~ABCDEFHPQRS])" + r"\x1b\[((?:[\d:]*;?){2,3})([u~ABCDEFHPQRS])" ) _re_in_band_window_resize: Final[re.Pattern[str]] = re.compile( r"\x1b\[48;(\d+(?:\:.*?)?);(\d+(?:\:.*?)?);(\d+(?:\:.*?)?);(\d+(?:\:.*?)?)t" @@ -334,8 +334,26 @@ class XTermParser(Parser[Message]): self._debug_log_file.close() self._debug_log_file = None + @classmethod + def _parse_colon_codepoints(cls, text_str: str) -> list[str | None]: + """Convert codepoints split on colons in to a list of characters. + + Args: + text_str: String with groups of digits, separated by one or more colons. + + Returns: + A list of characters. + """ + if not text_str: + return [None] + characters: list[str | None] = [ + chr(int(part)) if part.isdecimal() else chr(1) + for part in text_str.split(":") + ] + return characters + @lru_cache(maxsize=1024) - def _parse_extended_key(self, sequence: str) -> events.Key | None: + def _parse_extended_key(self, sequence: str) -> list[events.Key] | None: """Parse a Kitty sequence. Args: @@ -348,38 +366,47 @@ class XTermParser(Parser[Message]): if (match := _re_extended_key.fullmatch(sequence)) is None: return None + key_events: list[events.Key] = [] + codes, end = match.groups(default="") codepoint_str, modifiers_str, text_str, *_ = codes.split(";") + ["", "", ""] - codepoint = int(codepoint_str or "1") modifiers = int(modifiers_str or "0") - text = chr(int(text_str)) if text_str else None - if not (key := FUNCTIONAL_KEYS.get(f"{codepoint}{end}", "")): - key = _character_to_key(text if text else chr(codepoint)) + for text in self._parse_colon_codepoints(text_str): + if not (key := FUNCTIONAL_KEYS.get(f"{codepoint}{end}", "")): + key = _character_to_key(text if text else chr(codepoint)) - key_tokens: list[str] = [] - # The modifier is redundant on a modifier key - if modifiers and key not in MODIFIER_FUNCTIONAL_KEYS and text_str is not None: - modifier_bits = int(modifiers) - 1 - # Not convinced of the utility in reporting caps_lock and num_lock - MODIFIERS = ("alt", "ctrl", "super", "hyper", "meta") - # Ignore caps_lock and num_lock modifiers - if modifier_bits & 1 and (text is None or text.isspace()): - key_tokens.append("shift") - for bit, modifier in enumerate(MODIFIERS, 1): - if modifier == "alt" and text is not None: - continue - if modifier_bits & (1 << bit): - key_tokens.append(modifier) + key_tokens: list[str] = [] + # The modifier is redundant on a modifier key + if ( + modifiers + and key not in MODIFIER_FUNCTIONAL_KEYS + and text_str is not None + ): + modifier_bits = int(modifiers) - 1 + # Not convinced of the utility in reporting caps_lock and num_lock + MODIFIERS = ("alt", "ctrl", "super", "hyper", "meta") + # Ignore caps_lock and num_lock modifiers + if modifier_bits & 1 and (text is None or text.isspace()): + key_tokens.append("shift") + for bit, modifier in enumerate(MODIFIERS, 1): + if modifier == "alt" and text is not None: + continue + if modifier_bits & (1 << bit): + key_tokens.append(modifier) - key_tokens.sort() - if key is not None: - key_tokens.append(key) - return events.Key( - "+".join(key_tokens), - text or (None if modifiers else SPECIAL_KEY_TO_CHARACTER.get(key, None)), - ) + key_tokens.sort() + if key is not None: + key_tokens.append(key) + key_events.append( + events.Key( + "+".join(key_tokens), + text + or (None if modifiers else SPECIAL_KEY_TO_CHARACTER.get(key, None)), + ) + ) + return key_events def _sequence_to_key_events( self, sequence: str, alt: bool = False @@ -395,9 +422,10 @@ class XTermParser(Parser[Message]): if ( not constants.DISABLE_KITTY_KEY - and (key := self._parse_extended_key(sequence)) is not None + and (keys := self._parse_extended_key(sequence)) is not None ): - yield key.copy() + for key in keys: + yield key.copy() return keys = ANSI_SEQUENCES_KEYS.get(sequence) diff --git a/contrib/python/textual/textual/pilot.py b/contrib/python/textual/textual/pilot.py index c387b258276..76adae9d594 100644 --- a/contrib/python/textual/textual/pilot.py +++ b/contrib/python/textual/textual/pilot.py @@ -438,7 +438,7 @@ class Pilot(Generic[ReturnType]): ) offset = Offset(message_arguments["x"], message_arguments["y"]) - if offset not in screen.region: + if offset not in screen.size.region: raise OutOfBounds( "Target offset is outside of currently-visible screen region." ) diff --git a/contrib/python/textual/textual/screen.py b/contrib/python/textual/textual/screen.py index af9070538e7..c8b1d1462d4 100644 --- a/contrib/python/textual/textual/screen.py +++ b/contrib/python/textual/textual/screen.py @@ -1838,8 +1838,11 @@ class Screen(Generic[ScreenResultType], Widget): if select_offset is not None: content_widget = select_widget content_offset = select_offset - assert isinstance(content_widget.parent, Widget) - container = content_widget.parent + container = ( + content_widget + if isinstance(content_widget, Screen) + else content_widget.parent + ) else: content_widget = None container = select_widget @@ -1884,6 +1887,7 @@ class Screen(Generic[ScreenResultType], Widget): select_widget, select_offset = self.get_widget_and_offset_at( event.x, event.y ) + if ( select_widget is not None and select_widget.allow_select @@ -1893,8 +1897,11 @@ class Screen(Generic[ScreenResultType], Widget): if select_offset is not None: content_widget = select_widget content_offset = select_offset - assert isinstance(content_widget.parent, Widget) - container = content_widget.parent + container = ( + content_widget + if isinstance(content_widget, Screen) + else content_widget.parent + ) else: content_widget = None container = select_widget diff --git a/contrib/python/textual/textual/widgets/_input.py b/contrib/python/textual/textual/widgets/_input.py index 3aa2a945bdb..bcf2c2994de 100644 --- a/contrib/python/textual/textual/widgets/_input.py +++ b/contrib/python/textual/textual/widgets/_input.py @@ -122,9 +122,14 @@ class Input(ScrollView): Binding( "ctrl+w", "delete_left_word", "Delete left to start of word", show=False ), - Binding("ctrl+u", "delete_left_all", "Delete all to the left", show=False), Binding( - "ctrl+backspace", + "ctrl+u,super+backspace", + "delete_left_all", + "Delete all to the left", + show=False, + ), + Binding( + "ctrl+backspace,alt+backspace", "delete_right_word", "Delete right to start of word", show=False, @@ -145,6 +150,7 @@ class Input(ScrollView): | shift+right | Move cursor right and select. | | ctrl+right | Move the cursor one word to the right. | | backspace | Delete the character to the left of the cursor. | + | ctrl+backspace,alt+backspace | Delete right to start of word. | | ctrl+shift+right | Move cursor right a word and select. | | ctrl+shift+a | Select all text in the input. | | home,ctrl+a | Go to the beginning of the input. | @@ -154,7 +160,7 @@ class Input(ScrollView): | delete,ctrl+d | Delete the character to the right of the cursor. | | enter | Submit the current value of the input. | | ctrl+w | Delete the word to the left of the cursor. | - | ctrl+u | Delete everything to the left of the cursor. | + | ctrl+u,super+backspace | Delete everything to the left of the cursor. | | ctrl+f | Delete the word to the right of the cursor. | | ctrl+k | Delete everything to the right of the cursor. | | ctrl+x | Cut selected text. | diff --git a/contrib/python/textual/textual/widgets/_text_area.py b/contrib/python/textual/textual/widgets/_text_area.py index d970a31c5a7..1ca74675e54 100644 --- a/contrib/python/textual/textual/widgets/_text_area.py +++ b/contrib/python/textual/textual/widgets/_text_area.py @@ -354,7 +354,7 @@ TextArea { show=False, ), Binding( - "ctrl+w,ctrl+backspace", + "ctrl+w,ctrl+backspace,alt+backspace", "delete_word_left", "Delete left to start of word", show=False, @@ -390,7 +390,7 @@ TextArea { show=False, ), Binding( - "ctrl+u", + "ctrl+u,super+backspace", "delete_to_start_of_line", "Delete to line start", show=False, @@ -446,7 +446,7 @@ TextArea { | delete,ctrl+d | Delete character to the right of cursor. | | alt+delete | Delete from cursor to end of the word. | | ctrl+shift+k | Delete the current line. | - | ctrl+u | Delete from cursor to the start of the line. | + | ctrl+u,super+backspace | Delete from cursor to the start of the line. | | ctrl+k | Delete from cursor to the end of the line. | | f6 | Select the current line. | | f7 | Select all text in the document. | diff --git a/contrib/python/textual/ya.make b/contrib/python/textual/ya.make index 84a9205d17e..fd182d0171f 100644 --- a/contrib/python/textual/ya.make +++ b/contrib/python/textual/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(8.2.7) +VERSION(8.2.8) LICENSE(MIT) |
