Skip to content

Commit

Permalink
bpo-37706: IDLE - fix sidebar code bug and drag tests (pythonGH-15103)
Browse files Browse the repository at this point in the history
Convert mouse y to line number in the sidebar rather than the text.
  • Loading branch information
taleinat authored and terryjreedy committed Aug 4, 2019
1 parent 18b711c commit 86f1a18
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 35 deletions.
13 changes: 10 additions & 3 deletions Lib/idlelib/idle_test/htest.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,20 @@ def _wrapper(parent): # htest #
'file': 'sidebar',
'kwds': {},
'msg': textwrap.dedent("""\
Click on the line numbers and drag down below the edge of the
1. Click on the line numbers and drag down below the edge of the
window, moving the mouse a bit and then leaving it there for a while.
The text and line numbers should gradually scroll down, with the
selection updated continuously.
Do the same as above, dragging to above the window. The text and line
2. With the lines still selected, click on a line number above the
selected lines. Only the line whose number was clicked should be
selected.
3. Repeat step #1, dragging to above the window. The text and line
numbers should gradually scroll up, with the selection updated
continuously."""),
continuously.
4. Repeat step #2, clicking a line number below the selection."""),
}

_multi_call_spec = {
Expand Down
52 changes: 27 additions & 25 deletions Lib/idlelib/idle_test/test_sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ def get_width():
self.assert_sidebar_n_lines(1)
self.assertEqual(get_width(), 1)

@unittest.skipIf(platform == 'darwin', 'test tk version dependent')
def test_click_selection(self):
self.linenumber.show_sidebar()
self.text.insert('1.0', 'one\ntwo\nthree\nfour\n')
Expand All @@ -254,44 +253,47 @@ def test_click_selection(self):

self.assertEqual(self.get_selection(), ('2.0', '3.0'))

@unittest.skipIf(platform == 'darwin', 'test tk version dependent')
def test_drag_selection_down(self):
self.linenumber.show_sidebar()
self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n')
self.root.update()
def simulate_drag(self, start_line, end_line):
start_x, start_y = self.get_line_screen_position(start_line)
end_x, end_y = self.get_line_screen_position(end_line)

# Drag from the second line to the fourth line.
start_x, start_y = self.get_line_screen_position(2)
end_x, end_y = self.get_line_screen_position(4)
self.linenumber.sidebar_text.event_generate('<Button-1>',
x=start_x, y=start_y)
self.linenumber.sidebar_text.event_generate('<B1-Motion>',
x=start_x, y=start_y)
self.linenumber.sidebar_text.event_generate('<B1-Motion>',
x=end_x, y=end_y)
self.root.update()

def lerp(a, b, steps):
"""linearly interpolate from a to b (inclusive) in equal steps"""
last_step = steps - 1
for i in range(steps):
yield ((last_step - i) / last_step) * a + (i / last_step) * b

for x, y in zip(
map(int, lerp(start_x, end_x, steps=11)),
map(int, lerp(start_y, end_y, steps=11)),
):
self.linenumber.sidebar_text.event_generate('<B1-Motion>', x=x, y=y)
self.root.update()

self.linenumber.sidebar_text.event_generate('<ButtonRelease-1>',
x=end_x, y=end_y)
self.root.update()

def test_drag_selection_down(self):
self.linenumber.show_sidebar()
self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n')
self.root.update()

# Drag from the second line to the fourth line.
self.simulate_drag(2, 4)
self.assertEqual(self.get_selection(), ('2.0', '5.0'))

@unittest.skipIf(platform == 'darwin', 'test tk version dependent')
def test_drag_selection_up(self):
self.linenumber.show_sidebar()
self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n')
self.root.update()

# Drag from the fourth line to the second line.
start_x, start_y = self.get_line_screen_position(4)
end_x, end_y = self.get_line_screen_position(2)
self.linenumber.sidebar_text.event_generate('<Button-1>',
x=start_x, y=start_y)
self.linenumber.sidebar_text.event_generate('<B1-Motion>',
x=start_x, y=start_y)
self.linenumber.sidebar_text.event_generate('<B1-Motion>',
x=end_x, y=end_y)
self.linenumber.sidebar_text.event_generate('<ButtonRelease-1>',
x=end_x, y=end_y)
self.root.update()
self.simulate_drag(4, 2)
self.assertEqual(self.get_selection(), ('2.0', '5.0'))

def test_scroll(self):
Expand Down
31 changes: 24 additions & 7 deletions Lib/idlelib/sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,19 @@ def bind_mouse_event(event_name, target_event_name):
bind_mouse_event(event_name,
target_event_name=f'<Button-{button}>')

# This is set by b1_mousedown_handler() and read by
# drag_update_selection_and_insert_mark(), to know where dragging
# began.
start_line = None
# These are set by b1_motion_handler() and read by selection_handler().
# last_y is passed this way since the mouse Y-coordinate is not
# available on selection event objects. last_yview is passed this way
# to recognize scrolling while the mouse isn't moving.
last_y = last_yview = None

def b1_mousedown_handler(event):
# select the entire line
lineno = self.editwin.getlineno(f"@0,{event.y}")
lineno = int(float(self.sidebar_text.index(f"@0,{event.y}")))
self.text.tag_remove("sel", "1.0", "end")
self.text.tag_add("sel", f"{lineno}.0", f"{lineno+1}.0")
self.text.mark_set("insert", f"{lineno+1}.0")
Expand All @@ -217,15 +226,20 @@ def b1_mousedown_handler(event):
start_line = lineno
self.sidebar_text.bind('<Button-1>', b1_mousedown_handler)

# These are set by b1_motion_handler() and read by selection_handler();
# see below. last_y is passed this way since the mouse Y-coordinate
# is not available on selection event objects. last_yview is passed
# this way to recognize scrolling while the mouse isn't moving.
last_y = last_yview = None
def b1_mouseup_handler(event):
# On mouse up, we're no longer dragging. Set the shared persistent
# variables to None to represent this.
nonlocal start_line
nonlocal last_y
nonlocal last_yview
start_line = None
last_y = None
last_yview = None
self.sidebar_text.bind('<ButtonRelease-1>', b1_mouseup_handler)

def drag_update_selection_and_insert_mark(y_coord):
"""Helper function for drag and selection event handlers."""
lineno = self.editwin.getlineno(f"@0,{y_coord}")
lineno = int(float(self.sidebar_text.index(f"@0,{y_coord}")))
a, b = sorted([start_line, lineno])
self.text.tag_remove("sel", "1.0", "end")
self.text.tag_add("sel", f"{a}.0", f"{b+1}.0")
Expand Down Expand Up @@ -253,6 +267,9 @@ def b1_drag_handler(event, *args):
# while the mouse isn't moving, leading to the above fix not scrolling
# properly.
def selection_handler(event):
if last_yview is None:
# This logic is only needed while dragging.
return
yview = self.sidebar_text.yview()
if yview != last_yview:
self.text.yview_moveto(yview[0])
Expand Down

0 comments on commit 86f1a18

Please sign in to comment.