Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to automatically tether to focus or review #7489

Merged
merged 20 commits into from Jan 15, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/NVDAObjects/IAccessible/mscandui.py
Expand Up @@ -160,7 +160,7 @@ def event_show(self):
reportSelectedCandidate(item)
return
elif config.conf["reviewCursor"]["followFocus"]:
api.setNavigatorObject(candidateList)
api.setNavigatorObject(candidateList, isFocus=True)
elif role==controlTypes.ROLE_MENUBUTTON:
item=candidateList.firstChild.next.next
item=MSCandUI21_candidateMenuItem(IAccessibleObject=item.IAccessibleObject,IAccessibleChildID=item.IAccessibleChildID)
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/UIA/__init__.py
Expand Up @@ -1513,7 +1513,7 @@ def event_UIA_elementSelected(self):
focusControllerFor=api.getFocusObject().controllerFor
if len(focusControllerFor)>0 and focusControllerFor[0].appModule is self.appModule and self.name:
speech.cancelSpeech()
api.setNavigatorObject(self)
api.setNavigatorObject(self, isFocus=True)
self.reportFocus()
# Display results as flash messages.
braille.handler.message(braille.getBrailleTextForProperties(name=self.name, role=self.role, positionInfo=self.positionInfo))
2 changes: 1 addition & 1 deletion source/NVDAObjects/__init__.py
Expand Up @@ -961,7 +961,7 @@ def event_foreground(self):
def event_becomeNavigatorObject(self):
"""Called when this object becomes the navigator object.
"""
braille.handler.handleReviewMove()
braille.handler.handleReviewMove(shouldAutoTether=not eventHandler.lastReviewMoveDueToFollowing)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable name isn't very clear if you don't have the surrounding context of this PR. Maybe choose another name?


def event_valueChange(self):
if self is api.getFocusObject():
Expand Down
6 changes: 4 additions & 2 deletions source/api.py
Expand Up @@ -177,13 +177,14 @@ def getReviewPosition():
globalVars.reviewPosition,globalVars.reviewPositionObj=review.getPositionForCurrentMode(obj)
return globalVars.reviewPosition

def setReviewPosition(reviewPosition,clearNavigatorObject=True):
def setReviewPosition(reviewPosition,clearNavigatorObject=True, isCaret=False):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comma police. :) Also, the docstring needs updating.

"""Sets a TextInfo instance as the review position. if clearNavigatorObject is true, It sets the current navigator object to None so that the next time the navigator object is asked for it fetches it from the review position.
"""
globalVars.reviewPosition=reviewPosition.copy()
globalVars.reviewPositionObj=reviewPosition.obj
if clearNavigatorObject: globalVars.navigatorObject=None
braille.handler.handleReviewMove()
eventHandler.lastReviewMoveDueToFollowing = isCaret
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we be sure that this line and the one below it are only ever executed in one thread at a time?

braille.handler.handleReviewMove(shouldAutoTether=not isCaret)

def getNavigatorObject():
"""Gets the current navigator object. Navigator objects can be used to navigate around the operating system (with the number pad) with out moving the focus. If the navigator object is not set, it fetches it from the review position.
Expand Down Expand Up @@ -227,6 +228,7 @@ def setNavigatorObject(obj,isFocus=False):
if isFocus:
globalVars.reviewPosition=obj.treeInterceptor.makeTextInfo(textInfos.POSITION_CARET)
globalVars.reviewPositionObj=globalVars.reviewPosition
eventHandler.lastReviewMoveDueToFollowing = isFocus
eventHandler.executeEvent("becomeNavigatorObject",obj)

def isTypingProtected():
Expand Down
2 changes: 1 addition & 1 deletion source/appModules/explorer.py
Expand Up @@ -41,7 +41,7 @@ class SuggestionListItem(UIA):

def event_UIA_elementSelected(self):
speech.cancelSpeech()
api.setNavigatorObject(self)
api.setNavigatorObject(self, isFocus=True)
self.reportFocus()
super(SuggestionListItem,self).event_UIA_elementSelected()

Expand Down
4 changes: 2 additions & 2 deletions source/appModules/lockapp.py
Expand Up @@ -90,10 +90,10 @@ def event_appModule_gainFocus(self):
# Move the review cursor so others can't access its previous position.
self._oldReviewPos = api.getReviewPosition()
self._oldReviewObj = self._oldReviewPos.obj
api.setNavigatorObject(eventHandler.lastQueuedFocusObject)
api.setNavigatorObject(eventHandler.lastQueuedFocusObject, isFocus=True)

def event_appModule_loseFocus(self):
if not config.conf["reviewCursor"]["followFocus"]:
api.setReviewPosition(self._oldReviewPos)
api.setReviewPosition(self._oldReviewPos, isCaret=False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As isCaret is False by default, explicitly specifying isCaret=False here is not needed, and is perhaps a little confusing. I'd prefer that anywhere in the codebase that isCaret is only ever specified if it needs to be set to true. Mirroring that of isFocus for setNavigatorObject.

del self._oldReviewPos, self._oldReviewObj
inputCore.manager._captureFunc = None
3 changes: 2 additions & 1 deletion source/appModules/skype.py
Expand Up @@ -160,7 +160,8 @@ def reviewRecentMessage(self, index):
ui.message(_("No message yet"))
return
message = self.getChild(count - index)
api.setNavigatorObject(message)
# Reviewing a message should not auto tether
api.setNavigatorObject(message, isFocus=True)
self.reportMessage(message.name)

class Notification(NVDAObjects.behaviors.Notification):
Expand Down
62 changes: 43 additions & 19 deletions source/braille.py
Expand Up @@ -28,6 +28,7 @@
from collections import namedtuple
import re
import scriptHandler
import eventHandler
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not too happy with another circular dependency, but I know we discussed this before and all solutions we came up with were hacky.


roleLabels = {
# Translators: Displayed in braille for an object which is a
Expand Down Expand Up @@ -1450,6 +1451,7 @@ def __init__(self):
self._cells = []
self._cursorBlinkTimer = None
config.configProfileSwitched.register(self.handleConfigProfileSwitch)
self._tether = config.conf["braille"]["tetherTo"]

def terminate(self):
if self._messageCallLater:
Expand All @@ -1464,18 +1466,34 @@ def terminate(self):
self.display = None
_BgThread.stop()

def getTether(self):
return self._tether

def _get_tether(self):
return config.conf["braille"]["tetherTo"]
"""@deprecated: Use L{getTether instead."""
return self.getTether()

def _set_tether(self, tether):
if tether == config.conf["braille"]["tetherTo"]:
def setTether(self, tether, auto=False):
if auto and not self.shouldAutoTether:
return
if auto and tether == self.TETHER_REVIEW and eventHandler.lastReviewMoveDueToFollowing:
return
config.conf["braille"]["tetherTo"] = tether
if not auto:
config.conf["braille"]["tetherTo"] = tether
if tether == self._tether:
return
self._tether = tether
self.mainBuffer.clear()
if tether == self.TETHER_REVIEW:
self.handleReviewMove()
else:
self.handleGainFocus(api.getFocusObject())

def _set_tether(self, tether):
"""@deprecated: Use L{setTether instead."""
self.setTether(tether, auto=False)

def _get_isAutoTethered(self):
return self._tether is not config.conf["braille"]["tetherTo"]

def _get_shouldAutoTether(self):
return self.enabled and config.conf["braille"]["autoTether"]

def setDisplayByName(self, name, isFallback=False):
if not name:
Expand Down Expand Up @@ -1640,10 +1658,12 @@ def _dismissMessage(self):
self._messageCallLater = None
self.update()

def handleGainFocus(self, obj):
def handleGainFocus(self, obj, shouldAutoTether=True):
if not self.enabled:
return
if self.tether != self.TETHER_FOCUS:
if shouldAutoTether:
self.setTether(self.TETHER_FOCUS, auto=True)
elif self._tether != self.TETHER_FOCUS:
return
self._doNewObject(itertools.chain(getFocusContextRegions(obj, oldFocusRegions=self.mainBuffer.regions), getFocusRegions(obj)))

Expand Down Expand Up @@ -1672,12 +1692,12 @@ def _doNewObject(self, regions):
elif self.buffer is self.messageBuffer and keyboardHandler.keyCounter>self._keyCountForLastMessage:
self._dismissMessage()

def handleCaretMove(self, obj):
def handleCaretMove(self, obj, shouldAutoTether=True):
if not self.enabled:
return
if self.tether != self.TETHER_FOCUS:
return
if not self.mainBuffer.regions:
if shouldAutoTether:
self.setTether(self.TETHER_FOCUS, auto=True)
elif self._tether != self.TETHER_FOCUS or not self.mainBuffer.regions:
return
region = self.mainBuffer.regions[-1]
if region.obj is not obj:
Expand Down Expand Up @@ -1742,7 +1762,7 @@ def handleUpdate(self, obj):
# There are some objects that require special update behavior even if they have no region.
# This only applies when tethered to focus, because tethering to review shows only one object at a time,
# which always has a braille region associated with it.
if self.tether != self.TETHER_FOCUS:
if self._tether != self.TETHER_FOCUS:
return
# Late import to avoid circular import.
from NVDAObjects import NVDAObject
Expand All @@ -1758,12 +1778,16 @@ def handleUpdate(self, obj):
elif self.buffer is self.messageBuffer and keyboardHandler.keyCounter>self._keyCountForLastMessage:
self._dismissMessage()

def handleReviewMove(self):
def handleReviewMove(self, shouldAutoTether=True):
if not self.enabled:
return
if self.tether != self.TETHER_REVIEW:
if not shouldAutoTether and self._tether != self.TETHER_REVIEW:
return
reviewPos = api.getReviewPosition()
#if reviewPos.obj == api.getFocusObject() and config.conf["reviewCursor"]["followFocus"]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say just remove these two lines.

#return
if shouldAutoTether:
self.setTether(self.TETHER_REVIEW, auto=True)
region = self.mainBuffer.regions[-1] if self.mainBuffer.regions else None
if region and region.obj == reviewPos.obj:
self._doCursorMove(region)
Expand Down Expand Up @@ -1851,9 +1875,9 @@ def initialize():
# Braille is disabled or focus/review hasn't yet been initialised.
return
if handler.tether == handler.TETHER_FOCUS:
handler.handleGainFocus(api.getFocusObject())
handler.handleGainFocus(api.getFocusObject(), shouldAutoTether=False)
else:
handler.handleReviewMove()
handler.handleReviewMove(shouldAutoTether=False)

def pumpAll():
"""Runs tasks at the end of each core cycle. For now just caret updates."""
Expand Down
1 change: 1 addition & 0 deletions source/config/configSpec.py
Expand Up @@ -61,6 +61,7 @@
noMessageTimeout = boolean(default=false)
messageTimeout = integer(default=4,min=0,max=20)
tetherTo = string(default="focus")
autoTether= boolean(default=false)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space.

readByParagraph = boolean(default=false)
wordWrap = boolean(default=true)
focusContextPresentation = option("changedContext", "fill", "scroll", default="changedContext")
Expand Down
5 changes: 4 additions & 1 deletion source/eventHandler.py
Expand Up @@ -27,6 +27,9 @@
#: the last object queued for a gainFocus event. Useful for code running outside NVDA's core queue
lastQueuedFocusObject=None

#: Whether the last focus or caret event changed or is about to change the review cursor due to following
lastReviewMoveDueToFollowing = False

def queueEvent(eventName,obj,**kwargs):
"""Queues an NVDA event to be executed.
@param eventName: the name of the event type (e.g. 'gainFocus', 'nameChange')
Expand Down Expand Up @@ -170,7 +173,7 @@ def doPreGainFocus(obj,sleepMode=False):
if obj.treeInterceptor and obj.treeInterceptor.isReady and hasattr(obj.treeInterceptor,"event_treeInterceptor_gainFocus"):
obj.treeInterceptor.event_treeInterceptor_gainFocus()
return True

def doPreDocumentLoadComplete(obj):
focusObject=api.getFocusObject()
if (not obj.treeInterceptor or not obj.treeInterceptor.isAlive or obj.treeInterceptor.shouldPrepare) and (obj==focusObject or obj in api.getFocusAncestors()):
Expand Down