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:
parent
dd5f8abb54
commit
1d2b4dba6c
|
@ -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 = {
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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])
|
||||
|
|
Loading…
Reference in New Issue