Skip to content

Commit

Permalink
UI Automation in Windows Console: improve reliability of visible rang…
Browse files Browse the repository at this point in the history
…e checks (PR #9957)

Builds on #9614
Supersedes #9735 and #9899
Closes #9891

Previously, after the console window was maximized (or the review cursor is otherwise placed outside the visible text), text review is no longer functional.
The review top line and review bottom line scripts do not function as intended.

This commit changes:
- The isOffscreen property has been implemented as UIAUtils.isTextRangeOffscreen.
- When checking if the text range is out of bounds, we now also check that oldRange is in bounds before stopping movement.
- Re-implemented POSITION_FIRST and POSITION_LAST in terms of visible ranges.
  • Loading branch information
codeofdusk authored and feerrenrut committed Aug 1, 2019
1 parent 83d7233 commit 8a8c1c2
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 15 deletions.
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
# 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

0 comments on commit 8a8c1c2

Please sign in to comment.