Skip to content

Commit

Permalink
BrowseModeDocumentTreeInterceptor: delay calls to setFocus when focus…
Browse files Browse the repository at this point in the history
…ing due to the selection moving onto that control, and also ignore gainFocus events for a short time after the setFocus to stop the selection from being bounced around if javascript chooses to bounce the focus somewhere else.
  • Loading branch information
michaelDCurran committed Sep 23, 2015
1 parent 0276d24 commit db1c573
Showing 1 changed file with 41 additions and 6 deletions.
47 changes: 41 additions & 6 deletions source/browseMode.py
Expand Up @@ -8,6 +8,7 @@
import winsound
import time
import wx
import keyboardHandler
import queueHandler
from logHandler import log
import review
Expand All @@ -27,6 +28,7 @@
import speech
import sayAllHandler
import treeInterceptorHandler
import core
import inputCore
import api
from NVDAObjects import NVDAObject
Expand Down Expand Up @@ -935,6 +937,9 @@ def event_treeInterceptor_gainFocus(self):
reportPassThrough(self)
braille.handler.handleGainFocus(self)

def event_treeInterceptor_loseFocus(self):
self._cancelPendingSetFocusToObj()

def event_caret(self, obj, nextHandler):
if self.passThrough:
nextHandler()
Expand Down Expand Up @@ -978,7 +983,35 @@ def _activatePosition(self, info=None):
else:
self._activateNVDAObject(obj)

def _cancelPendingSetFocusToObj(self):
callObj=self._requestSetFocusToObj_callObj
if callObj:
self._requestSetFocusToObj_callObj=None
callObj.Stop()

setFocusDelayLength=0.2 #: length in seconds NVDA should wait to set focus to an object after the browse mode selection is moved.
setFocusIgnoreEventLength=0.1 #: Length in seconds of how long NVDA should ignore new gainFocus events for in browse mode after setting focus to an object due to the browse mode selection moving.
_requestSetFocusToObj_callObj=None
_lastSetFocusTime=0
_lastSetFocusKeyCount=0
def _requestSetFocusToObj(self,obj,reason):
def callback(self,obj,reason):
if not eventHandler.isPendingEvents("gainFocus") and obj != api.getFocusObject():
obj.setFocus()
self._lastSetFocusRequestTime=time.time()
self._lastSetFocusKeyCount=keyboardHandler.keyCounter
self.passThrough=self.shouldPassThrough(obj,reason=reason)
# Queue the reporting of pass through mode so that it will be spoken after the actual content.
queueHandler.queueFunction(queueHandler.eventQueue, reportPassThrough, self)
self._requestSetFocusToObj_callObj=core.callLater(self.setFocusDelayLength*1000,callback,self,obj,reason)

def _get__shouldIgnoreGainFocusEvent(self):
if keyboardHandler.keyCounter==self._lastSetFocusKeyCount or (time.time()-(self._lastSetFocusTime+self.setFocusDelayLength))<0:
return True
return False

def _set_selection(self, info, reason=controlTypes.REASON_CARET):
self._cancelPendingSetFocusToObj()
super(BrowseModeDocumentTreeInterceptor, self)._set_selection(info)
if isScriptWaiting() or not info.isCollapsed:
return
Expand All @@ -994,23 +1027,23 @@ def _set_selection(self, info, reason=controlTypes.REASON_CARET):
focusObj = api.getFocusObject()
if focusObj==self.rootNVDAObject:
return
self.passThrough=self.shouldPassThrough(focusObj,reason=reason)
# Queue the reporting of pass through mode so that it will be spoken after the actual content.
queueHandler.queueFunction(queueHandler.eventQueue, reportPassThrough, self)
else:
self._lastCaretMoveWasFocus = False
focusObj=info.focusableNVDAObjectAtStart
obj=info.NVDAObjectAtStart
if not obj:
log.debugWarning("Invalid NVDAObjectAtStart")
return
if obj==self.rootNVDAObject:
return
if focusObj and not eventHandler.isPendingEvents("gainFocus") and focusObj!=self.rootNVDAObject and focusObj != api.getFocusObject() and self._shouldSetFocusToObj(focusObj):
focusObj.setFocus()
focusObj=info.focusableNVDAObjectAtStart
if focusObj!=self.rootNVDAObject and self._shouldSetFocusToObj(focusObj):
self._requestSetFocusToObj(focusObj,reason)
obj.scrollIntoView()
if self.programmaticScrollMayFireEvent:
self._lastProgrammaticScrollTime = time.time()
self.passThrough=self.shouldPassThrough(focusObj,reason=reason)
# Queue the reporting of pass through mode so that it will be spoken after the actual content.
queueHandler.queueFunction(queueHandler.eventQueue, reportPassThrough, self)

def _shouldSetFocusToObj(self, obj):
"""Determine whether an object should receive focus.
Expand Down Expand Up @@ -1217,6 +1250,8 @@ def event_gainFocus(self, obj, nextHandler):
# Otherwise, if the user switches away and back to this document, the cursor will jump to this node.
# This is not ideal if the user was positioned over a node which cannot receive focus.
return
if self._shouldIgnoreGainFocusEvent:
return
if obj==self.rootNVDAObject:
if self.passThrough:
return nextHandler()
Expand Down

0 comments on commit db1c573

Please sign in to comment.