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

UI Automation in Windows Console: improve reliability of visible range checks #9957

Merged
merged 14 commits into from
Aug 1, 2019
Merged
39 changes: 27 additions & 12 deletions source/NVDAObjects/UIA/winConsoleUIA.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import UIAHandler

from scriptHandler import script
from UIAUtils import isTextRangeOffscreen
from winVersion import isWin10
from . import UIATextInfo
from ..behaviors import Terminal
Expand All @@ -26,6 +27,21 @@ class consoleUIATextInfo(UIATextInfo):
#: to do much good either.
_expandCollapseBeforeReview = False

def __init__(self,obj,position,_rangeObj=None):
super(consoleUIATextInfo, self).__init__(obj, position, _rangeObj)
# Re-implement POSITION_FIRST and POSITION_LAST in terms of
feerrenrut marked this conversation as resolved.
Show resolved Hide resolved
# visible ranges to fix review top/bottom scripts.
if position==textInfos.POSITION_FIRST:
visiRanges = self.obj.UIATextPattern.GetVisibleRanges()
firstVisiRange = visiRanges.GetElement(0)
self._rangeObj = firstVisiRange
self.collapse()
elif position==textInfos.POSITION_LAST:
visiRanges = self.obj.UIATextPattern.GetVisibleRanges()
lastVisiRange = visiRanges.GetElement(visiRanges.length - 1)
self._rangeObj = lastVisiRange
self.collapse(True)

def collapse(self,end=False):
"""Works around a UIA bug on Windows 10 1903 and later."""
if not isWin10(1903):
Expand All @@ -50,8 +66,6 @@ def move(self, unit, direction, endPoint=None):
visiRanges = self.obj.UIATextPattern.GetVisibleRanges()
visiLength = visiRanges.length
if visiLength > 0:
firstVisiRange = visiRanges.GetElement(0)
lastVisiRange = visiRanges.GetElement(visiLength - 1)
oldRange = self._rangeObj.clone()
if unit == textInfos.UNIT_WORD and direction != 0:
# UIA doesn't implement word movement, so we need to do it manually.
Expand Down Expand Up @@ -108,13 +122,11 @@ def move(self, unit, direction, endPoint=None):
else: # moving by a unit other than word
res = super(consoleUIATextInfo, self).move(unit, direction,
endPoint)
if oldRange and (
self._rangeObj.CompareEndPoints(
UIAHandler.TextPatternRangeEndpoint_Start, firstVisiRange,
UIAHandler.TextPatternRangeEndpoint_Start) < 0
or self._rangeObj.CompareEndPoints(
UIAHandler.TextPatternRangeEndpoint_Start, lastVisiRange,
UIAHandler.TextPatternRangeEndpoint_End) >= 0):
if (
oldRange
and isTextRangeOffscreen(self._rangeObj, visiRanges)
and not isTextRangeOffscreen(oldRange, visiRanges)
):
self._rangeObj = oldRange
return 0
return res
Expand Down Expand Up @@ -292,9 +304,12 @@ def script_flush_queuedChars(self, gesture):

def _getTextLines(self):
# Filter out extraneous empty lines from UIA
ptr = self.UIATextPattern.GetVisibleRanges()
res = [ptr.GetElement(i).GetText(-1) for i in range(ptr.length)]
return res
return (
self.makeTextInfo(textInfos.POSITION_ALL)
._rangeObj.getText(-1)
.rstrip()
.split("\r\n")
)

def _calculateNewText(self, newLines, oldLines):
self._hasNewLines = (
Expand Down
18 changes: 18 additions & 0 deletions source/UIAUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,24 @@ def getChildrenWithCacheFromUIATextRange(textRange,cacheRequest):
c=CacheableUIAElementArray(c)
return c

def isTextRangeOffscreen(range, visiRanges):
Copy link
Contributor

Choose a reason for hiding this comment

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

Please change the range argument so it does not shadow the builtin function

"""Given a UIA text range and a visible ranges array (returned from obj.UIATextPattern.GetVisibleRanges), determines if the given range is not within the visible ranges."""
visiLength = visiRanges.length
if visiLength > 0:
firstVisiRange = visiRanges.GetElement(0)
lastVisiRange = visiRanges.GetElement(visiLength - 1)
return range.CompareEndPoints(
UIAHandler.TextPatternRangeEndpoint_Start, firstVisiRange,
UIAHandler.TextPatternRangeEndpoint_Start
) < 0 or range.CompareEndPoints(
UIAHandler.TextPatternRangeEndpoint_Start, lastVisiRange,
UIAHandler.TextPatternRangeEndpoint_End) >= 0
else:
# Visible ranges not available, so fail gracefully.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not raise an error here and catch that when calling?

log.warning("UIA visible ranges not available.")
return True


class UIATextRangeAttributeValueFetcher(object):

def __init__(self,textRange):
Expand Down