11"""
22An auto-completion window for IDLE, used by the autocomplete extension
33"""
4+ import platform
5+
46from tkinter import *
57from tkinter .ttk import Scrollbar
68
79from idlelib .autocomplete import COMPLETE_FILES , COMPLETE_ATTRIBUTES
810from idlelib .multicall import MC_SHIFT
911
1012HIDE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-hide>>"
11- HIDE_SEQUENCES = ("<FocusOut>" , "<ButtonPress>" )
13+ HIDE_FOCUS_OUT_SEQUENCE = "<FocusOut>"
14+ HIDE_SEQUENCES = (HIDE_FOCUS_OUT_SEQUENCE , "<ButtonPress>" )
1215KEYPRESS_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keypress>>"
1316# We need to bind event beyond <Key> so that the function will be called
1417# before the default specific IDLE function
@@ -201,10 +204,12 @@ def show_window(self, comp_lists, index, complete, mode, userWantsWin):
201204 self ._selection_changed ()
202205
203206 # bind events
204- self .hideid = self .widget .bind (HIDE_VIRTUAL_EVENT_NAME ,
205- self .hide_event )
207+ self .hideaid = acw .bind (HIDE_VIRTUAL_EVENT_NAME , self .hide_event )
208+ self .hidewid = self .widget .bind (HIDE_VIRTUAL_EVENT_NAME , self .hide_event )
209+ acw .event_add (HIDE_VIRTUAL_EVENT_NAME , HIDE_FOCUS_OUT_SEQUENCE )
206210 for seq in HIDE_SEQUENCES :
207211 self .widget .event_add (HIDE_VIRTUAL_EVENT_NAME , seq )
212+
208213 self .keypressid = self .widget .bind (KEYPRESS_VIRTUAL_EVENT_NAME ,
209214 self .keypress_event )
210215 for seq in KEYPRESS_SEQUENCES :
@@ -240,9 +245,37 @@ def winconfig_event(self, event):
240245 new_y -= acw_height
241246 acw .wm_geometry ("+%d+%d" % (new_x , new_y ))
242247
248+ if platform .system ().startswith ('Windows' ):
249+ # See issue 15786. When on windows platform, Tk will misbehaive
250+ # to call winconfig_event multiple times, we need to prevent this,
251+ # otherwise mouse button double click will not be able to used.
252+ acw .unbind (WINCONFIG_SEQUENCE , self .winconfigid )
253+ self .winconfigid = None
254+
255+ def _hide_event_check (self ):
256+ if not self .autocompletewindow :
257+ return
258+
259+ try :
260+ if not self .autocompletewindow .focus_get ():
261+ self .hide_window ()
262+ except KeyError :
263+ # See issue 734176, when user click on menu, acw.focus_get()
264+ # will get KeyError.
265+ self .hide_window ()
266+
243267 def hide_event (self , event ):
268+ # Hide autocomplete list if it exists and does not have focus or
269+ # mouse click on widget / text area.
244270 if self .is_active ():
245- self .hide_window ()
271+ if event .type == EventType .FocusOut :
272+ # On windows platform, it will need to delay the check for
273+ # acw.focus_get() when click on acw, otherwise it will return
274+ # None and close the window
275+ self .widget .after (1 , self ._hide_event_check )
276+ elif event .type == EventType .ButtonPress :
277+ # ButtonPress event only bind to self.widget
278+ self .hide_window ()
246279
247280 def listselect_event (self , event ):
248281 if self .is_active ():
@@ -391,10 +424,15 @@ def hide_window(self):
391424 return
392425
393426 # unbind events
427+ self .autocompletewindow .event_delete (HIDE_VIRTUAL_EVENT_NAME ,
428+ HIDE_FOCUS_OUT_SEQUENCE )
394429 for seq in HIDE_SEQUENCES :
395430 self .widget .event_delete (HIDE_VIRTUAL_EVENT_NAME , seq )
396- self .widget .unbind (HIDE_VIRTUAL_EVENT_NAME , self .hideid )
397- self .hideid = None
431+
432+ self .autocompletewindow .unbind (HIDE_VIRTUAL_EVENT_NAME , self .hideaid )
433+ self .widget .unbind (HIDE_VIRTUAL_EVENT_NAME , self .hidewid )
434+ self .hideaid = None
435+ self .hidewid = None
398436 for seq in KEYPRESS_SEQUENCES :
399437 self .widget .event_delete (KEYPRESS_VIRTUAL_EVENT_NAME , seq )
400438 self .widget .unbind (KEYPRESS_VIRTUAL_EVENT_NAME , self .keypressid )
@@ -405,8 +443,12 @@ def hide_window(self):
405443 self .keyreleaseid = None
406444 self .listbox .unbind (LISTUPDATE_SEQUENCE , self .listupdateid )
407445 self .listupdateid = None
408- self .autocompletewindow .unbind (WINCONFIG_SEQUENCE , self .winconfigid )
409- self .winconfigid = None
446+ if self .winconfigid :
447+ self .autocompletewindow .unbind (WINCONFIG_SEQUENCE , self .winconfigid )
448+ self .winconfigid = None
449+
450+ # Re-focusOn frame.text (See issue #15786)
451+ self .widget .focus_set ()
410452
411453 # destroy widgets
412454 self .scrollbar .destroy ()
0 commit comments