Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/events/click.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ See [MouseEvent][textual.events.MouseEvent] for the list of properties and metho
- [MouseEvent][textual.events.MouseEvent]
- [MouseMove](mouse_move.md)
- [MouseScrollDown](mouse_scroll_down.md)
- [MouseScrollLeft](mouse_scroll_left.md)
- [MouseScrollRight](mouse_scroll_right.md)
- [MouseScrollUp](mouse_scroll_up.md)
- [MouseUp](mouse_up.md)
2 changes: 2 additions & 0 deletions docs/events/enter.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@
- [MouseDown](mouse_down.md)
- [MouseMove](mouse_move.md)
- [MouseScrollDown](mouse_scroll_down.md)
- [MouseScrollLeft](mouse_scroll_left.md)
- [MouseScrollRight](mouse_scroll_right.md)
- [MouseScrollUp](mouse_scroll_up.md)
- [MouseUp](mouse_up.md)
2 changes: 2 additions & 0 deletions docs/events/leave.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@
- [MouseDown](mouse_down.md)
- [MouseMove](mouse_move.md)
- [MouseScrollDown](mouse_scroll_down.md)
- [MouseScrollLeft](mouse_scroll_left.md)
- [MouseScrollRight](mouse_scroll_right.md)
- [MouseScrollUp](mouse_scroll_up.md)
- [MouseUp](mouse_up.md)
2 changes: 2 additions & 0 deletions docs/events/mouse_down.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ See [MouseEvent][textual.events.MouseEvent] for the full list of properties and
- [MouseEvent][textual.events.MouseEvent]
- [MouseMove](mouse_move.md)
- [MouseScrollDown](mouse_scroll_down.md)
- [MouseScrollLeft](mouse_scroll_left.md)
- [MouseScrollRight](mouse_scroll_right.md)
- [MouseScrollUp](mouse_scroll_up.md)
- [MouseUp](mouse_up.md)
2 changes: 2 additions & 0 deletions docs/events/mouse_move.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ See [MouseEvent][textual.events.MouseEvent] for the full list of properties and
- [MouseDown](mouse_down.md)
- [MouseEvent][textual.events.MouseEvent]
- [MouseScrollDown](mouse_scroll_down.md)
- [MouseScrollLeft](mouse_scroll_left.md)
- [MouseScrollRight](mouse_scroll_right.md)
- [MouseScrollUp](mouse_scroll_up.md)
- [MouseUp](mouse_up.md)
2 changes: 2 additions & 0 deletions docs/events/mouse_scroll_down.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ See [MouseEvent][textual.events.MouseEvent] for the full list of properties and
- [MouseDown](mouse_down.md)
- [MouseEvent][textual.events.MouseEvent]
- [MouseMove](mouse_move.md)
- [MouseScrollLeft](mouse_scroll_left.md)
- [MouseScrollRight](mouse_scroll_right.md)
- [MouseScrollUp](mouse_scroll_up.md)
- [MouseUp](mouse_up.md)
22 changes: 22 additions & 0 deletions docs/events/mouse_scroll_left.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: MouseScrollLeft
---

::: textual.events.MouseScrollLeft
options:
heading_level: 1

See [MouseEvent][textual.events.MouseEvent] for the full list of properties and methods.

## See also

- [Click](click.md)
- [Enter](enter.md)
- [Leave](leave.md)
- [MouseDown](mouse_down.md)
- [MouseEvent][textual.events.MouseEvent]
- [MouseMove](mouse_move.md)
- [MouseScrollDown](mouse_scroll_down.md)
- [MouseScrollRight](mouse_scroll_right.md)
- [MouseScrollUp](mouse_scroll_up.md)
- [MouseUp](mouse_up.md)
22 changes: 22 additions & 0 deletions docs/events/mouse_scroll_right.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: MouseScrollRight
---

::: textual.events.MouseScrollRight
options:
heading_level: 1

See [MouseEvent][textual.events.MouseEvent] for the full list of properties and methods.

## See also

- [Click](click.md)
- [Enter](enter.md)
- [Leave](leave.md)
- [MouseDown](mouse_down.md)
- [MouseEvent][textual.events.MouseEvent]
- [MouseMove](mouse_move.md)
- [MouseScrollDown](mouse_scroll_down.md)
- [MouseScrollLeft](mouse_scroll_left.md)
- [MouseScrollUp](mouse_scroll_up.md)
- [MouseUp](mouse_up.md)
2 changes: 2 additions & 0 deletions docs/events/mouse_scroll_up.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ See [MouseEvent][textual.events.MouseEvent] for the full list of properties and
- [MouseEvent][textual.events.MouseEvent]
- [MouseMove](mouse_move.md)
- [MouseScrollDown](mouse_scroll_down.md)
- [MouseScrollLeft](mouse_scroll_left.md)
- [MouseScrollRight](mouse_scroll_right.md)
- [MouseUp](mouse_up.md)
2 changes: 2 additions & 0 deletions docs/events/mouse_up.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ See [MouseEvent][textual.events.MouseEvent] for the full list of properties and
- [MouseEvent][textual.events.MouseEvent]
- [MouseMove](mouse_move.md)
- [MouseScrollDown](mouse_scroll_down.md)
- [MouseScrollLeft](mouse_scroll_left.md)
- [MouseScrollRight](mouse_scroll_right.md)
- [MouseScrollUp](mouse_scroll_up.md)
2 changes: 2 additions & 0 deletions docs/guide/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ If you want your app to respond to a mouse click you should prefer the Click eve

Most mice have a scroll wheel which you can use to scroll the window underneath the cursor. Scrollable containers in Textual will handle these automatically, but you can handle [MouseScrollDown](../events/mouse_scroll_down.md) and [MouseScrollUp](../events/mouse_scroll_up.md) if you want build your own scrolling functionality.

For terminals that support horizontal mouse wheel, Textual sends [MouseScrollLeft](../events/mouse_scroll_left.md) and [MouseScrollRight](../events/mouse_scroll_right.md), and scrollable containers handle them automatically.

!!! information

Terminal emulators will typically convert trackpad gestures into scroll events.
9 changes: 6 additions & 3 deletions src/textual/_xterm_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,12 @@ def parse_mouse_code(self, code: str) -> Message | None:
event_class: type[events.MouseEvent]

if buttons & 64:
event_class = (
events.MouseScrollDown if buttons & 1 else events.MouseScrollUp
)
event_class = [
events.MouseScrollUp,
events.MouseScrollDown,
events.MouseScrollLeft,
events.MouseScrollRight,
][buttons & 3]
button = 0
else:
button = (buttons + 1) & 3
Expand Down
18 changes: 18 additions & 0 deletions src/textual/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,24 @@ class MouseScrollUp(MouseEvent, bubble=True, verbose=True):
"""


@rich.repr.auto
class MouseScrollRight(MouseEvent, bubble=True, verbose=True):
"""Sent when the mouse wheel is scrolled *right*.

- [X] Bubbles
- [X] Verbose
"""


@rich.repr.auto
class MouseScrollLeft(MouseEvent, bubble=True, verbose=True):
"""Sent when the mouse wheel is scrolled *left*.

- [X] Bubbles
- [X] Verbose
"""


class Click(MouseEvent, bubble=True):
"""Sent when a widget is clicked.

Expand Down
19 changes: 18 additions & 1 deletion src/textual/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,12 @@


_MOUSE_EVENTS_DISALLOW_IF_DISABLED = (events.MouseEvent, events.Enter, events.Leave)
_MOUSE_EVENTS_ALLOW_IF_DISABLED = (events.MouseScrollDown, events.MouseScrollUp)
_MOUSE_EVENTS_ALLOW_IF_DISABLED = (
events.MouseScrollDown,
events.MouseScrollUp,
events.MouseScrollRight,
events.MouseScrollLeft,
)


@rich.repr.auto
Expand Down Expand Up @@ -4569,6 +4574,18 @@ def _on_mouse_scroll_up(self, event: events.MouseScrollUp) -> None:
if self._scroll_up_for_pointer(animate=False):
event.stop()

def _on_mouse_scroll_right(self, event: events.MouseScrollRight) -> None:
if self.allow_horizontal_scroll:
self.release_anchor()
if self._scroll_right_for_pointer(animate=False):
event.stop()

def _on_mouse_scroll_left(self, event: events.MouseScrollLeft) -> None:
if self.allow_horizontal_scroll:
self.release_anchor()
if self._scroll_left_for_pointer(animate=False):
event.stop()

def _on_scroll_to(self, message: ScrollTo) -> None:
if self._allow_scroll:
self.release_anchor()
Expand Down
52 changes: 52 additions & 0 deletions tests/test_xterm_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
MouseDown,
MouseMove,
MouseScrollDown,
MouseScrollLeft,
MouseScrollRight,
MouseScrollUp,
MouseUp,
Paste,
Expand Down Expand Up @@ -283,6 +285,56 @@ def test_mouse_scroll_down(parser, sequence, shift, meta):
assert event.meta is meta


@pytest.mark.parametrize(
"sequence, shift, meta",
[
("\x1b[<66;18;25M", False, False),
("\x1b[<70;18;25M", True, False),
("\x1b[<74;18;25M", False, True),
],
)
def test_mouse_scroll_left(parser, sequence, shift, meta):
"""Scrolling the mouse with and without modifiers held down.
We don't currently capture modifier keys in scroll events.
"""
events = list(parser.feed(sequence))

assert len(events) == 1

event = events[0]

assert isinstance(event, MouseScrollLeft)
assert event.x == 17
assert event.y == 24
assert event.shift is shift
assert event.meta is meta


@pytest.mark.parametrize(
"sequence, shift, meta",
[
("\x1b[<67;18;25M", False, False),
("\x1b[<71;18;25M", True, False),
("\x1b[<75;18;25M", False, True),
],
)
def test_mouse_scroll_right(parser, sequence, shift, meta):
"""Scrolling the mouse with and without modifiers held down.
We don't currently capture modifier keys in scroll events.
"""
events = list(parser.feed(sequence))

assert len(events) == 1

event = events[0]

assert isinstance(event, MouseScrollRight)
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):
# I don't know if this can actually happen in reality, but
# there's a branch in the code that allows for the possibility.
Expand Down
Loading