diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 6990af519b1..1373b7642a6 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -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 = { diff --git a/Lib/idlelib/idle_test/test_sidebar.py b/Lib/idlelib/idle_test/test_sidebar.py index 07e8f2e96b9..0f5b4c71223 100644 --- a/Lib/idlelib/idle_test/test_sidebar.py +++ b/Lib/idlelib/idle_test/test_sidebar.py @@ -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('', + 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('', x=x, y=y) + self.root.update() + + self.linenumber.sidebar_text.event_generate('', + 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('', - x=start_x, y=start_y) - self.linenumber.sidebar_text.event_generate('', - x=start_x, y=start_y) - self.linenumber.sidebar_text.event_generate('', - x=end_x, y=end_y) - self.linenumber.sidebar_text.event_generate('', - 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('', - x=start_x, y=start_y) - self.linenumber.sidebar_text.event_generate('', - x=start_x, y=start_y) - self.linenumber.sidebar_text.event_generate('', - x=end_x, y=end_y) - self.linenumber.sidebar_text.event_generate('', - 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): diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py index 76964a4cdfb..41c09684a20 100644 --- a/Lib/idlelib/sidebar.py +++ b/Lib/idlelib/sidebar.py @@ -204,10 +204,19 @@ class LineNumbers(BaseSideBar): bind_mouse_event(event_name, target_event_name=f'') + # 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('', 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('', 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])