Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions source/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ def __init__(self):
style = wx.DEFAULT_FRAME_STYLE ^ wx.MAXIMIZE_BOX ^ wx.MINIMIZE_BOX | wx.FRAME_NO_TASKBAR
super(MainFrame, self).__init__(None, wx.ID_ANY, versionInfo.name, size=(1,1), style=style)
self.Bind(wx.EVT_CLOSE, self.onExitCommand)

self.sysTrayIcon = SysTrayIcon(self)
# wx destroys child Windows automatically but `wx.adv.TaskBarIcon` is not a window
# so it must be set to be destroyed before destroying our main frame window (#12243)
self.Bind(wx.EVT_WINDOW_DESTROY, self._onDestroy, source=self)

#: The focus before the last popup or C{None} if unknown.
#: This is only valid before L{prePopup} is called,
#: so it should be used as early as possible in any popup that needs it.
Expand All @@ -75,6 +80,13 @@ def __init__(self):
self.Show()
self.Hide()

def _onDestroy(self, evt: wx.EVT_WINDOW_DESTROY):
evt.Skip(skip=False) # blocks other handlers as wxCommandEvents must only be processed by one handler
# wx destroys child Windows automatically but `wx.adv.TaskBarIcon` is not a window
# so it must be set to be destroyed when destroying our main frame window (#12243)
log.debug(f"destroying systray icon")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why you're using f-string here?

wx.CallAfter(self.sysTrayIcon.Destroy) # queue the destruction event safely

def prePopup(self):
"""Prepare for a popup.
This should be called before any dialog or menu which should pop up for the user.
Expand Down Expand Up @@ -358,30 +370,35 @@ def onConfigProfilesCommand(self, evt):

def safeAppExit():
"""
Ensures the app is exited by all the top windows being destroyed
Ensures the app is exited by all the top windows being destroyed.
wx objects that don't inherit from wx.Window (eg sysTrayIcon, Menu) need to be manually destroyed.
"""

for window in wx.GetTopLevelWindows():
if isinstance(window, wx.Dialog) and window.IsModal():
log.info(f"ending modal {window} during exit process")
log.debug(f"ending modal {window} during exit process")
wx.CallAfter(window.EndModal, wx.ID_CLOSE_ALL)
if isinstance(window, MainFrame):
log.info(f"destroying main frame during exit process")
log.debug(f"destroying main frame during exit process")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here w.r.t f-string being unnecessary.

# the MainFrame has EVT_CLOSE bound to the ExitDialog
# which calls this function on exit, so destroy this window
wx.CallAfter(window.Destroy)
else:
log.info(f"closing window {window} during exit process")
log.debug(f"closing window {window} during exit process")
wx.CallAfter(window.Close)


class SysTrayIcon(wx.adv.TaskBarIcon):

def __init__(self, frame):
super(SysTrayIcon, self).__init__()
icon=wx.Icon(ICON_PATH,wx.BITMAP_TYPE_ICO)
self.SetIcon(icon, versionInfo.name)

self.menu=wx.Menu()
# wx.Windows destroy child Windows automatically but wx.Menu and TaskBarIcon don't inherit from
# wx.Window. The menu must be manually destroyed when destroying our system tray icon (#12243)
self.menu = wx.Menu()

menu_preferences=self.preferencesMenu=wx.Menu()
item = menu_preferences.Append(wx.ID_ANY,
# Translators: The label for the menu item to open NVDA Settings dialog.
Expand Down Expand Up @@ -542,6 +559,13 @@ def __init__(self, frame):
self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.onActivate)
self.Bind(wx.adv.EVT_TASKBAR_RIGHT_DOWN, self.onActivate)

def Destroy(self):
# wx.Windows destroy child Windows automatically but wx.Menu and TaskBarIcon don't inherit from
# wx.Window. The menu must be manually destroyed when destroying our system tray icon (#12243)
log.debug(f"destroying systray menu during exit process")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And one more

wx.CallAfter(self.menu.Destroy)
return super().Destroy()

def onActivate(self, evt):
mainFrame.prePopup()
import appModules.nvda
Expand Down