Skip to content
Open
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
34 changes: 32 additions & 2 deletions Lib/idlelib/idle_test/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import tkinter
from tkinter import EventType
from idlelib import util
from idlelib.multicall import MC_SHIFT, MC_CONTROL
from idlelib.idle_test.mock_tk import Event


Expand Down Expand Up @@ -72,18 +73,27 @@ class WheelEventTest(unittest.TestCase):

# An unmapped widget has no height and does not scroll by lines,
# so record the yview call instead of a real scroll.
def event(self, event_type, delta=0, num='??'):
def event(self, event_type, delta=0, num='??', state=0):
# Tk leaves num '??' for a wheel event and delta 0 for a button.
return Event(type=event_type, delta=delta, num=num,
return Event(type=event_type, delta=delta, num=num, state=state,
widget=mock.Mock())

def scroll(self, event, widget=None):
"Return the arguments of the yview call."
self.assertEqual(util.wheel_event(event, widget), 'break')
scrolled = event.widget if widget is None else widget
scrolled.yview.assert_called_once()
scrolled.xview.assert_not_called()
return scrolled.yview.call_args.args

def hscroll(self, event, widget=None):
"Return the arguments of the xview call."
self.assertEqual(util.wheel_event(event, widget), 'break')
scrolled = event.widget if widget is None else widget
scrolled.xview.assert_called_once()
scrolled.yview.assert_not_called()
return scrolled.xview.call_args.args

def test_mousewheel(self):
# Delta is positive for up on all systems.
for delta in 120, 1, 1200:
Expand All @@ -100,6 +110,26 @@ def test_buttons(self):
self.assertEqual(self.scroll(self.event(EventType.ButtonPress, num=5)),
('scroll', 5, 'units'))

def test_horizontal(self):
# The Shift modifier means horizontal.
for delta in 120, 1, 1200:
self.assertEqual(self.hscroll(self.event(EventType.MouseWheel,
delta, state=MC_SHIFT)),
('scroll', -5, 'units'))
self.assertEqual(self.hscroll(self.event(EventType.MouseWheel,
-delta, state=MC_SHIFT)),
('scroll', 5, 'units'))
self.assertEqual(self.hscroll(self.event(EventType.ButtonPress,
num=4, state=MC_SHIFT)),
('scroll', -5, 'units'))
self.assertEqual(self.hscroll(self.event(EventType.ButtonPress,
num=5, state=MC_SHIFT)),
('scroll', 5, 'units'))
# Other modifiers alone do not.
self.assertEqual(self.scroll(self.event(EventType.MouseWheel,
120, state=MC_CONTROL)),
('scroll', -5, 'units'))

def test_widget_argument(self):
# A tree label scrolls the canvas, not itself.
event = self.event(EventType.MouseWheel, 120)
Expand Down
16 changes: 13 additions & 3 deletions Lib/idlelib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"""
import sys

from idlelib.multicall import MC_SHIFT

# .pyw is for Windows; .pyi is for typing stub files.
# The extension order is needed for iomenu open/save dialogs.
py_extensions = ('.py', '.pyw', '.pyi')
Expand Down Expand Up @@ -101,16 +103,24 @@ def wheel_event(event, widget=None):
A <Button-4> or <Button-5> event (see x11_buttons) says up or down
by its number, and has no delta; a wheel event has no number.

If the Shift modifier is set in event.state, scroll horizontally.
Tk 9 reports horizontal scrolling, as with a touchpad, as a
<Shift-MouseWheel> event on all systems, and Tk 8.6.10 and newer
on X11 report it as a <Shift-Button-4> or <Shift-Button-5> event.

The widget parameter is needed so tree label bindings can pass the
underlying canvas. If tree is replaced by ttk.Treeview, it can go.

This function depends on widget.yview to not be overridden by
a subclass.
This function depends on widget.xview and widget.yview to not be
overridden by a subclass.
"""
up = event.num == 4 if event.num in (4, 5) else event.delta > 0
lines = -5 if up else 5
widget = event.widget if widget is None else widget
widget.yview('scroll', lines, 'units')
if event.state & MC_SHIFT:
widget.xview('scroll', lines, 'units')
else:
widget.yview('scroll', lines, 'units')
return 'break'


Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,7 @@ John Tromp
Diane Trout
Jason Trowbridge
Steven Troxler
Daniel Tsvetkov
Brent Tubbs
Anthony Tuininga
Erno Tukia
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed trackpad scrolling direction in :mod:`IDLE <idlelib>`
Loading