bpo-37706: IDLE - fix sidebar code bug and drag tests (GH-15103)

Convert mouse y to line number in the sidebar rather than the text.
This commit is contained in:
Tal Einat 2019-08-04 19:25:27 +03:00 committed by Terry Jan Reedy
parent 18b711c5a7
commit 86f1a18abf
3 changed files with 61 additions and 35 deletions

View File

@ -210,13 +210,20 @@ _linenumbers_drag_scrolling_spec = {
'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 = {

View File

@ -240,7 +240,6 @@ class LineNumbersTest(unittest.TestCase):
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')
@ -254,44 +253,47 @@ class LineNumbersTest(unittest.TestCase):
self.assertEqual(self.get_selection(), ('2.0', '3.0'))
@unittest.skipIf(platform == 'darwin', 'test tk version dependent')
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)
self.linenumber.sidebar_text.event_generate('<Button-1>',
x=start_x, y=start_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.
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.linenumber.sidebar_text.event_generate('<ButtonRelease-1>',
x=end_x, y=end_y)
self.root.update()
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):

View File

@ -204,10 +204,19 @@ class LineNumbers(BaseSideBar):
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")
@ -217,15 +226,20 @@ class LineNumbers(BaseSideBar):
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")
@ -253,6 +267,9 @@ class LineNumbers(BaseSideBar):
# 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])