From 33caf1ca929aa32bfae3934cdc109745b030791a Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Mon, 5 Feb 2024 13:04:00 +1000 Subject: [PATCH 1/4] Fix multiple issues with native selection mode in Browse mode, including: * In firefox, If NVDA fails to update the native selection when turning on native selection mode, it is now left off, and the user is notified that native selection mode is not supported. this stops errors when moving with the arrow keys in Thunderbird after turning on native selection mode. * Exceptions are logged when turning on and off native selection mode. * If native selection mode is not supported when trying to turn it on, NVDA honors the _nativeAppSelectionMode boolean to tailor the message to state that it cannot be turned off, if it is on (though not supported). * UIA browse mode documents (E.g. MS word) set _nativeSelectionMode to True, though it cannot be turned off. This not only provides a better message to the user if they try and toggle it, but it also has the advantage that copying with control+c from MS word browse mode, will now also copy with formatting. --- source/NVDAObjects/UIA/web.py | 1 + source/NVDAObjects/UIA/wordDocument.py | 6 +++++ source/UIAHandler/browseMode.py | 1 + source/browseMode.py | 31 ++++++++++++++-------- source/virtualBuffers/gecko_ia2.py | 36 ++++++++++++++------------ 5 files changed, 48 insertions(+), 27 deletions(-) diff --git a/source/NVDAObjects/UIA/web.py b/source/NVDAObjects/UIA/web.py index f4d4d4f767a..1f973f7a7c7 100644 --- a/source/NVDAObjects/UIA/web.py +++ b/source/NVDAObjects/UIA/web.py @@ -487,6 +487,7 @@ def HeadingControlQuicknavIterator(itemType, document, position, direction="next class UIAWebTreeInterceptor(cursorManager.ReviewCursorManager, UIABrowseModeDocument): TextInfo = UIABrowseModeDocumentTextInfo + _nativeAppSelectionMode = False def makeTextInfo(self, position): try: diff --git a/source/NVDAObjects/UIA/wordDocument.py b/source/NVDAObjects/UIA/wordDocument.py index d5b04ec4191..9f17756fb9f 100644 --- a/source/NVDAObjects/UIA/wordDocument.py +++ b/source/NVDAObjects/UIA/wordDocument.py @@ -554,6 +554,12 @@ def event_UIA_notification(self, activityId=None, **kwargs): # such as "delete back word" when Control+Backspace is pressed. if activityId == "AccSN2": # Delete activity ID return + # copy to clipboard + if activityId == 'AccSN3': + ti = self.treeInterceptor + if ti and not ti.passThrough: + # Browse mode provides its own copy to clipboard message. + return super(WordDocument, self).event_UIA_notification(**kwargs) # The following overide of the EditableText._caretMoveBySentenceHelper private method diff --git a/source/UIAHandler/browseMode.py b/source/UIAHandler/browseMode.py index 9fc54eed76a..60a6c08e4ad 100644 --- a/source/UIAHandler/browseMode.py +++ b/source/UIAHandler/browseMode.py @@ -373,6 +373,7 @@ class UIABrowseModeDocument(UIADocumentWithTableNavigation,browseMode.BrowseMode # UIA browseMode documents cannot remember caret positions across loads (I.e. when going back a page in Edge) # Because UIA TextRanges are opaque and are tied specifically to one particular document. shouldRememberCaretPositionAcrossLoads=False + _nativeAppSelectionMode = True def event_UIA_activeTextPositionChanged(self, obj, nextHandler, textRange=None): if not self.isReady: diff --git a/source/browseMode.py b/source/browseMode.py index 46ed2320b0a..e223da9dfaa 100644 --- a/source/browseMode.py +++ b/source/browseMode.py @@ -2036,21 +2036,30 @@ def clearAppSelection(self): ) def script_toggleNativeAppSelectionMode(self, gesture: inputCore.InputGesture): if not self._nativeAppSelectionModeSupported: - # Translators: the message when native selection mode is not available in this browse mode document. - ui.message(_("Native selection mode unsupported in this document")) + if not self._nativeAppSelectionMode: + # Translators: the message when native selection mode is not available in this browse mode document. + ui.message(_("Native selection mode unsupported in this browse mode document")) + else: + # Translators: the message when native selection mode is not available in this browse mode document. + ui.message(_("Native selection mode cannot be turned off in this browse mode document")) return - self._nativeAppSelectionMode = not self._nativeAppSelectionMode - if self._nativeAppSelectionMode: - # Translators: reported when native selection mode is toggled on. - ui.message(_("Native app selection mode enabled.")) + nativeAppSelectionModeOn = not self._nativeAppSelectionMode + if nativeAppSelectionModeOn: try: self.updateAppSelection() except NotImplementedError: - pass + log.debugWarning("updateAppSelection failed", exc_info=True) + # Translators: the message when native selection mode is not available in this browse mode document. + ui.message(_("Native selection mode unsupported in this document")) + return + self._nativeAppSelectionMode = True + # Translators: reported when native selection mode is toggled on. + ui.message(_("Native app selection mode enabled")) else: - # Translators: reported when native selection mode is toggled off. - ui.message(_("Native app selection mode disabled.")) try: self.clearAppSelection() - except NotImplementedError: - pass + except (NotImplementedError, COMError): + log.debugWarning("clearAppSelection failed", exc_info=True) + self._nativeAppSelectionMode = False + # Translators: reported when native selection mode is toggled off. + ui.message(_("Native app selection mode disabled")) diff --git a/source/virtualBuffers/gecko_ia2.py b/source/virtualBuffers/gecko_ia2.py index e41e3393e4c..8f8277a0427 100755 --- a/source/virtualBuffers/gecko_ia2.py +++ b/source/virtualBuffers/gecko_ia2.py @@ -699,22 +699,26 @@ def updateAppSelection(self): except COMError as e: raise NotImplementedError from e selInfo = self.makeTextInfo(textInfos.POSITION_SELECTION) - selFields = selInfo.getTextWithFields() - ia2Sel = _Ia2Selection() - - log.debug("checking fields...") - self._getStartSelection(ia2Sel, selFields) - self._getEndSelection(ia2Sel, selFields) - - log.debug("setting selection...") - r = IA2TextSelection( - ia2Sel.startObj, - ia2Sel.startOffset, - ia2Sel.endObj, - ia2Sel.endOffset, - False - ) - paccTextSelectionContainer.SetSelections(1, byref(r)) + if not selInfo.isCollapsed: + selFields = selInfo.getTextWithFields() + ia2Sel = _Ia2Selection() + + log.debug("checking fields...") + self._getStartSelection(ia2Sel, selFields) + self._getEndSelection(ia2Sel, selFields) + + log.debug("setting selection...") + r = IA2TextSelection( + ia2Sel.startObj, + ia2Sel.startOffset, + ia2Sel.endObj, + ia2Sel.endOffset, + False + ) + paccTextSelectionContainer.SetSelections(1, byref(r)) + else: # No selection + r = IA2TextSelection(None, 0, None, 0, False) + paccTextSelectionContainer.SetSelections(0, byref(r)) def clearAppSelection(self): """Clear the native selection in the application.""" From 442ee95dce751bd13fa06b0ec25dcc8ea1c7f5a9 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Mon, 5 Feb 2024 15:37:04 +1000 Subject: [PATCH 2/4] Browse mode Toggle native selection script: no need to catch COMError, as the methods already convert ot to NotImplementedError. --- source/browseMode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/browseMode.py b/source/browseMode.py index e223da9dfaa..4f0189389a6 100644 --- a/source/browseMode.py +++ b/source/browseMode.py @@ -2058,7 +2058,7 @@ def script_toggleNativeAppSelectionMode(self, gesture: inputCore.InputGesture): else: try: self.clearAppSelection() - except (NotImplementedError, COMError): + except NotImplementedError: log.debugWarning("clearAppSelection failed", exc_info=True) self._nativeAppSelectionMode = False # Translators: reported when native selection mode is toggled off. From 6a89e65bea8b61369e3c4f9feb28a4b2795c9fa1 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Mon, 5 Feb 2024 15:40:43 +1000 Subject: [PATCH 3/4] Update source/browseMode.py Co-authored-by: Sean Budd --- source/browseMode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/browseMode.py b/source/browseMode.py index 4f0189389a6..3b3f0ab891f 100644 --- a/source/browseMode.py +++ b/source/browseMode.py @@ -2040,7 +2040,7 @@ def script_toggleNativeAppSelectionMode(self, gesture: inputCore.InputGesture): # Translators: the message when native selection mode is not available in this browse mode document. ui.message(_("Native selection mode unsupported in this browse mode document")) else: - # Translators: the message when native selection mode is not available in this browse mode document. + # Translators: the message when native selection mode cannot be turned off in this browse mode document. ui.message(_("Native selection mode cannot be turned off in this browse mode document")) return nativeAppSelectionModeOn = not self._nativeAppSelectionMode From 9d886199fa7083939643ad3cffed3c312b77a0a0 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Tue, 6 Feb 2024 08:30:41 +1000 Subject: [PATCH 4/4] Update what's new --- user_docs/en/changes.t2t | 1 + 1 file changed, 1 insertion(+) diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t index a4ecc5d393c..a09f3b47984 100644 --- a/user_docs/en/changes.t2t +++ b/user_docs/en/changes.t2t @@ -39,6 +39,7 @@ Windows 8.1 is the minimum Windows version supported. - A new Native Selection mode (toggled by ``NVDA+shift+f10``) is now available in NVDA's browse mode for Mozilla Firefox. When turned on, selecting text in browse mode will also manipulate Firefox's own native selection. Copying text with ``control+c`` will pass straight through to Firefox, thus copying the rich content, rather than NVDA's plain text representation. (#15830) +- When copying text in Microsoft Word with NVDA's browse mode enabled, formatting is now also included. (#16129) - A new "on-demand" speech mode has been added. When speech is on-demand, NVDA does not speak automatically (e.g. when moving the cursor) but still speaks when calling commands whose goal is explicitly to report something (e.g. report window title). (#481, @CyrilleB79) - In the Speech category of NVDA's settings, it is now possible to exclude unwanted speech modes from the Cycle speech modes command (``NVDA+s``). (#15806, @lukaszgo1)