Skip to content

Commit

Permalink
Fix multiple issues with native selection mode in Browse mode, includ…
Browse files Browse the repository at this point in the history
…ing:

* 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.
  • Loading branch information
michaelDCurran committed Feb 5, 2024
1 parent 4c2d7c3 commit 33caf1c
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 27 deletions.
1 change: 1 addition & 0 deletions source/NVDAObjects/UIA/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions source/NVDAObjects/UIA/wordDocument.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions source/UIAHandler/browseMode.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
31 changes: 20 additions & 11 deletions source/browseMode.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
36 changes: 20 additions & 16 deletions source/virtualBuffers/gecko_ia2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down

0 comments on commit 33caf1c

Please sign in to comment.