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
7 changes: 4 additions & 3 deletions Lib/idlelib/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
from idlelib import query
from idlelib import replace
from idlelib import search
from idlelib.tree import wheel_event
from idlelib.util import py_extensions
from idlelib import window
from idlelib.help import _get_dochome
from idlelib.util import x11_buttons, wheel_event

# The default tab setting for a Text widget, in average-width characters.
TK_TABWIDTH_DEFAULT = 8
Expand Down Expand Up @@ -115,10 +115,11 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
# Elsewhere, use right-click for popup menus.
text.bind("<3>",self.right_menu_event)

text.bind('<MouseWheel>', wheel_event)
if text._windowingsystem == 'x11':
if x11_buttons:
text.bind('<Button-4>', wheel_event)
text.bind('<Button-5>', wheel_event)
else:
text.bind('<MouseWheel>', wheel_event)
text.bind('<Configure>', self.handle_winconfig)
text.bind("<<cut>>", self.cut)
text.bind("<<copy>>", self.copy)
Expand Down
12 changes: 3 additions & 9 deletions Lib/idlelib/idle_test/test_sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
from idlelib.percolator import Percolator
import idlelib.pyshell
from idlelib.pyshell import PyShell, PyShellFileList
from idlelib.util import fix_scaling, fix_word_breaks, fix_x11_paste
from idlelib.util import (fix_scaling, fix_word_breaks, fix_x11_paste,
x11_buttons)
import idlelib.sidebar
from idlelib.sidebar import get_end_linenumber, get_lineno

Expand Down Expand Up @@ -685,21 +686,14 @@ def test_mousewheel(self):
self.do_input('x = """' + '\n'*100 + '"""\n')
yield
self.assertGreater(get_lineno(text, '@0,0'), 1)

last_lineno = get_end_linenumber(text)
self.assertIsNotNone(text.dlineinfo(text.index(f'{last_lineno}.0')))

# Simulate a mouse wheel notch. Tk 8.7 replaced the X11
# <Button-4>/<Button-5> wheel events with <MouseWheel> (whose delta is
# platform-dependent); older Tk on X11 still uses the button events.
x11_buttons = (sidebar.canvas._windowingsystem == 'x11'
and tk.TkVersion < 8.7)
delta = 1 if sidebar.canvas._windowingsystem == 'aqua' else 120

# Scroll up.
if x11_buttons:
sidebar.canvas.event_generate('<Button-4>', x=0, y=0)
else:
delta = 1 if sidebar.canvas._windowingsystem == 'aqua' else 120
sidebar.canvas.event_generate('<MouseWheel>', x=0, y=0, delta=delta)
yield
self.assertIsNone(text.dlineinfo(text.index(f'{last_lineno}.0')))
Expand Down
29 changes: 1 addition & 28 deletions Lib/idlelib/idle_test/test_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import unittest
from test.support import requires
requires('gui')
from tkinter import Tk, EventType, SCROLL
from tkinter import Tk


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


class TestScrollEvent(unittest.TestCase):

def test_wheel_event(self):
# Fake widget class containing `yview` only.
class _Widget:
def __init__(widget, *expected):
widget.expected = expected
def yview(widget, *args):
self.assertTupleEqual(widget.expected, args)
# Fake event class
class _Event:
pass
# (type, delta, num, amount)
tests = ((EventType.MouseWheel, 120, -1, -5),
(EventType.MouseWheel, -120, -1, 5),
(EventType.ButtonPress, -1, 4, -5),
(EventType.ButtonPress, -1, 5, 5))

event = _Event()
for ty, delta, num, amount in tests:
event.type = ty
event.delta = delta
event.num = num
res = tree.wheel_event(event, _Widget(SCROLL, amount, "units"))
self.assertEqual(res, "break")


if __name__ == '__main__':
unittest.main(verbosity=2)
38 changes: 37 additions & 1 deletion Lib/idlelib/idle_test/test_util.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Test util, coverage 100%"""
"""Test util, coverage %"""

import unittest
from idlelib import util
Expand All @@ -9,6 +9,42 @@ def test_extensions(self):
for extension in {'.pyi', '.py', '.pyw'}:
self.assertIn(extension, util.py_extensions)

class ScrollTest(unittest.TestCase):

@classmethod
def setUpClass(cls):
cls.orig_x11 = util.x11_buttons

@classmethod
def tearDownClass(cls):
util.x11_buttons = cls.orig_x11

def test_wheel_event(self):
# Fake widget class containing `yview` only.
class _Widget:
def yview(widget, scroll, lines, units):
self.assertTupleEqual((scroll, lines, units),
('scroll', widget.lines, 'units'))
# Fake event class
class _Event:
pass

event = _Event()
event.widget = widget = _Widget()
tests = ((False, 120, -5),
(False, -120, 5),
(True, 4, -5),
(True, 5, 5))
for x11, value, lines in tests:
util.x11_buttons = x11
event.delta, event.num = (None, value) if x11 else (value, None)
widget.lines = lines
self.assertEqual(util.wheel_event(event), "break")

(widget2 := _Widget()).lines = widget.lines
widget.lines = 0 # Make assert fail if widget.yview called.
util.wheel_event(event, widget2) # Test delagation to passed widget.


if __name__ == '__main__':
unittest.main(verbosity=2)
45 changes: 12 additions & 33 deletions Lib/idlelib/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from idlelib.config import idleConf
from idlelib import zoomheight
from idlelib.util import x11_buttons, wheel_event

ICONDIR = "Icons"

Expand Down Expand Up @@ -56,33 +57,8 @@ def listicons(icondir=ICONDIR):
column = 0
root.images = images

def wheel_event(event, widget=None):
"""Handle scrollwheel event.

For wheel up, event.delta = 120*n on Windows, -1*n on darwin,
where n can be > 1 if one scrolls fast. Flicking the wheel
generates up to maybe 20 events with n up to 10 or more 1.
Macs use wheel down (delta = 1*n) to scroll up, so positive
delta means to scroll up on both systems.

X-11 sends Control-Button-4,5 events instead.

The widget parameter is needed so browser label bindings can pass
the underlying canvas.

This function depends on widget.yview to not be overridden by
a subclass.
"""
up = {EventType.MouseWheel: event.delta > 0,
EventType.ButtonPress: event.num == 4}
lines = -5 if up[event.type] else 5
widget = event.widget if widget is None else widget
widget.yview(SCROLL, lines, 'units')
return 'break'


class TreeNode:

dy = 0

def __init__(self, canvas, parent, item):
Expand Down Expand Up @@ -285,10 +261,12 @@ def drawtext(self):
anchor="nw", window=self.label)
self.label.bind("<1>", self.select_or_edit)
self.label.bind("<Double-1>", self.flip)
self.label.bind("<MouseWheel>", lambda e: wheel_event(e, self.canvas))
if self.label._windowingsystem == 'x11':
self.label.bind("<Button-4>", lambda e: wheel_event(e, self.canvas))
self.label.bind("<Button-5>", lambda e: wheel_event(e, self.canvas))
def wheel_event2(event): wheel_event(event, self.canvas)
if x11_buttons:
self.label.bind('<Button-4>', wheel_event2)
self.label.bind('<Button-5>', wheel_event2)
else:
self.label.bind('<MouseWheel>', wheel_event2)
self.text_id = id
if TreeNode.dy == 0:
# The first row doesn't matter what the dy is, just measure its
Expand Down Expand Up @@ -466,10 +444,11 @@ def __init__(self, master, **opts):
self.canvas.bind("<Key-Next>", self.page_down)
self.canvas.bind("<Key-Up>", self.unit_up)
self.canvas.bind("<Key-Down>", self.unit_down)
self.canvas.bind("<MouseWheel>", wheel_event)
if self.canvas._windowingsystem == 'x11':
self.canvas.bind("<Button-4>", wheel_event)
self.canvas.bind("<Button-5>", wheel_event)
if x11_buttons:
self.canvas.bind('<Button-4>', wheel_event)
self.canvas.bind('<Button-5>', wheel_event)
else:
self.canvas.bind('<MouseWheel>', wheel_event)
#if isinstance(master, Toplevel) or isinstance(master, Tk):
self.canvas.bind("<Alt-Key-2>", self.zoom_height)
self.canvas.focus_set()
Expand Down
32 changes: 31 additions & 1 deletion Lib/idlelib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@
* warning stuff (pyshell, run).
"""
import sys
import tkinter

# .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')


# fix_x functions seem only needed once per process.
# fix_x functions seem needed only once per process.

def fix_scaling(root): # Called in filelist _test, pyshell, and run.
"""Scale fonts on HiDPI displays, once per process."""
Expand All @@ -45,6 +46,7 @@ def fix_win_hidpi(): # Called in pyshell and turtledemo.
except (ImportError, AttributeError, OSError):
pass


def fix_word_breaks(root): # Called in editor htest, filelist _test, pyshell.
# On Windows, tcl/tk breaks 'words' only on spaces, as in Command Prompt.
# We want Motif style everywhere. See #21474, msg218992 and followup.
Expand All @@ -65,6 +67,34 @@ def fix_x11_paste(root):
root.bind_class(cls, '<<Paste>>'))


# On X11, Tk 8.6- signals mouse wheel rotations with <Button-4> and
# <Button-5> events. With 8.7+, it generates <Mousewheel events
# as on other systems. Used here, editor, tree, and test_sidebar.
x11_buttons = (tkinter.BaseWidget._windowingsystem == 'x11' and
tkinter.TkVersion <= 8.6)

def wheel_event(event, widget=None): # Bound in editor and tree.
"""Handle scrollwheel event.

For MouseWheel up events, event.delta is generally= 120*n, but -1*n
on darwin (or just Aqua?), where n can be > 1 if one scrolls fast.
Macs use wheel down (delta = 1*n) to scroll up, so positive
delta means to scroll up on both systems.

When x11_buttons is True, Button-4,5 events signal up and down.

The widget parameter is needed in tree so browser events can be
passed to the underlying canvas vertical scrollbar. If tree is
replaced by ttk.Treeview, 'widget' can go.
"""
up = event.num == 4 if x11_buttons else event.delta > 0
# The following works, though +-4*n might be more standard,
lines = -5 if up else 5
widget = event.widget if widget is None else widget
widget.yview('scroll', lines, 'units')
return 'break'


if __name__ == '__main__':
from unittest import main
main('idlelib.idle_test.test_util', verbosity=2)
Loading