From be5769f85620cbdc0f9735610fa55cec8dcbb9bb Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Tue, 13 Aug 2024 02:06:53 -0400 Subject: [PATCH 01/11] Add Copy and Close buttons to ui.browseableMessage, and a few of its instances. * Report link destination, Speak selection command, & character formatting browseableMessages given close and copy buttons. * Added a new browsable message component failure messaging function. * Used a live region to alert users about the status of the copy operation. * Added some further error handling, as proposed by the AI. * Change onkeypress to onkeydown, in order to pick up modifier keys in message.html. * Used Ctrl+Shift+C for the copy button accelerator key, because Alt+C makes a Windows error ding. * Switch to innerText from innerHTML where possible. * Remove invalid language attribute on body element in message.html per review. * Solve for MSHTML bug in the use of aria-labelledby. * Close the MSHTML Window if args wasn't provided for some reason. --- source/globalCommands.py | 9 ++- source/message.html | 71 +++++++++++++++++++-- source/ui.py | 134 +++++++++++++++++++++++++++++++-------- user_docs/en/changes.md | 2 + 4 files changed, 181 insertions(+), 35 deletions(-) diff --git a/source/globalCommands.py b/source/globalCommands.py index 6507ac4512d..8759807a22b 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -340,9 +340,8 @@ def script_reportCurrentSelection(self, gesture): if scriptCount == 0: speech.speakTextSelected(info.text) braille.handler.message(selectMessage) - elif scriptCount == 3: - ui.browseableMessage(info.text) + ui.browseableMessage(info.text, copyButton=True, closeButton=True) return elif len(info.text) < speech.speech.MAX_LENGTH_FOR_SELECTION_REPORTING: @@ -2441,6 +2440,8 @@ def _reportFormattingHelper(self, info, browseable=False): message, # Translators: title for formatting information dialog. _("Formatting"), + copyButton=True, + closeButton=True, ) @staticmethod @@ -4140,7 +4141,7 @@ def script_reportLinkDestination( ) -> None: """Generates a ui.message or ui.browseableMessage of a link's destination, if focus or caret is positioned on a link, or an element with an included link such as a graphic. - @param forceBrowseable: skips the press once check, and displays the browseableMessage version. + :param forceBrowseable: skips the press once check, and displays the browseableMessage version. """ try: ti: textInfos.TextInfo = api.getCaretPosition() @@ -4174,6 +4175,8 @@ def script_reportLinkDestination( # Translators: Informs the user that the window contains the destination of the # link with given title title=_("Destination of: {name}").format(name=obj.name), + closeButton=True, + copyButton=True, ) elif presses == 0: # One press ui.message(linkDestination) # Speak the link diff --git a/source/message.html b/source/message.html index 0e9396315e5..c314eee9965 100644 --- a/source/message.html +++ b/source/message.html @@ -4,24 +4,83 @@ - -
+ +
+ + + + + diff --git a/source/ui.py b/source/ui.py index 83bca0c26c8..c042a56e042 100644 --- a/source/ui.py +++ b/source/ui.py @@ -46,10 +46,11 @@ HTMLDLG_VERIFY = 0x0100 -def _warnBrowsableMessageNotAvailableOnSecureScreens(title: Optional[str]) -> None: +def _warnBrowsableMessageNotAvailableOnSecureScreens(title: str | None = None) -> None: """Warn the user that a browsable message could not be shown on a secure screen (sign-on screen / UAC prompt). - @param title: If provided, the title of the browsable message to give the user more context. + + :param title: If provided, the title of the browsable message to give the user more context. """ log.warning( "While on secure screens browsable messages can not be used." @@ -88,13 +89,67 @@ def _warnBrowsableMessageNotAvailableOnSecureScreens(title: Optional[str]) -> No ) -def browseableMessage(message: str, title: Optional[str] = None, isHtml: bool = False) -> None: +def _warnBrowsableMessageComponentFailure(title: str | None = None) -> None: + """Warn the user that a browsable message could not be shown because of a component failure. + + :param title: If provided, the title of the browsable message to give the user more context. + """ + log.warning( + "A browsable message could not be shown because of a component failure." + f" Attempted to open message with title: {title!r}", + ) + + if not title: + browsableMessageUnavailableMsg: str = _( + # Translators: This is the message for a warning shown if NVDA cannot open a browsable message window + # because of a component failure. + "An error has caused this feature to be unavailable at this time. " + "Restarting NVDA or Windows may solve this problem.", + ) + else: + browsableMessageUnavailableMsg: str = _( + # Translators: This is the message for a warning shown if NVDA cannot open a browsable message window + # because of a component failure. This prompt includes the title + # of the Window that could not be opened for context. + # The {title} will be replaced with the title. + # The title may be something like "Formatting". + "An error has caused this feature ({title}) to be unavailable at this time. " + "Restarting NVDA or Windows may solve this problem.", + ) + browsableMessageUnavailableMsg = browsableMessageUnavailableMsg.format(title=title) + + import wx # Late import to prevent circular dependency. + import gui # Late import to prevent circular dependency. + + log.debug("Presenting browsable message unavailable warning.") + gui.messageBox( + browsableMessageUnavailableMsg, + # Translators: This is the title for a warning dialog, shown if NVDA cannot open a browsable message. + caption=_("Feature unavailable."), + style=wx.ICON_ERROR | wx.OK, + ) + + +def browseableMessage( + message: str, + title: str | None = None, + isHtml: bool = False, + closeButton: bool = False, + copyButton: bool = False, +) -> None: """Present a message to the user that can be read in browse mode. The message will be presented in an HTML document. - @param message: The message in either html or text. - @param title: The title for the message. - @param isHtml: Whether the message is html + + :param message: The message in either html or text. + :param title: The title for the message, defaults to "NVDA Message". + :param isHtml: Whether the message is html, defaults to False. + :param closeButton: Whether to include a "close" button, defaults to False. + :param copyButton: Whether to include a "copy" (to clipboard) button, defaults to False. """ + if title is None: + # Translators: The title for the dialog used to present general NVDA messages in browse mode. + title = _("NVDA Message") + if isRunningOnSecureDesktop(): import wx # Late import to prevent circular dependency. @@ -103,37 +158,64 @@ def browseableMessage(message: str, title: Optional[str] = None, isHtml: bool = htmlFileName = os.path.join(globalVars.appDir, "message.html") if not os.path.isfile(htmlFileName): + _warnBrowsableMessageComponentFailure(title) raise LookupError(htmlFileName) + moniker = POINTER(IUnknown)() - windll.urlmon.CreateURLMonikerEx(0, htmlFileName, byref(moniker), URL_MK_UNIFORM) - if not title: - # Translators: The title for the dialog used to present general NVDA messages in browse mode. - title = _("NVDA Message") - if not isHtml: - message = f"
{escape(message)}
" + try: + windll.urlmon.CreateURLMonikerEx(0, htmlFileName, byref(moniker), URL_MK_UNIFORM) + except Exception as e: + log.error(f"Failed to create URL moniker: {e}") + _warnBrowsableMessageComponentFailure(title) + return + try: d = comtypes.client.CreateObject("Scripting.Dictionary") except (COMError, OSError): log.error("Scripting.Dictionary component unavailable", exc_info=True) - # Store the module level message function in a new variable since it is masked by a local variable with - # the same name - messageFunction = globals()["message"] - # Translators: reported when unable to display a browsable message. - messageFunction(_("Unable to display browseable message")) + _warnBrowsableMessageComponentFailure(title) return d.add("title", title) + + if not isHtml: + message = f"
{escape(message)}
" + else: + log.warning("Passing raw HTML to ui.browseableMessage!") d.add("message", message) + + # Translators: A notice to the user that a copy operation succeeded. + d.add("copySuccessfulAlertText", _("Text copied.")) + # Translators: A notice to the user that a copy operation failed. + d.add("copyFailedAlertText", _("Couldn't copy to clipboard.")) + if closeButton: + # Translators: The text of a button which closes the window. + d.add("closeButtonText", _("Close")) + if copyButton: + # Translators: The label of a button to copy the text of the window to the clipboard. + d.add("copyButtonText", _("Copy")) + # Translators: A portion of an accessibility label for the "Copy" button, + # describing the key to press to activate the button. Currently, this key may only be Ctrl+Shift+C. + # Translation makes sense here if the Control or Shift keys are called something else in a + # given language; or to set this to the empty string if that key combination is unavailable on some keyboard. + d.add("copyButtonAcceleratorAccessibilityLabel", _("Ctrl+Shift+C")) + dialogArgsVar = automation.VARIANT(d) gui.mainFrame.prePopup() - windll.mshtml.ShowHTMLDialogEx( - gui.mainFrame.Handle, - moniker, - HTMLDLG_MODELESS, - byref(dialogArgsVar), - DIALOG_OPTIONS, - None, - ) - gui.mainFrame.postPopup() + try: + windll.mshtml.ShowHTMLDialogEx( + gui.mainFrame.Handle, + moniker, + HTMLDLG_MODELESS, + byref(dialogArgsVar), + DIALOG_OPTIONS, + None, + ) + except Exception as e: + log.error(f"Failed to show HTML dialog: {e}") + _warnBrowsableMessageComponentFailure(title) + return + finally: + gui.mainFrame.postPopup() def message( diff --git a/user_docs/en/changes.md b/user_docs/en/changes.md index b1ae8743159..8992219a8b5 100644 --- a/user_docs/en/changes.md +++ b/user_docs/en/changes.md @@ -24,6 +24,7 @@ The available options are: * eSpeak NG has been updated to 1.52-dev commit `961454ff`. (#16775) * Added new languages Faroese and Xextan. * When using a multi-line braille display via the standard HID braille driver, all lines of cells will be used. (#16993, @alexmoon) +* The Report link destination, Character formatting information, and Speak selection dialogs, now include "Close" and "Copy" buttons for user convenience. (#placeholder, @XLTechie) ### Bug Fixes @@ -59,6 +60,7 @@ Please refer to [the developer guide](https://www.nvaccess.org/files/nvda/docume * It is now possible to redirect objects retrieved from on-screen coordinates, by using the `NVDAObject.objectFromPointRedirect` method. (#16788, @Emil-18) * Running SCons with the parameter `--all-cores` will automatically pick the maximum number of available CPU cores. (#16943, #16868, @LeonarddeR) * Developer info now includes information on app architecture (such as AMD64) for the navigator object. (#16488, @josephsl) +* `ui.browseableMessage` may now be called with options to present a button for copying to clipboard, and/or a button for closing the window. (#placeholder, @XLTechie) #### Deprecations From 5b760264045f6758e3b6d620a99d9cd52d9b8963 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Thu, 15 Aug 2024 05:45:55 -0400 Subject: [PATCH 02/11] Make the _warningBrowseableMessage* functions self-contained with respect to wx. --- source/message.html | 2 ++ source/ui.py | 30 +++++++++++++----------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/source/message.html b/source/message.html index c314eee9965..ae8ca133949 100644 --- a/source/message.html +++ b/source/message.html @@ -45,6 +45,8 @@ function windowOnLoad() { if (args) { document.title = args.item('title'); + // We can't use innerText, even for text-only nodes, + // because of #12004. messageDiv.innerHTML = args.item('message'); // If caller wants a close button if (args.item('closeButtonText') != null) { diff --git a/source/ui.py b/source/ui.py index c042a56e042..cbe81c39a99 100644 --- a/source/ui.py +++ b/source/ui.py @@ -73,17 +73,16 @@ def _warnBrowsableMessageNotAvailableOnSecureScreens(title: str | None = None) - # The title may be something like "Formatting". "This feature ({title}) is unavailable while on secure screens" " such as the sign-on screen or UAC prompt.", - ) - browsableMessageUnavailableMsg = browsableMessageUnavailableMsg.format(title=title) + ).format(title=title) import wx # Late import to prevent circular dependency. import gui # Late import to prevent circular dependency. log.debug("Presenting browsable message unavailable warning.") - gui.messageBox( + wx.CallAfter( + gui.messageBox, browsableMessageUnavailableMsg, - # Translators: This is the title for a warning dialog, shown if NVDA cannot open a browsable message - # dialog. + # Translators: This is the title for a warning dialog, shown if NVDA cannot open a browsable message. caption=_("Feature unavailable."), style=wx.ICON_ERROR | wx.OK, ) @@ -115,14 +114,13 @@ def _warnBrowsableMessageComponentFailure(title: str | None = None) -> None: # The title may be something like "Formatting". "An error has caused this feature ({title}) to be unavailable at this time. " "Restarting NVDA or Windows may solve this problem.", - ) - browsableMessageUnavailableMsg = browsableMessageUnavailableMsg.format(title=title) + ).format(title=title) + log.debug("Presenting browsable message unavailable warning.") import wx # Late import to prevent circular dependency. import gui # Late import to prevent circular dependency. - - log.debug("Presenting browsable message unavailable warning.") - gui.messageBox( + wx.CallAfter( + gui.messageBox, browsableMessageUnavailableMsg, # Translators: This is the title for a warning dialog, shown if NVDA cannot open a browsable message. caption=_("Feature unavailable."), @@ -146,14 +144,8 @@ def browseableMessage( :param closeButton: Whether to include a "close" button, defaults to False. :param copyButton: Whether to include a "copy" (to clipboard) button, defaults to False. """ - if title is None: - # Translators: The title for the dialog used to present general NVDA messages in browse mode. - title = _("NVDA Message") - if isRunningOnSecureDesktop(): - import wx # Late import to prevent circular dependency. - - wx.CallAfter(_warnBrowsableMessageNotAvailableOnSecureScreens, title) + _warnBrowsableMessageNotAvailableOnSecureScreens(title) return htmlFileName = os.path.join(globalVars.appDir, "message.html") @@ -175,6 +167,10 @@ def browseableMessage( log.error("Scripting.Dictionary component unavailable", exc_info=True) _warnBrowsableMessageComponentFailure(title) return + + if title is None: + # Translators: The title for the dialog used to present general NVDA messages in browse mode. + title = _("NVDA Message") d.add("title", title) if not isHtml: From 5b9a8984d48e8b39e810140fc80cef48a494cccd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 18 Aug 2024 06:13:27 +0000 Subject: [PATCH 03/11] Pre-commit auto-fix --- source/ui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/ui.py b/source/ui.py index cbe81c39a99..7afac03819c 100644 --- a/source/ui.py +++ b/source/ui.py @@ -119,6 +119,7 @@ def _warnBrowsableMessageComponentFailure(title: str | None = None) -> None: log.debug("Presenting browsable message unavailable warning.") import wx # Late import to prevent circular dependency. import gui # Late import to prevent circular dependency. + wx.CallAfter( gui.messageBox, browsableMessageUnavailableMsg, From b4a462e97fcb61feb9c24583d19de69668021dc9 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Sun, 18 Aug 2024 02:20:19 -0400 Subject: [PATCH 04/11] Changes --- user_docs/en/changes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_docs/en/changes.md b/user_docs/en/changes.md index 8992219a8b5..1fc5f271600 100644 --- a/user_docs/en/changes.md +++ b/user_docs/en/changes.md @@ -24,7 +24,7 @@ The available options are: * eSpeak NG has been updated to 1.52-dev commit `961454ff`. (#16775) * Added new languages Faroese and Xextan. * When using a multi-line braille display via the standard HID braille driver, all lines of cells will be used. (#16993, @alexmoon) -* The Report link destination, Character formatting information, and Speak selection dialogs, now include "Close" and "Copy" buttons for user convenience. (#placeholder, @XLTechie) +* The Report link destination, Character formatting information, and Speak selection dialogs, now include "Close" and "Copy" buttons for user convenience. (#17018, @XLTechie) ### Bug Fixes @@ -60,7 +60,7 @@ Please refer to [the developer guide](https://www.nvaccess.org/files/nvda/docume * It is now possible to redirect objects retrieved from on-screen coordinates, by using the `NVDAObject.objectFromPointRedirect` method. (#16788, @Emil-18) * Running SCons with the parameter `--all-cores` will automatically pick the maximum number of available CPU cores. (#16943, #16868, @LeonarddeR) * Developer info now includes information on app architecture (such as AMD64) for the navigator object. (#16488, @josephsl) -* `ui.browseableMessage` may now be called with options to present a button for copying to clipboard, and/or a button for closing the window. (#placeholder, @XLTechie) +* `ui.browseableMessage` may now be called with options to present a button for copying to clipboard, and/or a button for closing the window. (#17018, @XLTechie) #### Deprecations From eacc8c1ccdc7c4a8cb4d0cd3149040568c03ccde Mon Sep 17 00:00:00 2001 From: "Luke Davis, Open Source Systems" Date: Mon, 19 Aug 2024 02:00:04 -0400 Subject: [PATCH 05/11] New handleKeyPress code in JS, as proposed by the AI. --- source/message.html | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/message.html b/source/message.html index ae8ca133949..560c15ae8ae 100644 --- a/source/message.html +++ b/source/message.html @@ -9,10 +9,15 @@ var copyStatusTimeout = 5000; // milliseconds before copy status message goes away function handleKeyPress(e) { + /* + This code should be effective for modern browsers (EdgeWebView2), + while also serving in old IE (MSHTML) contexts. + */ e = e || window.event; - if(e.keyCode == 27){ // code for escape - window.close(); - } else if (e.shiftKey && e.ctrlKey && e.keyCode == 67) { // code for Ctrl+Shift+C + var key = e.key || e.keyCode; + if (key === 'Escape' || key === 27) { // code for escape + onCloseButtonPress(); + } else if (e.shiftKey && e.ctrlKey && (key === 'C' || key === 67)) { // code for Ctrl+Shift+C onCopyButtonPress(); } } From f58e85fdc7393093b69c041edef4f2890a211243 Mon Sep 17 00:00:00 2001 From: "Luke Davis, Open Source Systems" Date: Mon, 19 Aug 2024 02:04:20 -0400 Subject: [PATCH 06/11] AI proposed more modern copy code. AI isn't sure if it will work. --- source/message.html | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/source/message.html b/source/message.html index 560c15ae8ae..dfd7675c72f 100644 --- a/source/message.html +++ b/source/message.html @@ -23,21 +23,18 @@ } function onCopyButtonPress() { - // Copy code came from http://www.codestore.net/store.nsf/unid/DOMM-4QHQE8/ - var rng = document.body.createTextRange(); - rng.moveToElementText(messageDiv); - rng.scrollIntoView(); - rng.select(); - var success = rng.execCommand("Copy"); - rng.collapse(false); - rng.select(); - copyStatusDiv.innerHTML = '

'; - if (success) { // Notify the user about the copy result - copyStatusText.innerText = args.item('copySuccessfulAlertText'); - } else { + var range = document.createRange(); + var selection = window.getSelection(); + range.selectNodeContents(messageDiv); + selection.removeAllRanges(); + selection.addRange(range); + try { + var success = document.execCommand('copy'); + copyStatusDiv.innerHTML = '

'; + copyStatusText.innerText = success ? args.item('copySuccessfulAlertText') : args.item('copyFailedAlertText'); + } catch (err) { copyStatusText.innerText = args.item('copyFailedAlertText'); } - // Time out the user alert message setTimeout(function() { copyStatusDiv.innerHTML = ''; }, copyStatusTimeout); From c071400f97cb22580e9dcc69985da7ac4ee7f905 Mon Sep 17 00:00:00 2001 From: "Luke Davis, Open Source Systems" Date: Mon, 19 Aug 2024 02:07:10 -0400 Subject: [PATCH 07/11] Revert "AI proposed more modern copy code. AI wasn't sure if it worked, and it doesn't. This reverts commit f58e85fdc7393093b69c041edef4f2890a211243. --- source/message.html | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/source/message.html b/source/message.html index dfd7675c72f..560c15ae8ae 100644 --- a/source/message.html +++ b/source/message.html @@ -23,18 +23,21 @@ } function onCopyButtonPress() { - var range = document.createRange(); - var selection = window.getSelection(); - range.selectNodeContents(messageDiv); - selection.removeAllRanges(); - selection.addRange(range); - try { - var success = document.execCommand('copy'); - copyStatusDiv.innerHTML = '

'; - copyStatusText.innerText = success ? args.item('copySuccessfulAlertText') : args.item('copyFailedAlertText'); - } catch (err) { + // Copy code came from http://www.codestore.net/store.nsf/unid/DOMM-4QHQE8/ + var rng = document.body.createTextRange(); + rng.moveToElementText(messageDiv); + rng.scrollIntoView(); + rng.select(); + var success = rng.execCommand("Copy"); + rng.collapse(false); + rng.select(); + copyStatusDiv.innerHTML = '

'; + if (success) { // Notify the user about the copy result + copyStatusText.innerText = args.item('copySuccessfulAlertText'); + } else { copyStatusText.innerText = args.item('copyFailedAlertText'); } + // Time out the user alert message setTimeout(function() { copyStatusDiv.innerHTML = ''; }, copyStatusTimeout); From b50e031eca9c31f4c03cd9d519a65915ebd6d2e8 Mon Sep 17 00:00:00 2001 From: "Luke Davis, Open Source Systems" Date: Mon, 19 Aug 2024 02:26:52 -0400 Subject: [PATCH 08/11] AI suggested improvement to error logging detail. --- source/ui.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/ui.py b/source/ui.py index 7afac03819c..bce669f4750 100644 --- a/source/ui.py +++ b/source/ui.py @@ -157,8 +157,12 @@ def browseableMessage( moniker = POINTER(IUnknown)() try: windll.urlmon.CreateURLMonikerEx(0, htmlFileName, byref(moniker), URL_MK_UNIFORM) + except OSError as e: + log.error(f"OS error during URL moniker creation: {e}") + _warnBrowsableMessageComponentFailure(title) + return except Exception as e: - log.error(f"Failed to create URL moniker: {e}") + log.error(f"Unexpected error during URL moniker creation: {e}") _warnBrowsableMessageComponentFailure(title) return From 2eab07657dc779365033b7b3daef2617b5cc1448 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Thu, 22 Aug 2024 02:51:08 -0400 Subject: [PATCH 09/11] Fix a bug where copy accelerator still worked, even if button wasn't shown. --- source/message.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/message.html b/source/message.html index 560c15ae8ae..ada156164e7 100644 --- a/source/message.html +++ b/source/message.html @@ -7,6 +7,7 @@ // Though these should be constants, MSHTML does not support const as of 2024 var args = window.dialogArguments; var copyStatusTimeout = 5000; // milliseconds before copy status message goes away +var copyEnabled = false; function handleKeyPress(e) { /* @@ -23,6 +24,7 @@ } function onCopyButtonPress() { + if (!copyEnabled) return; // Copy code came from http://www.codestore.net/store.nsf/unid/DOMM-4QHQE8/ var rng = document.body.createTextRange(); rng.moveToElementText(messageDiv); @@ -61,6 +63,7 @@ } // If caller wants a copy to clip button if (args.item('copyButtonText') != null) { + copyEnabled = true; copyButton.innerText = args.item('copyButtonText'); // Assign the (translated) label copyButtonSpan.style.display = 'inline'; // Display the button copyStatusDiv.style.display = 'block'; // Display the copy status From ce75b547090fdc648cf3584df985e1b2026b7c97 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Wed, 28 Aug 2024 04:09:17 -0400 Subject: [PATCH 10/11] Changes update --- user_docs/en/changes.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/user_docs/en/changes.md b/user_docs/en/changes.md index 2c37b9f9f60..2939042c91c 100644 --- a/user_docs/en/changes.md +++ b/user_docs/en/changes.md @@ -8,6 +8,10 @@ * In Mozilla Firefox, NVDA will report the highlighted text when a URL containing a text fragment is visited. (#16910, @jcsteh) +### Changes + +* The Report link destination, Character formatting information, and Speak selection dialogs, now include "Close" and "Copy" buttons for user convenience. (#17018, @XLTechie) + ### Bug Fixes * Native support for the Dot Pad tactile graphics device from Dot Inc as a multiline braille display. (#17007) @@ -18,6 +22,7 @@ Please refer to [the developer guide](https://www.nvaccess.org/files/nvda/docume * Note: this is an Add-on API compatibility breaking release. Add-ons will need to be re-tested and have their manifest updated. +* `ui.browseableMessage` may now be called with options to present a button for copying to clipboard, and/or a button for closing the window. (#17018, @XLTechie) #### API Breaking Changes @@ -69,7 +74,6 @@ There have also been a number of fixes, including to mouse tracking in Firefox, * eSpeak NG has been updated to 1.52-dev commit `961454ff`. (#16775) * Added new languages Faroese and Xextan. * When using a multi-line braille display via the standard HID braille driver, all lines of cells will be used. (#16993, @alexmoon) -* The Report link destination, Character formatting information, and Speak selection dialogs, now include "Close" and "Copy" buttons for user convenience. (#17018, @XLTechie) ### Bug Fixes @@ -106,7 +110,6 @@ Please refer to [the developer guide](https://www.nvaccess.org/files/nvda/docume * It is now possible to redirect objects retrieved from on-screen coordinates, by using the `NVDAObject.objectFromPointRedirect` method. (#16788, @Emil-18) * Running SCons with the parameter `--all-cores` will automatically pick the maximum number of available CPU cores. (#16943, #16868, @LeonarddeR) * Developer info now includes information on app architecture (such as AMD64) for the navigator object. (#16488, @josephsl) -* `ui.browseableMessage` may now be called with options to present a button for copying to clipboard, and/or a button for closing the window. (#17018, @XLTechie) #### Deprecations From 28baf8c5123943c6549ed52b26682e228203712c Mon Sep 17 00:00:00 2001 From: Sean Budd Date: Thu, 29 Aug 2024 13:45:00 +1000 Subject: [PATCH 11/11] Update source/ui.py --- source/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/ui.py b/source/ui.py index bce669f4750..c724d2486a8 100644 --- a/source/ui.py +++ b/source/ui.py @@ -198,7 +198,7 @@ def browseableMessage( # describing the key to press to activate the button. Currently, this key may only be Ctrl+Shift+C. # Translation makes sense here if the Control or Shift keys are called something else in a # given language; or to set this to the empty string if that key combination is unavailable on some keyboard. - d.add("copyButtonAcceleratorAccessibilityLabel", _("Ctrl+Shift+C")) + d.add("copyButtonAcceleratorAccessibilityLabel", _("control+shift+c")) dialogArgsVar = automation.VARIANT(d) gui.mainFrame.prePopup()