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:
parent
18b711c5a7
commit
86f1a18abf
|
@ -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 = {
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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])
|
||||||
|
|
Loading…
Reference in New Issue