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.
(cherry picked from commit 86f1a18abf)

Co-authored-by: Tal Einat <taleinat+github@gmail.com>
This commit is contained in:
Miss Islington (bot) 2019-08-04 09:47:11 -07:00 committed by GitHub
parent 8cd6305784
commit 9e0c48a002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 35 deletions

View File

@ -210,13 +210,20 @@ _linenumbers_drag_scrolling_spec = {
'file': 'sidebar', 'file': 'sidebar',
'kwds': {}, 'kwds': {},
'msg': textwrap.dedent("""\ '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. 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 The text and line numbers should gradually scroll down, with the
selection updated continuously. 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 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 = { _multi_call_spec = {

View File

@ -240,7 +240,6 @@ class LineNumbersTest(unittest.TestCase):
self.assert_sidebar_n_lines(1) self.assert_sidebar_n_lines(1)
self.assertEqual(get_width(), 1) self.assertEqual(get_width(), 1)
@unittest.skipIf(platform == 'darwin', 'test tk version dependent')
def test_click_selection(self): def test_click_selection(self):
self.linenumber.show_sidebar() self.linenumber.show_sidebar()
self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') 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')) 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): def test_drag_selection_down(self):
self.linenumber.show_sidebar() self.linenumber.show_sidebar()
self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n')
self.root.update() self.root.update()
# Drag from the second line to the fourth line. # Drag from the second line to the fourth line.
start_x, start_y = self.get_line_screen_position(2) self.simulate_drag(2, 4)
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.assertEqual(self.get_selection(), ('2.0', '5.0')) self.assertEqual(self.get_selection(), ('2.0', '5.0'))
@unittest.skipIf(platform == 'darwin', 'test tk version dependent')
def test_drag_selection_up(self): def test_drag_selection_up(self):
self.linenumber.show_sidebar() self.linenumber.show_sidebar()
self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n')
self.root.update() self.root.update()
# Drag from the fourth line to the second line. # Drag from the fourth line to the second line.
start_x, start_y = self.get_line_screen_position(4) self.simulate_drag(4, 2)
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.assertEqual(self.get_selection(), ('2.0', '5.0')) self.assertEqual(self.get_selection(), ('2.0', '5.0'))
def test_scroll(self): def test_scroll(self):

View File

@ -204,10 +204,19 @@ class LineNumbers(BaseSideBar):
bind_mouse_event(event_name, bind_mouse_event(event_name,
target_event_name=f'<Button-{button}>') 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 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): def b1_mousedown_handler(event):
# select the entire line # 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_remove("sel", "1.0", "end")
self.text.tag_add("sel", f"{lineno}.0", f"{lineno+1}.0") self.text.tag_add("sel", f"{lineno}.0", f"{lineno+1}.0")
self.text.mark_set("insert", f"{lineno+1}.0") self.text.mark_set("insert", f"{lineno+1}.0")
@ -217,15 +226,20 @@ class LineNumbers(BaseSideBar):
start_line = lineno start_line = lineno
self.sidebar_text.bind('<Button-1>', b1_mousedown_handler) self.sidebar_text.bind('<Button-1>', b1_mousedown_handler)
# These are set by b1_motion_handler() and read by selection_handler(); def b1_mouseup_handler(event):
# see below. last_y is passed this way since the mouse Y-coordinate # On mouse up, we're no longer dragging. Set the shared persistent
# is not available on selection event objects. last_yview is passed # variables to None to represent this.
# this way to recognize scrolling while the mouse isn't moving. nonlocal start_line
last_y = last_yview = None 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): def drag_update_selection_and_insert_mark(y_coord):
"""Helper function for drag and selection event handlers.""" """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]) a, b = sorted([start_line, lineno])
self.text.tag_remove("sel", "1.0", "end") self.text.tag_remove("sel", "1.0", "end")
self.text.tag_add("sel", f"{a}.0", f"{b+1}.0") 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 # while the mouse isn't moving, leading to the above fix not scrolling
# properly. # properly.
def selection_handler(event): def selection_handler(event):
if last_yview is None:
# This logic is only needed while dragging.
return
yview = self.sidebar_text.yview() yview = self.sidebar_text.yview()
if yview != last_yview: if yview != last_yview:
self.text.yview_moveto(yview[0]) self.text.yview_moveto(yview[0])