Repository: nvaccess/nvda

Issue:
Cannot close speech viewer with alt+f4
<!--
Please thoroughly read NVDA's wiki article on how to fill in this template, including how to provide the required files.
Issues may be closed if the required information is not present.
https://github.com/nvaccess/nvda/wiki/Github-issue-template-explanation-and-examples
-->

### Steps to reproduce:

1. Press NVDA+n, then t, then s to open speech viewer.
2. Press alt+shift+tab to move to the speech viewer window (it starts as the last window in the alt+tab sequence).
3. Press alt+f4

### Actual behavior:

At step 3, nothing happens.  Nothing is even recorded to the NVDA log at debug level:

IO - inputCore.InputManager.executeGesture (13:33:05.596) - winInputHook (11520):
Input: kb(desktop):alt+f4
IO - inputCore.InputManager.executeGesture (13:33:08.944) - winInputHook (11520):
Input: kb(desktop):NVDA+f1

(I opened the log viewer a few seconds later)

### Expected behavior:

ALT+F4 should close the window, as happens in any other Window (including other NVDA windows).

Note that the speech viewer window can still be closed by pressing alt+n, t, s again, or by using the shortcut key (if you have defined a shortcut key to toggle speech viewer).  Creating a shortcut for speech viewer (in NVDA's input gestures dialog) is recommended for users who frequently toggle this feature on and off.

### System configuration
#### NVDA installed/portable/running from source:

Installed
#### NVDA version:
2019.3.1
#### Windows version:
1903 build 18362.388
#### Name and version of other software in use when reproducing the issue:

#### Other information about your system:

### Other questions
#### Does the issue still occur after restarting your PC?
Yes.  Also reported by a user.

#### Have you tried any other versions of NVDA? If so, please report their behaviors.
2019.2.1 - alt+f4 closes speech viewer as expected.



Discussion:
@Qchristensen: Please also check this out with the new Braille Viewer window in NVDA 2019.3.1. Thanks.
Good question, the Braille viewer DOES close with alt+f4, so this seems to be something about the speech viewer.  (Settings, speech dictionary, punctuation, input gestures, log viewer, braille viewer, python console, add-ons manager, create portable copy, welcome dialog, about dialog, configuration profile and exit dialog can all be closed with alt+f4.

The only other dialog I couldn't close with alt+f4 was the "Run COM Registration fixing tool", which can't be closed with alt+f4, or escape, you need to activate either the Yes or No button (ideally, ALT+F4, and escape, should both activate the no button on that dialog).
I can confirm that this issue persists with NVDA version 2020.2 Beta 1. I would, however, like to request that the scope of the request in this ticket be expanded: the Speech Viewer window should not just close when Alt+F4 is pressed, but it should also have a dedicated Close button. The reason for this is that it can be reasonably expected, I think, that a substantial proportion of the users of the Speech Viewer functionality will be people with some sight. A significant subset of these will be fully sighted and may not be used to issuing commands using keyboard shortcuts at all. As such, they may not know of the Alt+F4 key command, and even if they know of it, it may not strike them since they seldom use it. Hence, having a conspicuous Close button would reduce any potential confusion for these mouse users.
In addition, I think there is a case to be made for having the usual Minimize, Maximize and Close buttons in the Speech Viewer window too. However, I think that request is distinct enough from this ticket to merit another discussion some other day.
This is due to the event being vetoed https://github.com/nvaccess/nvda/blob/release-2020.4/source/speechViewer.py#L103
@bhavyashah wrote:
> I would, however, like to request that the scope of the request in this ticket be expanded: the Speech Viewer window should not just close when Alt+F4 is pressed, but it should also have a dedicated Close button. The reason for this is that it can be reasonably expected, I think, that a substantial proportion of the users of the Speech Viewer functionality will be people with some sight. A significant subset of these will be fully sighted and may not be used to issuing commands using keyboard shortcuts at all. As such, they may not know of the Alt+F4 key command, and even if they know of it, it may not strike them since they seldom use it. Hence, having a conspicuous Close button would reduce any potential confusion for these mouse users.

I fully agree. Please see PR #12328 for the addition of a standard close button on the Braille Viewer as well.


@seanbudd wrote:
> This is due to the event being vetoed https://github.com/nvaccess/nvda/blob/release-2020.4/source/speechViewer.py#L103

Indeed. Still, care must be taken to properly handle unchecking of the dedicated NVDA Tool menu entry, as is currently done with the Braille Viewer.

Resolving patch:
diff --git a/source/gui/__init__.py b/source/gui/__init__.py
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -1,7 +1,7 @@
 # -*- coding: UTF-8 -*-
 # A part of NonVisual Desktop Access (NVDA)
-# Copyright (C) 2006-2020 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Mesar Hameed, Joseph Lee,
-# Thomas Stivers, Babbage B.V.
+# Copyright (C) 2006-2021 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.
 
@@ -439,7 +439,8 @@ def __init__(self, frame):
 			item = menu_tools.Append(wx.ID_ANY, _("View log"))
 			self.Bind(wx.EVT_MENU, frame.onViewLogCommand, item)
 		# Translators: The label for the menu item to toggle Speech Viewer.
-		item=self.menu_tools_toggleSpeechViewer = menu_tools.AppendCheckItem(wx.ID_ANY, _("Speech viewer"))
+		item = self.menu_tools_toggleSpeechViewer = menu_tools.AppendCheckItem(wx.ID_ANY, _("Speech viewer"))
+		item.Check(speechViewer.isActive)
 		self.Bind(wx.EVT_MENU, frame.onToggleSpeechViewerCommand, item)
 
 		self.menu_tools_toggleBrailleViewer: wx.MenuItem = menu_tools.AppendCheckItem(
diff --git a/source/speechViewer.py b/source/speechViewer.py
--- a/source/speechViewer.py
+++ b/source/speechViewer.py
@@ -1,5 +1,5 @@
 # A part of NonVisual Desktop Access (NVDA)
-# Copyright (C) 2006-2020 NV Access Limited, Thomas Stivers
+# Copyright (C) 2006-2021 NV Access Limited, Thomas Stivers, Accessolutions, Julien Cochuyt
 # This file is covered by the GNU General Public License.
 # See the file COPYING for more details.
 
@@ -38,7 +38,7 @@ def __init__(self, onDestroyCallBack):
 			title=_("NVDA Speech Viewer"),
 			size=dialogSize,
 			pos=dialogPos,
-			style=wx.CAPTION | wx.RESIZE_BORDER | wx.STAY_ON_TOP
+			style=wx.CAPTION | wx.CLOSE_BOX | wx.RESIZE_BORDER | wx.STAY_ON_TOP
 		)
 		self._isDestroyed = False
 		self.onDestroyCallBack = onDestroyCallBack
@@ -97,10 +97,8 @@ def _onDialogActivated(self, evt):
 			self.shouldShowOnStartupCheckBox.SetFocus()
 
 	def onClose(self, evt):
-		if not evt.CanVeto():
-			deactivate()
-			return
-		evt.Veto()
+		assert isActive, "Cannot close Speech Viewer as it is already inactive"
+		deactivate()
 
 	def onShouldShowOnStartupChanged(self, evt):
 		config.conf["speechViewer"]["showSpeechViewerAtStartup"] = self.shouldShowOnStartupCheckBox.IsChecked()
@@ -185,4 +183,3 @@ def deactivate():
 	# #7077: If the window is destroyed, text control will be gone, so save speech viewer position before destroying the window.
 	_guiFrame.savePositionInformation()
 	_guiFrame.Destroy()
-	isActive = False


Test patch: