From 8228ae3537819b1c961ee3b53a7ac8c9e8366f6f Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Tue, 6 Feb 2024 10:24:51 +1000 Subject: [PATCH] Several fixes to native selection mode (#16129) Fixes #16064 Fixes #16097 Summary of the issue: Native selection mode can be enabled in unsupported Gecko, such as Thunderbird 115.x #16064: turning on native selection mode in thunderbird 115 would cause errors when moving with the arrow keys as IAccessibleTextSelectionContainer is unavailable. Wrong message reported when toggling native selection in Word #16097: NVDA is misleading in MS Word browse mode when it says that native selection mode is not supported when trying to toggle it on, as MS word Browse mode does move the caret / selection, thus it has always used native selection mode.. It is just you can't toggle it off. Description of user facing changes 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. NVDA no longer incorrectly alerts the user that native selection mode is not supported in Microsoft Word. Rather, the message state that it canot be turned off. when copying text with control+c in Microsoft Word with Browse mode on, formatting is now also copied, fulfilling the expectation of a native selection mode. Description of development approach Gecko virtualBuffer's updateAppSelection method: if the selection is collapsed, don't try fetching information for the selection, instead just clear the app selection. This error was previously silently ignored. In UIA Browse mode documents, _nativeAppSelectionMode is now set to True, as this correctly reflects that UIA browse mode documents move the native selection. BrowseMode document's toggleNativeSelectionMode script: Tailor the message reported if native selection mode is not supported (by looking at _nativeSelectionMode). If _nativeSelectionMode is True, then the message is changed to state it can't be turned off. If updateappSelection fails when turning on native selection mode, log the error, turn it off, and alert the user that it is not supported. In MS word, suppress the "copy" UIA notification, if in MS word Browse mode. Otherwise there would be double speaking along with the message in cursorManager's copyToclipboard script when doing a native copy. --- source/NVDAObjects/UIA/web.py | 1 + source/NVDAObjects/UIA/wordDocument.py | 6 +++++ source/UIAHandler/browseMode.py | 1 + source/browseMode.py | 29 ++++++++++++++------- source/virtualBuffers/gecko_ia2.py | 36 ++++++++++++++------------ user_docs/en/changes.t2t | 1 + 6 files changed, 48 insertions(+), 26 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..3b3f0ab891f 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 cannot be turned off 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 + 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.""" diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t index 3ce14adba67..c4bc9894c7c 100644 --- a/user_docs/en/changes.t2t +++ b/user_docs/en/changes.t2t @@ -77,6 +77,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)