From e13ea5e0bd98a9f938b23abdab6bdc61f9283409 Mon Sep 17 00:00:00 2001 From: Nitzan Shaked Date: Fri, 30 Dec 2022 18:44:09 +0200 Subject: [PATCH] MouseScrollUp/MouseScrollDown => plain MousEvent's ... which means they get passesd x, y, etc. In particular, they are passed the keyboard modifiers. This allows widgets to use e.g. ctrl-wheel to scroll right/left. --- src/textual/_xterm_parser.py | 47 ++++++++++++++++++------------------ src/textual/events.py | 20 +++++---------- tests/test_xterm_parser.py | 24 ++++++++++-------- 3 files changed, 43 insertions(+), 48 deletions(-) diff --git a/src/textual/_xterm_parser.py b/src/textual/_xterm_parser.py index 40af686a4..d80489e84 100644 --- a/src/textual/_xterm_parser.py +++ b/src/textual/_xterm_parser.py @@ -54,36 +54,35 @@ class XTermParser(Parser[events.Event]): if sgr_match: _buttons, _x, _y, state = sgr_match.groups() buttons = int(_buttons) - button = (buttons + 1) & 3 x = int(_x) - 1 y = int(_y) - 1 delta_x = x - self.last_x delta_y = y - self.last_y self.last_x = x self.last_y = y - event: events.Event - if buttons & 64: - event = ( - events.MouseScrollUp if button == 1 else events.MouseScrollDown - )(sender, x, y) - else: - event = ( - events.MouseMove - if buttons & 32 - else (events.MouseDown if state == "M" else events.MouseUp) - )( - sender, - x, - y, - delta_x, - delta_y, - button, - bool(buttons & 4), - bool(buttons & 8), - bool(buttons & 16), - screen_x=x, - screen_y=y, - ) + event_cls = ( + (events.MouseScrollDown if buttons & 1 else events.MouseScrollUp) + if buttons & 64 + else events.MouseMove + if buttons & 32 + else (events.MouseDown if state == "M" else events.MouseUp) + ) + if event_cls in (events.MouseScrollUp, events.MouseScrollDown): + buttons &= ~(64 | 3) + button = (buttons + 1) & 3 + event = event_cls( + sender, + x, + y, + delta_x, + delta_y, + button, + bool(buttons & 4), + bool(buttons & 8), + bool(buttons & 16), + screen_x=x, + screen_y=y, + ) return event return None diff --git a/src/textual/events.py b/src/textual/events.py index 1f509a072..e99007e21 100644 --- a/src/textual/events.py +++ b/src/textual/events.py @@ -419,22 +419,14 @@ class MouseUp(MouseEvent, bubble=True, verbose=True): pass -class MouseScrollDown(InputEvent, bubble=True, verbose=True): - __slots__ = ["x", "y"] - - def __init__(self, sender: MessageTarget, x: int, y: int) -> None: - super().__init__(sender) - self.x = x - self.y = y +@rich.repr.auto +class MouseScrollDown(MouseEvent, bubble=True): + pass -class MouseScrollUp(InputEvent, bubble=True, verbose=True): - __slots__ = ["x", "y"] - - def __init__(self, sender: MessageTarget, x: int, y: int) -> None: - super().__init__(sender) - self.x = x - self.y = y +@rich.repr.auto +class MouseScrollUp(MouseEvent, bubble=True): + pass class Click(MouseEvent, bubble=True): diff --git a/tests/test_xterm_parser.py b/tests/test_xterm_parser.py index 6e352d87d..a8a951b02 100644 --- a/tests/test_xterm_parser.py +++ b/tests/test_xterm_parser.py @@ -236,14 +236,14 @@ def test_mouse_move(parser, sequence, shift, meta, button): @pytest.mark.parametrize( - "sequence", + "sequence, shift, meta", [ - "\x1b[<64;18;25M", - "\x1b[<68;18;25M", - "\x1b[<72;18;25M", + ("\x1b[<64;18;25M", False, False), + ("\x1b[<68;18;25M", True, False), + ("\x1b[<72;18;25M", False, True), ], ) -def test_mouse_scroll_up(parser, sequence): +def test_mouse_scroll_up(parser, sequence, shift, meta): """Scrolling the mouse with and without modifiers held down. We don't currently capture modifier keys in scroll events. """ @@ -256,17 +256,19 @@ def test_mouse_scroll_up(parser, sequence): assert isinstance(event, MouseScrollUp) assert event.x == 17 assert event.y == 24 + assert event.shift is shift + assert event.meta is meta @pytest.mark.parametrize( - "sequence", + "sequence, shift, meta", [ - "\x1b[<65;18;25M", - "\x1b[<69;18;25M", - "\x1b[<73;18;25M", + ("\x1b[<65;18;25M", False, False), + ("\x1b[<69;18;25M", True, False), + ("\x1b[<73;18;25M", False, True), ], ) -def test_mouse_scroll_down(parser, sequence): +def test_mouse_scroll_down(parser, sequence, shift, meta): events = list(parser.feed(sequence)) assert len(events) == 1 @@ -276,6 +278,8 @@ def test_mouse_scroll_down(parser, sequence): assert isinstance(event, MouseScrollDown) assert event.x == 17 assert event.y == 24 + assert event.shift is shift + assert event.meta is meta def test_mouse_event_detected_but_info_not_parsed(parser):