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..ada156164e7 100644 --- a/source/message.html +++ b/source/message.html @@ -4,24 +4,93 @@
{escape(message)}" + 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"Unexpected error during URL moniker creation: {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 + + 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: + 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", _("control+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 07276751878..f3e22da436b 100644 --- a/user_docs/en/changes.md +++ b/user_docs/en/changes.md @@ -10,7 +10,8 @@ ### Changes -* The exit dialog now allows you to restart NVDA with add-ons disabled and debug logging enabled simultaneously. (#11538, @CyrilleB79) +* The Report link destination, Character formatting information, and Speak selection dialogs, now include "Close" and "Copy" buttons for user convenience. (#17018, @XLTechie) +* The exit dialog now allows you to restart NVDA with add-ons disabled and debug logging enabled simultaneously. (#11538, @CyrilleB79)r ### Bug Fixes @@ -22,6 +23,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