Skip to content

Support MouseScrollLeft and MouseScrollRight control sequences.#5995

Merged
willmcgugan merged 2 commits into
Textualize:mainfrom
fancidev:mouse
Jul 31, 2025
Merged

Support MouseScrollLeft and MouseScrollRight control sequences.#5995
willmcgugan merged 2 commits into
Textualize:mainfrom
fancidev:mouse

Conversation

@fancidev

@fancidev fancidev commented Jul 27, 2025

Copy link
Copy Markdown
Contributor

Background

Horizontal scrolling is useful in certain scenarios, such as browsing a DataTable with many rows and columns or moving around in a map in all four directions. Since most input devices do not have a dedicated horizontal scroll wheel, most terminal emulators only send control sequences for vertical (but not horizontal) scroll. Textual performs horizontal scrolling when a vertical scroll sequence is received with the CTRL or SHIFT key pressed; see source code.

Some terminal emulators do send a specific control sequence on horizontal mouse scrolling. For example, iTerm2 sends ^[[66;x;yM for scroll-left and ^[[67;x;yM for scroll-right, with x and y replaced by the cursor coordinates. These control codes are left unspecified under xterm when the mouse wheel is in action. Most terminal emulators, such as Windows Terminal or MacOS Terminal, do not send these codes.

At the moment, Textual ignores the second lowest bit of the control code when the mouse wheel is in action. Consequently, for a terminal emulator that sends 66 or 67, scroll-left (66) is treated as scroll-up (64) and scroll-right (67) is treated as scroll-down (65); see source code.

Change

This PR adds support for control codes 66 and 67. When these control codes are received and the mouse wheel is in action, the MouseScrollLeft or MouseScrollRight event is sent accordingly. These events are then handled by scrollable containers to perform horizontal scrolling.

Checklist

  • Docstrings on all new or modified functions / classes
  • Updated documentation
  • Updated CHANGELOG.md (skipped -- probably better to update at release time)

Demo

The following script creates a DataTable with many rows and columns. Run the script under iTerm2 (MacOS only) and "scroll" the Magic Mouse in all four directions to have the DataTable scroll accordingly.

from textual.app import App, ComposeResult
from textual.widgets import DataTable

class MyApp(App):
    def compose(self) -> ComposeResult:
        table = DataTable()
        table.add_columns(*[f"Column{j}" for j in range(100)])
        for i in range(100):
            table.add_rows([[f"Cell({i},{j})" for j in range(100)]])
        yield table

if __name__ == "__main__":
    MyApp().run()

@willmcgugan willmcgugan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Appreciate the thoroughness with the docs.

@willmcgugan willmcgugan merged commit 335e045 into Textualize:main Jul 31, 2025
23 checks passed
@fancidev fancidev deleted the mouse branch July 31, 2025 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants