summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormlouielu <git@louie.lu>2017-06-14 15:13:19 (GMT)
committerterryjreedy <tjreedy@udel.edu>2017-06-14 15:13:19 (GMT)
commit778b484145edfd0d9b65129322d3295bed8eb71a (patch)
tree5f8895ec0e82caecc3b5ec01b626a7a212796813
parent4ebf03d109f827c91a23256a447c1d74a203dfee (diff)
downloadcpython-778b484145edfd0d9b65129322d3295bed8eb71a.zip
cpython-778b484145edfd0d9b65129322d3295bed8eb71a.tar.gz
cpython-778b484145edfd0d9b65129322d3295bed8eb71a.tar.bz2
bpo-15786: IDLE: Fix mouse clicks on autocompletetion window (#1811)
The root problem was non-check for hide_event. When user clicks on autocomplete window (acw), root widget gets focusOut event, then triggers hide_window to close the acw. It should only be hide when acw is active, and acw didn't get focus at FocusOut event (this event bind on acw and widget), or when widget get a ButtonPress event (this event only bind on widget). MacOS froze after double click on acw because when doubleclick_event try to hide window at the end, hide_window function destory whole acw, but tkinter didn't get focus back to widget. So set focus on widget first, then destory acw. Windows could not respond on double click event, because of the misbehavior of Configure event. When acw was shown, tkinter called winconfig event multiple times. That caused tkinter to not response to double click event. When on Windows, unbind Configure event first time get into winconfig_event to prevent multiple call of this event.
-rw-r--r--Lib/idlelib/autocomplete_w.py58
1 files changed, 50 insertions, 8 deletions
diff --git a/Lib/idlelib/autocomplete_w.py b/Lib/idlelib/autocomplete_w.py
index 3374c6e..e7354d0 100644
--- a/Lib/idlelib/autocomplete_w.py
+++ b/Lib/idlelib/autocomplete_w.py
@@ -1,6 +1,8 @@
"""
An auto-completion window for IDLE, used by the autocomplete extension
"""
+import platform
+
from tkinter import *
from tkinter.ttk import Scrollbar
@@ -8,7 +10,8 @@ from idlelib.autocomplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES
from idlelib.multicall import MC_SHIFT
HIDE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-hide>>"
-HIDE_SEQUENCES = ("<FocusOut>", "<ButtonPress>")
+HIDE_FOCUS_OUT_SEQUENCE = "<FocusOut>"
+HIDE_SEQUENCES = (HIDE_FOCUS_OUT_SEQUENCE, "<ButtonPress>")
KEYPRESS_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keypress>>"
# We need to bind event beyond <Key> so that the function will be called
# before the default specific IDLE function
@@ -201,10 +204,12 @@ class AutoCompleteWindow:
self._selection_changed()
# bind events
- self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,
- self.hide_event)
+ self.hideaid = acw.bind(HIDE_VIRTUAL_EVENT_NAME, self.hide_event)
+ self.hidewid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME, self.hide_event)
+ acw.event_add(HIDE_VIRTUAL_EVENT_NAME, HIDE_FOCUS_OUT_SEQUENCE)
for seq in HIDE_SEQUENCES:
self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)
+
self.keypressid = self.widget.bind(KEYPRESS_VIRTUAL_EVENT_NAME,
self.keypress_event)
for seq in KEYPRESS_SEQUENCES:
@@ -240,9 +245,37 @@ class AutoCompleteWindow:
new_y -= acw_height
acw.wm_geometry("+%d+%d" % (new_x, new_y))
+ if platform.system().startswith('Windows'):
+ # See issue 15786. When on windows platform, Tk will misbehaive
+ # to call winconfig_event multiple times, we need to prevent this,
+ # otherwise mouse button double click will not be able to used.
+ acw.unbind(WINCONFIG_SEQUENCE, self.winconfigid)
+ self.winconfigid = None
+
+ def _hide_event_check(self):
+ if not self.autocompletewindow:
+ return
+
+ try:
+ if not self.autocompletewindow.focus_get():
+ self.hide_window()
+ except KeyError:
+ # See issue 734176, when user click on menu, acw.focus_get()
+ # will get KeyError.
+ self.hide_window()
+
def hide_event(self, event):
+ # Hide autocomplete list if it exists and does not have focus or
+ # mouse click on widget / text area.
if self.is_active():
- self.hide_window()
+ if event.type == EventType.FocusOut:
+ # On windows platform, it will need to delay the check for
+ # acw.focus_get() when click on acw, otherwise it will return
+ # None and close the window
+ self.widget.after(1, self._hide_event_check)
+ elif event.type == EventType.ButtonPress:
+ # ButtonPress event only bind to self.widget
+ self.hide_window()
def listselect_event(self, event):
if self.is_active():
@@ -391,10 +424,15 @@ class AutoCompleteWindow:
return
# unbind events
+ self.autocompletewindow.event_delete(HIDE_VIRTUAL_EVENT_NAME,
+ HIDE_FOCUS_OUT_SEQUENCE)
for seq in HIDE_SEQUENCES:
self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)
- self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)
- self.hideid = None
+
+ self.autocompletewindow.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideaid)
+ self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hidewid)
+ self.hideaid = None
+ self.hidewid = None
for seq in KEYPRESS_SEQUENCES:
self.widget.event_delete(KEYPRESS_VIRTUAL_EVENT_NAME, seq)
self.widget.unbind(KEYPRESS_VIRTUAL_EVENT_NAME, self.keypressid)
@@ -405,8 +443,12 @@ class AutoCompleteWindow:
self.keyreleaseid = None
self.listbox.unbind(LISTUPDATE_SEQUENCE, self.listupdateid)
self.listupdateid = None
- self.autocompletewindow.unbind(WINCONFIG_SEQUENCE, self.winconfigid)
- self.winconfigid = None
+ if self.winconfigid:
+ self.autocompletewindow.unbind(WINCONFIG_SEQUENCE, self.winconfigid)
+ self.winconfigid = None
+
+ # Re-focusOn frame.text (See issue #15786)
+ self.widget.focus_set()
# destroy widgets
self.scrollbar.destroy()