Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions source/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,11 @@ def triggerNVDAExit(newNVDA: Optional[NewNVDAInstance] = None) -> bool:
instance information with `newNVDA`.
@return: True if this is the first call to trigger the exit, and the shutdown event was queued.
"""
from gui.message import isInMessageBox
from gui.message import isModalMessageBoxActive
import queueHandler
global _hasShutdownBeenTriggered
with _shuttingDownFlagLock:
safeToExit = not isInMessageBox()
safeToExit = not isModalMessageBoxActive()
if not safeToExit:
log.error("NVDA cannot exit safely, ensure open dialogs are closed")
return False
Expand Down
22 changes: 8 additions & 14 deletions source/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@
import queueHandler
import core
from . import guiHelper
from buildVersion import version_year
from .message import (
isInMessageBox as _isInMessageBox,
isModalMessageBoxActive,
# messageBox is accessed through `gui.messageBox` as opposed to `gui.message.messageBox` throughout NVDA,
# be cautious when removing
messageBox,
Expand Down Expand Up @@ -61,11 +60,6 @@
### Globals
mainFrame = None

if version_year < 2022:
# Like other top level variables, this must be used as follows (#13011):
# import gui; doSomething(gui.isInMessageBox)
# NOT the following: from gui import isInMessageBox; doSomething(isInMessageBox)
isInMessageBox = False

class MainFrame(wx.Frame):

Expand Down Expand Up @@ -159,7 +153,7 @@ def onSaveConfigurationCommand(self,evt):
messageBox(_("Could not save configuration - probably read only file system"),_("Error"),wx.OK | wx.ICON_ERROR)

def _popupSettingsDialog(self, dialog, *args, **kwargs):
if _isInMessageBox():
if isModalMessageBoxActive():
return
self.prePopup()
try:
Expand Down Expand Up @@ -208,7 +202,7 @@ def evaluateUpdatePendingUpdateMenuItemCommand(self):
self.sysTrayIcon.menu.Insert(self.sysTrayIcon.installPendingUpdateMenuItemPos,self.sysTrayIcon.installPendingUpdateMenuItem)

def onExitCommand(self, evt):
if _isInMessageBox():
if isModalMessageBoxActive():
return
if config.conf["general"]["askToExit"]:
self.prePopup()
Expand Down Expand Up @@ -311,7 +305,7 @@ def onPythonConsoleCommand(self, evt):
pythonConsole.activate()

def onAddonsManagerCommand(self,evt):
if _isInMessageBox() or globalVars.appArgs.secure:
if isModalMessageBoxActive() or globalVars.appArgs.secure:
return
self.prePopup()
from .addonGui import AddonsDialog
Expand All @@ -327,7 +321,7 @@ def onReloadPluginsCommand(self, evt):
NVDAObject.clearDynamicClassCache()

def onCreatePortableCopyCommand(self,evt):
if _isInMessageBox():
if isModalMessageBoxActive():
return
self.prePopup()
import gui.installerGui
Expand All @@ -336,13 +330,13 @@ def onCreatePortableCopyCommand(self,evt):
self.postPopup()

def onInstallCommand(self, evt):
if _isInMessageBox():
if isModalMessageBoxActive():
return
from gui import installerGui
installerGui.showInstallGui()

def onRunCOMRegistrationFixesCommand(self, evt):
if _isInMessageBox():
if isModalMessageBoxActive():
return
if messageBox(
# Translators: A message to warn the user when starting the COM Registration Fixing tool
Expand Down Expand Up @@ -376,7 +370,7 @@ def onRunCOMRegistrationFixesCommand(self, evt):
)

def onConfigProfilesCommand(self, evt):
if _isInMessageBox():
if isModalMessageBoxActive():
return
self.prePopup()
from .configProfiles import ProfilesDialog
Expand Down
48 changes: 36 additions & 12 deletions source/gui/message.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# -*- coding: UTF-8 -*-
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2006-2021 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Mesar Hameed, Joseph Lee,
# Copyright (C) 2006-2022 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Mesar Hameed, Joseph Lee,
# Thomas Stivers, Babbage B.V., Accessolutions, Julien Cochuyt
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

from buildVersion import version_year
import threading
from typing import Optional
import wx
Expand All @@ -14,8 +13,24 @@
_messageBoxCounter = 0


def isInMessageBox() -> bool:
return _messageBoxCounter != 0
def isModalMessageBoxActive() -> bool:
"""
`gui.message.messageBox` is a function which blocks the calling thread,
until a user responds to the modal dialog.
When some action (e.g. quitting NVDA) should be prevented due to any active modal message box,
even if unrelated, use `isModalMessageBoxActive` to check before triggering the action.
NVDA is in an uncertain state while waiting for an answer from a `gui.message.messageBox`.

It's possible for multiple message boxes to be open at a time.
This function can be used to check before opening subsequent `gui.message.messageBox` instances.

Because an answer is required to continue after a modal messageBox is opened,
some actions such as shutting down are prevented while NVDA is in a possibly uncertain state.

@return: True if a thread blocking modal response is still pending.
"""
with _messageBoxCounterLock:
return _messageBoxCounter != 0


def messageBox(
Expand All @@ -25,30 +40,39 @@ def messageBox(
parent: Optional[wx.Window] = None
) -> int:
"""Display a message dialog.
This should be used for all message dialogs
rather than using C{wx.MessageDialog} and C{wx.MessageBox} directly.
Avoid using C{wx.MessageDialog} and C{wx.MessageBox} directly.
@param message: The message text.
@param caption: The caption (title) of the dialog.
@param style: Same as for wx.MessageBox.
@param parent: The parent window.
@return: Same as for wx.MessageBox.

`gui.message.messageBox` is a function which blocks the calling thread,
until a user responds to the modal dialog.
This function should be used when an answer is required before proceeding.
Consider using a custom subclass of a wxDialog if an answer is not required
or a default answer can be provided.

It's possible for multiple message boxes to be open at a time.
Before opening a new messageBox, use `isModalMessageBoxActive`
to check if another messageBox modal response is still pending.

Because an answer is required to continue after a modal messageBox is opened,
some actions such as shutting down are prevented while NVDA is in a possibly uncertain state.
"""
from gui import mainFrame
import gui
global _messageBoxCounter
with _messageBoxCounterLock:
_messageBoxCounter += 1
if version_year < 2022:
gui.isInMessageBox = True

try:
if not parent:
mainFrame.prePopup()
res = wx.MessageBox(message, caption, style, parent or mainFrame)
finally:
if not parent:
mainFrame.postPopup()
finally:
with _messageBoxCounterLock:
_messageBoxCounter -= 1
if version_year < 2022:
gui.isInMessageBox = isInMessageBox()

return res
1 change: 1 addition & 0 deletions user_docs/en/changes.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ This can dramatically decrease build times on multi core systems. (#13226)
- ``cachedAutomationId`` can be used if obtained directly from the element.
-
- ``NVDAObjects.window.scintilla.CharacterRangeStruct`` has moved to ``NVDAObjects.window.scintilla.Scintilla.CharacterRangeStruct``. (#13364)
- Boolean ``gui.isInMessageBox`` is removed, please use the function ``gui.message.isModalMessageBoxActive`` instead. (#12984, #13376)
- ``controlTypes`` has been split up into various submodules. (#12510)
- ``ROLE_*`` and ``STATE_*`` have been replaced with ``Role.*`` and ``State.*``.
- ``roleLabels``, ``stateLabels`` and ``negativeStateLabels`` has been removed.
Expand Down