Skip to content

Commit 2cd9025

Browse files
GeeTransitterryjreedy
authored andcommitted
bpo-37902: IDLE: Add scrolling for IDLE browsers. (#15368)
Modify the wheel event handler so it can also be used for module, path, and stack browsers. Patch by George Zhang.
1 parent 87bd207 commit 2cd9025

File tree

7 files changed

+78
-21
lines changed

7 files changed

+78
-21
lines changed

Lib/idlelib/NEWS.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ Released on 2019-10-20?
33
======================================
44

55

6+
bpo-37092: Add mousewheel scrolling for IDLE module, path, and stack
7+
browsers. Patch by George Zhang.
8+
69
bpo-35771: To avoid occasional spurious test_idle failures on slower
710
machines, increase the ``hover_delay`` in test_tooltip.
811

Lib/idlelib/editor.py

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from idlelib import query
2727
from idlelib import replace
2828
from idlelib import search
29+
from idlelib.tree import wheel_event
2930
from idlelib import window
3031

3132
# The default tab setting for a Text widget, in average-width characters.
@@ -151,9 +152,10 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
151152
else:
152153
# Elsewhere, use right-click for popup menus.
153154
text.bind("<3>",self.right_menu_event)
154-
text.bind('<MouseWheel>', self.mousescroll)
155-
text.bind('<Button-4>', self.mousescroll)
156-
text.bind('<Button-5>', self.mousescroll)
155+
156+
text.bind('<MouseWheel>', wheel_event)
157+
text.bind('<Button-4>', wheel_event)
158+
text.bind('<Button-5>', wheel_event)
157159
text.bind('<Configure>', self.handle_winconfig)
158160
text.bind("<<cut>>", self.cut)
159161
text.bind("<<copy>>", self.copy)
@@ -502,23 +504,6 @@ def handle_yview(self, event, *args):
502504
self.text.yview(event, *args)
503505
return 'break'
504506

505-
def mousescroll(self, event):
506-
"""Handle scrollwheel event.
507-
508-
For wheel up, event.delta = 120*n on Windows, -1*n on darwin,
509-
where n can be > 1 if one scrolls fast. Flicking the wheel
510-
generates up to maybe 20 events with n up to 10 or more 1.
511-
Macs use wheel down (delta = 1*n) to scroll up, so positive
512-
delta means to scroll up on both systems.
513-
514-
X-11 sends Control-Button-4 event instead.
515-
"""
516-
up = {EventType.MouseWheel: event.delta > 0,
517-
EventType.Button: event.num == 4}
518-
lines = -5 if up[event.type] else 5
519-
self.text.yview_scroll(lines, 'units')
520-
return 'break'
521-
522507
rmenu = None
523508

524509
def right_menu_event(self, event):

Lib/idlelib/idle_test/test_multicall.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ def test_init(self):
3535
mctext = self.mc(self.root)
3636
self.assertIsInstance(mctext._MultiCall__binders, list)
3737

38+
def test_yview(self):
39+
# Added for tree.wheel_event
40+
# (it depends on yview to not be overriden)
41+
mc = self.mc
42+
self.assertIs(mc.yview, Text.yview)
43+
mctext = self.mc(self.root)
44+
self.assertIs(mctext.yview.__func__, Text.yview)
45+
3846

3947
if __name__ == '__main__':
4048
unittest.main(verbosity=2)

Lib/idlelib/idle_test/test_tree.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import unittest
55
from test.support import requires
66
requires('gui')
7-
from tkinter import Tk
7+
from tkinter import Tk, EventType, SCROLL
88

99

1010
class TreeTest(unittest.TestCase):
@@ -29,5 +29,32 @@ def test_init(self):
2929
node.expand()
3030

3131

32+
class TestScrollEvent(unittest.TestCase):
33+
34+
def test_wheel_event(self):
35+
# Fake widget class containing `yview` only.
36+
class _Widget:
37+
def __init__(widget, *expected):
38+
widget.expected = expected
39+
def yview(widget, *args):
40+
self.assertTupleEqual(widget.expected, args)
41+
# Fake event class
42+
class _Event:
43+
pass
44+
# (type, delta, num, amount)
45+
tests = ((EventType.MouseWheel, 120, -1, -5),
46+
(EventType.MouseWheel, -120, -1, 5),
47+
(EventType.ButtonPress, -1, 4, -5),
48+
(EventType.ButtonPress, -1, 5, 5))
49+
50+
event = _Event()
51+
for ty, delta, num, amount in tests:
52+
event.type = ty
53+
event.delta = delta
54+
event.num = num
55+
res = tree.wheel_event(event, _Widget(SCROLL, amount, "units"))
56+
self.assertEqual(res, "break")
57+
58+
3259
if __name__ == '__main__':
3360
unittest.main(verbosity=2)

Lib/idlelib/tree.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,30 @@ def listicons(icondir=ICONDIR):
5656
column = 0
5757
root.images = images
5858

59+
def wheel_event(event, widget=None):
60+
"""Handle scrollwheel event.
61+
62+
For wheel up, event.delta = 120*n on Windows, -1*n on darwin,
63+
where n can be > 1 if one scrolls fast. Flicking the wheel
64+
generates up to maybe 20 events with n up to 10 or more 1.
65+
Macs use wheel down (delta = 1*n) to scroll up, so positive
66+
delta means to scroll up on both systems.
67+
68+
X-11 sends Control-Button-4,5 events instead.
69+
70+
The widget parameter is needed so browser label bindings can pass
71+
the underlying canvas.
72+
73+
This function depends on widget.yview to not be overridden by
74+
a subclass.
75+
"""
76+
up = {EventType.MouseWheel: event.delta > 0,
77+
EventType.ButtonPress: event.num == 4}
78+
lines = -5 if up[event.type] else 5
79+
widget = event.widget if widget is None else widget
80+
widget.yview(SCROLL, lines, 'units')
81+
return 'break'
82+
5983

6084
class TreeNode:
6185

@@ -260,6 +284,9 @@ def drawtext(self):
260284
anchor="nw", window=self.label)
261285
self.label.bind("<1>", self.select_or_edit)
262286
self.label.bind("<Double-1>", self.flip)
287+
self.label.bind("<MouseWheel>", lambda e: wheel_event(e, self.canvas))
288+
self.label.bind("<Button-4>", lambda e: wheel_event(e, self.canvas))
289+
self.label.bind("<Button-5>", lambda e: wheel_event(e, self.canvas))
263290
self.text_id = id
264291

265292
def select_or_edit(self, event=None):
@@ -410,6 +437,7 @@ def GetSubList(self):
410437
# A canvas widget with scroll bars and some useful bindings
411438

412439
class ScrolledCanvas:
440+
413441
def __init__(self, master, **opts):
414442
if 'yscrollincrement' not in opts:
415443
opts['yscrollincrement'] = 17
@@ -431,6 +459,9 @@ def __init__(self, master, **opts):
431459
self.canvas.bind("<Key-Next>", self.page_down)
432460
self.canvas.bind("<Key-Up>", self.unit_up)
433461
self.canvas.bind("<Key-Down>", self.unit_down)
462+
self.canvas.bind("<MouseWheel>", wheel_event)
463+
self.canvas.bind("<Button-4>", wheel_event)
464+
self.canvas.bind("<Button-5>", wheel_event)
434465
#if isinstance(master, Toplevel) or isinstance(master, Tk):
435466
self.canvas.bind("<Alt-Key-2>", self.zoom_height)
436467
self.canvas.focus_set()

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,7 @@ Nickolai Zeldovich
18711871
Yuxiao Zeng
18721872
Uwe Zessin
18731873
Cheng Zhang
1874+
George Zhang
18741875
Kai Zhu
18751876
Tarek Ziadé
18761877
Jelle Zijlstra
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add mousewheel scrolling for IDLE module, path, and stack browsers.
2+
Patch by George Zhang.

0 commit comments

Comments
 (0)