mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-39885: Make IDLE context menu cut and copy work again (GH-18951)
Leave selection when right click within. This exception to clearing selections when right-clicking was omitted from the previous commit,4ca060d. I did not realize that this completely disabled the context menu entries, and I should have merged a minimal fix immediately. An automated test should follow. (cherry picked from commit97e4e0f53d) Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
This commit is contained in:
		
							parent
							
								
									805fa54676
								
							
						
					
					
						commit
						9f3f70fd0b
					
				
					 4 changed files with 52 additions and 16 deletions
				
			
		|  | @ -12,8 +12,9 @@ when fetching a calltip. | ||||||
| bpo-27115: For 'Go to Line', use a Query entry box subclass with | bpo-27115: For 'Go to Line', use a Query entry box subclass with | ||||||
| IDLE standard behavior and improved error checking. | IDLE standard behavior and improved error checking. | ||||||
| 
 | 
 | ||||||
| bpo-39885: Since clicking to get an IDLE context menu moves the | bpo-39885: When a context menu is invoked by right-clicking outside | ||||||
| cursor, any text selection should be and now is cleared. | of a selection, clear the selection and move the cursor.  Cut and | ||||||
|  | Copy require that the click be within the selection. | ||||||
| 
 | 
 | ||||||
| bpo-39852: Edit "Go to line" now clears any selection, preventing | bpo-39852: Edit "Go to line" now clears any selection, preventing | ||||||
| accidental deletion.  It also updates Ln and Col on the status bar. | accidental deletion.  It also updates Ln and Col on the status bar. | ||||||
|  |  | ||||||
|  | @ -499,15 +499,23 @@ def handle_yview(self, event, *args): | ||||||
|     rmenu = None |     rmenu = None | ||||||
| 
 | 
 | ||||||
|     def right_menu_event(self, event): |     def right_menu_event(self, event): | ||||||
|         self.text.tag_remove("sel", "1.0", "end") |         text = self.text | ||||||
|         self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) |         newdex = text.index(f'@{event.x},{event.y}') | ||||||
|  |         try: | ||||||
|  |             in_selection = (text.compare('sel.first', '<=', newdex) and | ||||||
|  |                            text.compare(newdex, '<=',  'sel.last')) | ||||||
|  |         except TclError: | ||||||
|  |             in_selection = False | ||||||
|  |         if not in_selection: | ||||||
|  |             text.tag_remove("sel", "1.0", "end") | ||||||
|  |             text.mark_set("insert", newdex) | ||||||
|         if not self.rmenu: |         if not self.rmenu: | ||||||
|             self.make_rmenu() |             self.make_rmenu() | ||||||
|         rmenu = self.rmenu |         rmenu = self.rmenu | ||||||
|         self.event = event |         self.event = event | ||||||
|         iswin = sys.platform[:3] == 'win' |         iswin = sys.platform[:3] == 'win' | ||||||
|         if iswin: |         if iswin: | ||||||
|             self.text.config(cursor="arrow") |             text.config(cursor="arrow") | ||||||
| 
 | 
 | ||||||
|         for item in self.rmenu_specs: |         for item in self.rmenu_specs: | ||||||
|             try: |             try: | ||||||
|  | @ -520,7 +528,6 @@ def right_menu_event(self, event): | ||||||
|             state = getattr(self, verify_state)() |             state = getattr(self, verify_state)() | ||||||
|             rmenu.entryconfigure(label, state=state) |             rmenu.entryconfigure(label, state=state) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         rmenu.tk_popup(event.x_root, event.y_root) |         rmenu.tk_popup(event.x_root, event.y_root) | ||||||
|         if iswin: |         if iswin: | ||||||
|             self.text.config(cursor="ibeam") |             self.text.config(cursor="ibeam") | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| from collections import namedtuple | from collections import namedtuple | ||||||
| from test.support import requires | from test.support import requires | ||||||
| from tkinter import Tk | from tkinter import Tk | ||||||
|  | from idlelib.idle_test.mock_idle import Func | ||||||
| 
 | 
 | ||||||
| Editor = editor.EditorWindow | Editor = editor.EditorWindow | ||||||
| 
 | 
 | ||||||
|  | @ -92,6 +93,12 @@ def test_tabwidth_8(self): | ||||||
|                 ) |                 ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def insert(text, string): | ||||||
|  |     text.delete('1.0', 'end') | ||||||
|  |     text.insert('end', string) | ||||||
|  |     text.update()  # Force update for colorizer to finish. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class IndentAndNewlineTest(unittest.TestCase): | class IndentAndNewlineTest(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|  | @ -113,13 +120,6 @@ def tearDownClass(cls): | ||||||
|         cls.root.destroy() |         cls.root.destroy() | ||||||
|         del cls.root |         del cls.root | ||||||
| 
 | 
 | ||||||
|     def insert(self, text): |  | ||||||
|         t = self.window.text |  | ||||||
|         t.delete('1.0', 'end') |  | ||||||
|         t.insert('end', text) |  | ||||||
|         # Force update for colorizer to finish. |  | ||||||
|         t.update() |  | ||||||
| 
 |  | ||||||
|     def test_indent_and_newline_event(self): |     def test_indent_and_newline_event(self): | ||||||
|         eq = self.assertEqual |         eq = self.assertEqual | ||||||
|         w = self.window |         w = self.window | ||||||
|  | @ -170,13 +170,13 @@ def test_indent_and_newline_event(self): | ||||||
|         w.prompt_last_line = '' |         w.prompt_last_line = '' | ||||||
|         for test in tests: |         for test in tests: | ||||||
|             with self.subTest(label=test.label): |             with self.subTest(label=test.label): | ||||||
|                 self.insert(test.text) |                 insert(text, test.text) | ||||||
|                 text.mark_set('insert', test.mark) |                 text.mark_set('insert', test.mark) | ||||||
|                 nl(event=None) |                 nl(event=None) | ||||||
|                 eq(get('1.0', 'end'), test.expected) |                 eq(get('1.0', 'end'), test.expected) | ||||||
| 
 | 
 | ||||||
|         # Selected text. |         # Selected text. | ||||||
|         self.insert('  def f1(self, a, b):\n    return a + b') |         insert(text, '  def f1(self, a, b):\n    return a + b') | ||||||
|         text.tag_add('sel', '1.17', '1.end') |         text.tag_add('sel', '1.17', '1.end') | ||||||
|         nl(None) |         nl(None) | ||||||
|         # Deletes selected text before adding new line. |         # Deletes selected text before adding new line. | ||||||
|  | @ -184,11 +184,37 @@ def test_indent_and_newline_event(self): | ||||||
| 
 | 
 | ||||||
|         # Preserves the whitespace in shell prompt. |         # Preserves the whitespace in shell prompt. | ||||||
|         w.prompt_last_line = '>>> ' |         w.prompt_last_line = '>>> ' | ||||||
|         self.insert('>>> \t\ta =') |         insert(text, '>>> \t\ta =') | ||||||
|         text.mark_set('insert', '1.5') |         text.mark_set('insert', '1.5') | ||||||
|         nl(None) |         nl(None) | ||||||
|         eq(get('1.0', 'end'), '>>> \na =\n') |         eq(get('1.0', 'end'), '>>> \na =\n') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class RMenuTest(unittest.TestCase): | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def setUpClass(cls): | ||||||
|  |         requires('gui') | ||||||
|  |         cls.root = Tk() | ||||||
|  |         cls.root.withdraw() | ||||||
|  |         cls.window = Editor(root=cls.root) | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def tearDownClass(cls): | ||||||
|  |         cls.window._close() | ||||||
|  |         del cls.window | ||||||
|  |         cls.root.update_idletasks() | ||||||
|  |         for id in cls.root.tk.call('after', 'info'): | ||||||
|  |             cls.root.after_cancel(id) | ||||||
|  |         cls.root.destroy() | ||||||
|  |         del cls.root | ||||||
|  | 
 | ||||||
|  |     class DummyRMenu: | ||||||
|  |         def tk_popup(x, y): pass | ||||||
|  | 
 | ||||||
|  |     def test_rclick(self): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     unittest.main(verbosity=2) |     unittest.main(verbosity=2) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Make context menu Cut and Copy work again when right-clicking within a | ||||||
|  | selection. | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Miss Islington (bot)
						Miss Islington (bot)