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
48 changes: 33 additions & 15 deletions source/NVDAObjects/UIA/winConsoleUIA.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import textInfos
import UIAHandler

from comtypes import COMError
from UIAUtils import isTextRangeOffscreen
from winVersion import isWin10
from . import UIATextInfo
from ..behaviors import KeyboardHandlerBasedTypedCharSupport
Expand All @@ -22,6 +24,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 @@ -46,8 +63,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 @@ -90,7 +105,6 @@ def move(self, unit, direction, endPoint=None):
lineInfo.expand(textInfos.UNIT_LINE)
offset = self._getCurrentOffsetInThisLine(lineInfo)
# Finally using the new offset,

# Calculate the current word offsets and move to the start of
# this word if we are not already there.
start, end = self._getWordOffsetsInThisLine(offset, lineInfo)
Expand All @@ -104,15 +118,16 @@ 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):
self._rangeObj = oldRange
return 0
try:
if (
oldRange
and isTextRangeOffscreen(self._rangeObj, visiRanges)
and not isTextRangeOffscreen(oldRange, visiRanges)
):
self._rangeObj = oldRange
return 0
except (COMError, RuntimeError):
pass
return res

def expand(self, unit):
Expand Down Expand Up @@ -236,9 +251,12 @@ def _get_caretMovementDetectionUsesEvents(self):

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 findExtraOverlayClasses(obj, clsList):
Expand Down
17 changes: 17 additions & 0 deletions source/UIAUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,23 @@ def getChildrenWithCacheFromUIATextRange(textRange,cacheRequest):
c=CacheableUIAElementArray(c)
return c

def isTextRangeOffscreen(textRange, visiRanges):
"""Given a UIA text range and a visible textRanges array (returned from obj.UIATextPattern.GetVisibleRanges), determines if the given textRange is not within the visible textRanges."""
visiLength = visiRanges.length
if visiLength > 0:
firstVisiRange = visiRanges.GetElement(0)
lastVisiRange = visiRanges.GetElement(visiLength - 1)
return textRange.CompareEndPoints(
UIAHandler.TextPatternRangeEndpoint_Start, firstVisiRange,
UIAHandler.TextPatternRangeEndpoint_Start
) < 0 or textRange.CompareEndPoints(
UIAHandler.TextPatternRangeEndpoint_Start, lastVisiRange,
UIAHandler.TextPatternRangeEndpoint_End) >= 0
else:
# Visible textRanges not available.
raise RuntimeError("Visible textRanges array is empty or invalid.")


class UIATextRangeAttributeValueFetcher(object):

def __init__(self,textRange):
Expand Down
15 changes: 15 additions & 0 deletions user_docs/en/userGuide.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,21 @@ When in the table view of added books:
| Context menu | applications | Opens the context menu for the selected book. |
%kc:endInclude

++ Windows Console ++[WinConsole]
NVDA provides support for the Windows command console used by Command Prompt, PowerShell, and the Windows Subsystem for Linux.
The console window is of fixed size, typically much smaller than the buffer that holds the output.
As new text is written, the content scroll upwards and previous text is no longer visible.
Text that is not visibly displayed in the window is not accessible with NVDA's text review commands.
Therefore, it is necessary to scroll the console window to read earlier text.
%kc:beginInclude
The following built-in Windows Console keyboard shortcuts may be useful when [reviewing text #ReviewingText] with NVDA:
|| Name | Key | Description |
| Scroll up | control+upArrow | Scrolls the console window up, so earlier text can be read. |
| Scroll down | control+downArrow | Scrolls the console window down, so later text can be read. |
| Scroll to start | control+home | Scrolls the console window to the beginning of the buffer. |
| Scroll to end | control+end | Scrolls the console window to the end of the buffer. |
%kc:endInclude

+ Configuring NVDA +[ConfiguringNVDA]
Most configuration can be performed using dialog boxes accessed through the Preferences sub-menu of the NVDA menu.
Many of these settings can be found in the multi-page [NVDA Settings dialog #NVDASettings].
Expand Down