@@ -1406,6 +1406,11 @@ def _set_selection(self, info, reason=OutputReason.CARET):
14061406 followBrowseModeFocus = config .conf ["virtualBuffers" ]["autoFocusFocusableElements" ]
14071407 if followBrowseModeFocus or self .passThrough :
14081408 focusObj .setFocus ()
1409+ # Track this object as NVDA having just requested setting focus to it
1410+ # So that when NVDA does receive the focus event for it
1411+ # It can handle it quietly rather than speaking the new focus.
1412+ if followBrowseModeFocus :
1413+ self ._objPendingFocusBeforeActivate = obj
14091414 # Queue the reporting of pass through mode so that it will be spoken after the actual content.
14101415 queueHandler .queueFunction (queueHandler .eventQueue , reportPassThrough , self )
14111416
@@ -1623,12 +1628,30 @@ def event_gainFocus(self, obj, nextHandler):
16231628 self ._replayFocusEnteredEvents ()
16241629 return nextHandler ()
16251630
1626- #We only want to update the caret and speak the field if we're not in the same one as before
1627- caretInfo = self .makeTextInfo (textInfos .POSITION_CARET )
1628- # Expand to one character, as isOverlapping() doesn't treat, for example, (4,4) and (4,5) as overlapping.
1629- caretInfo .expand (textInfos .UNIT_CHARACTER )
1630- isOverlapping = focusInfo .isOverlapping (caretInfo )
1631- if not self ._hadFirstGainFocus or not isOverlapping or (isOverlapping and previousFocusObjIsDefunct ):
1631+ # Save off and clear any previous object that was focused by NVDA
1632+ # and waiting on a focus event.
1633+ objPendingFocusBeforeActivate = self ._objPendingFocusBeforeActivate
1634+ self ._objPendingFocusBeforeActivate = None
1635+
1636+ # We do not want to speak the new focus and update the caret if...
1637+ if not self ._hadFirstGainFocus or previousFocusObjIsDefunct :
1638+ # still initializing or the old focus is dead.
1639+ isOverlapping = False
1640+ elif config .conf ["virtualBuffers" ]["autoFocusFocusableElements" ]:
1641+ # if this focus event was caused by NVDA setting the focus itself
1642+ # Due to auto focus focusable elements option being enabled,
1643+ # And we detect that the caret was already positioned within the focus.
1644+ # Note that this is not the default and may be removed in future.
1645+ caretInfo = self .makeTextInfo (textInfos .POSITION_CARET )
1646+ # Expand to one character, as isOverlapping() doesn't treat, for example, (4,4) and (4,5) as overlapping.
1647+ caretInfo .expand (textInfos .UNIT_CHARACTER )
1648+ isOverlapping = focusInfo .isOverlapping (caretInfo )
1649+ else :
1650+ # if this focus event was caused by NVDA setting the focus itself
1651+ # due to activation or applications key etc.
1652+ isOverlapping = (obj == objPendingFocusBeforeActivate )
1653+
1654+ if not isOverlapping :
16321655 # The virtual caret is not within the focus node.
16331656 oldPassThrough = self .passThrough
16341657 passThrough = self .shouldPassThrough (obj , reason = OutputReason .FOCUS )
@@ -1666,9 +1689,9 @@ def event_gainFocus(self, obj, nextHandler):
16661689 # Note: this is usually called after the caret movement.
16671690 vision .handler .handleGainFocus (obj )
16681691 elif (
1669- self . _objPendingFocusBeforeActivate
1670- and obj == self . _objPendingFocusBeforeActivate
1671- and obj is not self . _objPendingFocusBeforeActivate
1692+ objPendingFocusBeforeActivate
1693+ and obj == objPendingFocusBeforeActivate
1694+ and obj is not objPendingFocusBeforeActivate
16721695 ):
16731696 # With auto focus focusable elements disabled, when the user activates
16741697 # an element (e.g. by pressing enter) or presses a key which we pass
@@ -1680,10 +1703,9 @@ def event_gainFocus(self, obj, nextHandler):
16801703 # the properties before the activation/key, so use that to speak any
16811704 # changes.
16821705 speech .speakObject (
1683- self . _objPendingFocusBeforeActivate ,
1706+ objPendingFocusBeforeActivate ,
16841707 OutputReason .CHANGE
16851708 )
1686- self ._objPendingFocusBeforeActivate = None
16871709 else :
16881710 self ._replayFocusEnteredEvents ()
16891711 return nextHandler ()
0 commit comments