Skip to content

Support MouseScrollLeft and MouseScrollRight control sequences. #5995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

fancidev
Copy link
Contributor

@fancidev fancidev commented Jul 27, 2025

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()

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.

1 participant