From 41d481683b574e672552b2cfaff609d87f118494 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Tue, 28 May 2019 10:08:41 -0400 Subject: [PATCH 01/15] Restrict the number of blank lines shown in console UIA's object review. Note: there are issues when reaching the bottom of the review (cursor gets stuck). --- source/NVDAObjects/UIA/winConsoleUIA.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 6470911194e..01088bbbc97 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -30,6 +30,14 @@ def __init__(self, obj, position, _rangeObj=None): 1 ) + def move(self,unit,direction,endPoint=None): + # Insure we haven't gone beyond the visible text. + # UIA adds thousands of blank lines to the end of the console. + visiRanges = self.obj.UIATextPattern.GetVisibleRanges() + lastVisiRange = visiRanges.GetElement(visiRanges.length - 1) + if self._rangeObj.CompareEndPoints(UIAHandler.TextPatternRangeEndpoint_Start, lastVisiRange, UIAHandler.TextPatternRangeEndpoint_End) >= 0: + return 0 + super(consoleUIATextInfo, self).move(unit, direction, endPoint) class winConsoleUIA(Terminal): _TextInfo = consoleUIATextInfo @@ -56,7 +64,6 @@ def script_clear_isTyping(self, gesture): def _getTextLines(self): # Filter out extraneous empty lines from UIA - # Todo: do this (also) somewhere else so they aren't in document review either ptr = self.UIATextPattern.GetVisibleRanges() res = [ptr.GetElement(i).GetText(-1) for i in range(ptr.length)] return res From 53ecca9ff9673655854f3ab008e2c79752a305f8 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Tue, 28 May 2019 16:59:58 -0400 Subject: [PATCH 02/15] UIA console: only apply our custom move logic when not moving the caret. This should mostly restore caret movement support. --- source/NVDAObjects/UIA/winConsoleUIA.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 01088bbbc97..8a11456f69b 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -16,10 +16,12 @@ class consoleUIATextInfo(UIATextInfo): _expandCollapseBeforeReview = False + _isCaret = False def __init__(self, obj, position, _rangeObj=None): super(consoleUIATextInfo, self).__init__(obj, position, _rangeObj) if position == textInfos.POSITION_CARET: + self._isCaret = True if isAtLeastWin10(1903): # The UIA implementation in 1903 causes the caret to be # off-by-one, so move it one position to the right @@ -31,12 +33,13 @@ def __init__(self, obj, position, _rangeObj=None): ) def move(self,unit,direction,endPoint=None): - # Insure we haven't gone beyond the visible text. - # UIA adds thousands of blank lines to the end of the console. - visiRanges = self.obj.UIATextPattern.GetVisibleRanges() - lastVisiRange = visiRanges.GetElement(visiRanges.length - 1) - if self._rangeObj.CompareEndPoints(UIAHandler.TextPatternRangeEndpoint_Start, lastVisiRange, UIAHandler.TextPatternRangeEndpoint_End) >= 0: - return 0 + if not self._isCaret: + # Insure we haven't gone beyond the visible text. + # UIA adds thousands of blank lines to the end of the console. + visiRanges = self.obj.UIATextPattern.GetVisibleRanges() + lastVisiRange = visiRanges.GetElement(visiRanges.length - 1) + if self._rangeObj.CompareEndPoints(UIAHandler.TextPatternRangeEndpoint_Start, lastVisiRange, UIAHandler.TextPatternRangeEndpoint_End) >= 0: + return 0 super(consoleUIATextInfo, self).move(unit, direction, endPoint) class winConsoleUIA(Terminal): From a9575ba5afc501bdd268608266d8b4500d9fc0f5 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Thu, 30 May 2019 05:00:13 -0400 Subject: [PATCH 03/15] Add basic word movement support. --- source/NVDAObjects/UIA/winConsoleUIA.py | 77 ++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 8a11456f69b..c79c5bda226 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -4,6 +4,8 @@ # See the file COPYING for more details. # Copyright (C) 2019 Bill Dengler +import ctypes +import NVDAHelper import time import textInfos import UIAHandler @@ -32,7 +34,7 @@ def __init__(self, obj, position, _rangeObj=None): 1 ) - def move(self,unit,direction,endPoint=None): + def move(self, unit, direction, endPoint=None): if not self._isCaret: # Insure we haven't gone beyond the visible text. # UIA adds thousands of blank lines to the end of the console. @@ -40,7 +42,78 @@ def move(self,unit,direction,endPoint=None): lastVisiRange = visiRanges.GetElement(visiRanges.length - 1) if self._rangeObj.CompareEndPoints(UIAHandler.TextPatternRangeEndpoint_Start, lastVisiRange, UIAHandler.TextPatternRangeEndpoint_End) >= 0: return 0 - super(consoleUIATextInfo, self).move(unit, direction, endPoint) + if unit == textInfos.UNIT_WORD and direction != 0: + # UIA doesn't implement word movement, so we need to do it manually. + offset = self._countCharsToEnd(reverse=True) + index = 1 if direction > 0 else 0 + wordOffsets = self._getWordOffsets(offset) + res = self.move( + textInfos.UNIT_CHARACTER, + wordOffsets[index], + endPoint=endPoint + ) + if res != 0: + return direction + else: + return res + return super(consoleUIATextInfo, self).move(unit, direction, endPoint) + + def _countCharsToEnd(self, reverse=False): + direction = -1 if reverse else 1 + lineInfo = self.copy() + lineInfo.expand(textInfos.UNIT_LINE) + charInfo = self.copy() + res = 0 + chars = None + compareCondition = True + while True: + charInfo.expand(textInfos.UNIT_CHARACTER) + if reverse: + compareCondition = charInfo.compareEndPoints( + lineInfo, + "startToStart" + ) >= 0 + else: + compareCondition = charInfo.compareEndPoints( + lineInfo, + "endToEnd" + ) < 0 + chars = charInfo.move(textInfos.UNIT_CHARACTER, direction) + if reverse: + chars *= -1 + if chars != 0 and compareCondition: + res += chars + else: + break + # Subtract 1 from res since UIA seems to wrap around + res -= 1 + return res + + def _getWordOffsets(self, offset): + lineInfo = self.copy() + lineInfo.expand(textInfos.UNIT_LINE) + lineText = lineInfo.text + # Convert NULL and non-breaking space to space to make sure + # that words will break on them + lineText = lineText.translate({0: u' ', 0xa0: u' '}) + start = ctypes.c_int() + end = ctypes.c_int() + # Uniscribe does some strange things when you give it a string with + # not more than two alphanumeric chars in a row. + # Inject two alphanumeric characters at the end to fix this. + lineText += "xx" + NVDAHelper.localLib.calculateWordOffsets( + lineText, + len(lineText), + offset, + ctypes.byref(start), + ctypes.byref(end) + ) + return ( + (offset - start.value) * -1, + min(end.value, len(lineText)) - offset + ) + class winConsoleUIA(Terminal): _TextInfo = consoleUIATextInfo From 1cf0563e8991f20dbb71624850f3a8e1b95600f1 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Thu, 30 May 2019 16:34:54 -0400 Subject: [PATCH 04/15] ConsoleUIATextInfo: support expantion by word. --- source/NVDAObjects/UIA/winConsoleUIA.py | 40 +++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index c79c5bda226..055abf71096 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -19,6 +19,7 @@ class consoleUIATextInfo(UIATextInfo): _expandCollapseBeforeReview = False _isCaret = False + _expandedToWord = False def __init__(self, obj, position, _rangeObj=None): super(consoleUIATextInfo, self).__init__(obj, position, _rangeObj) @@ -46,7 +47,11 @@ def move(self, unit, direction, endPoint=None): # UIA doesn't implement word movement, so we need to do it manually. offset = self._countCharsToEnd(reverse=True) index = 1 if direction > 0 else 0 - wordOffsets = self._getWordOffsets(offset) + start, end = self._getWordOffsets(offset) + wordOffsets = ( + (offset - start + 1) * -1, + end - offset + ) res = self.move( textInfos.UNIT_CHARACTER, wordOffsets[index], @@ -58,6 +63,27 @@ def move(self, unit, direction, endPoint=None): return res return super(consoleUIATextInfo, self).move(unit, direction, endPoint) + def expand(self, unit): + if unit == textInfos.UNIT_WORD: + self._expandedToWord = True + else: + self._expandedToWord = False + return super(consoleUIATextInfo, self).expand(unit) + + def collapse(self): + self._expandedToWord = False + return super(consoleUIATextInfo, self).collapse() + + def getTextWithFields(self,formatConfig=None): + if self._expandedToWord: + return [self.text] + return super(consoleUIATextInfo, self).getTextWithFields(formatConfig=formatConfig) + + def _get_text(self): + if self._expandedToWord: + return self._getCurrentWord() + return super(consoleUIATextInfo, self)._get_text() + def _countCharsToEnd(self, reverse=False): direction = -1 if reverse else 1 lineInfo = self.copy() @@ -110,10 +136,18 @@ def _getWordOffsets(self, offset): ctypes.byref(end) ) return ( - (offset - start.value) * -1, - min(end.value, len(lineText)) - offset + start.value, + min(end.value, len(lineText)) ) + def _getCurrentWord(self): + lineInfo=self.copy() + lineInfo.expand(textInfos.UNIT_LINE) + lineText=lineInfo.text + offset = self._countCharsToEnd(reverse=True) + start, end = self._getWordOffsets(offset) + return lineText[start:end] + class winConsoleUIA(Terminal): _TextInfo = consoleUIATextInfo From 6083ded0e8bc8ec4ae8fa7b5ca84cb2409bda9e7 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Thu, 30 May 2019 18:56:31 -0400 Subject: [PATCH 05/15] consoleUIATextInfo._countCharsToEnd(reverse=True) is now consoleUIATextInfo._getCurrentOffset. The reverse option has been removed. --- source/NVDAObjects/UIA/winConsoleUIA.py | 33 +++++++++---------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 055abf71096..4c7af5194e3 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -45,16 +45,16 @@ def move(self, unit, direction, endPoint=None): return 0 if unit == textInfos.UNIT_WORD and direction != 0: # UIA doesn't implement word movement, so we need to do it manually. - offset = self._countCharsToEnd(reverse=True) + offset = self._getCurrentOffset() index = 1 if direction > 0 else 0 start, end = self._getWordOffsets(offset) - wordOffsets = ( + wordMoveDirections = ( (offset - start + 1) * -1, end - offset ) res = self.move( textInfos.UNIT_CHARACTER, - wordOffsets[index], + wordMoveDirections[index], endPoint=endPoint ) if res != 0: @@ -84,30 +84,19 @@ def _get_text(self): return self._getCurrentWord() return super(consoleUIATextInfo, self)._get_text() - def _countCharsToEnd(self, reverse=False): - direction = -1 if reverse else 1 + def _getCurrentOffset(self): lineInfo = self.copy() lineInfo.expand(textInfos.UNIT_LINE) charInfo = self.copy() res = 0 chars = None - compareCondition = True while True: charInfo.expand(textInfos.UNIT_CHARACTER) - if reverse: - compareCondition = charInfo.compareEndPoints( - lineInfo, - "startToStart" - ) >= 0 - else: - compareCondition = charInfo.compareEndPoints( - lineInfo, - "endToEnd" - ) < 0 - chars = charInfo.move(textInfos.UNIT_CHARACTER, direction) - if reverse: - chars *= -1 - if chars != 0 and compareCondition: + chars = charInfo.move(textInfos.UNIT_CHARACTER, -1) * -1 + if chars != 0 and charInfo.compareEndPoints( + lineInfo, + "startToStart" + ) >= 0: res += chars else: break @@ -143,8 +132,8 @@ def _getWordOffsets(self, offset): def _getCurrentWord(self): lineInfo=self.copy() lineInfo.expand(textInfos.UNIT_LINE) - lineText=lineInfo.text - offset = self._countCharsToEnd(reverse=True) + lineText = lineInfo.text + offset = self._getCurrentOffset() start, end = self._getWordOffsets(offset) return lineText[start:end] From 768fe0fc5778e4a52b2cee7c46b49fd976cb20ed Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Fri, 31 May 2019 06:30:05 -0400 Subject: [PATCH 06/15] Cleanup. --- source/NVDAObjects/UIA/winConsoleUIA.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 4c7af5194e3..adfa62282aa 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -41,7 +41,11 @@ def move(self, unit, direction, endPoint=None): # UIA adds thousands of blank lines to the end of the console. visiRanges = self.obj.UIATextPattern.GetVisibleRanges() lastVisiRange = visiRanges.GetElement(visiRanges.length - 1) - if self._rangeObj.CompareEndPoints(UIAHandler.TextPatternRangeEndpoint_Start, lastVisiRange, UIAHandler.TextPatternRangeEndpoint_End) >= 0: + if self._rangeObj.CompareEndPoints( + UIAHandler.TextPatternRangeEndpoint_Start, + lastVisiRange, + UIAHandler.TextPatternRangeEndpoint_End + ) >= 0: return 0 if unit == textInfos.UNIT_WORD and direction != 0: # UIA doesn't implement word movement, so we need to do it manually. @@ -74,10 +78,12 @@ def collapse(self): self._expandedToWord = False return super(consoleUIATextInfo, self).collapse() - def getTextWithFields(self,formatConfig=None): + def getTextWithFields(self, formatConfig=None): if self._expandedToWord: return [self.text] - return super(consoleUIATextInfo, self).getTextWithFields(formatConfig=formatConfig) + return super(consoleUIATextInfo, self).getTextWithFields( + formatConfig=formatConfig + ) def _get_text(self): if self._expandedToWord: @@ -130,7 +136,7 @@ def _getWordOffsets(self, offset): ) def _getCurrentWord(self): - lineInfo=self.copy() + lineInfo = self.copy() lineInfo.expand(textInfos.UNIT_LINE) lineText = lineInfo.text offset = self._getCurrentOffset() @@ -146,7 +152,10 @@ class winConsoleUIA(Terminal): def _reportNewText(self, line): # Additional typed character filtering beyond that in LiveText - if self._isTyping and time.time() - self._lastCharTime <= self._TYPING_TIMEOUT: + if ( + self._isTyping + and time.time() - self._lastCharTime <= self._TYPING_TIMEOUT + ): return super(winConsoleUIA, self)._reportNewText(line) From 9a8835ccbf6b1d44fd97005ad5d895258c6e7b07 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Fri, 31 May 2019 21:56:18 -0400 Subject: [PATCH 07/15] Improve cursor placement after word movement. --- source/NVDAObjects/UIA/winConsoleUIA.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index adfa62282aa..6219e86f1ac 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -53,7 +53,7 @@ def move(self, unit, direction, endPoint=None): index = 1 if direction > 0 else 0 start, end = self._getWordOffsets(offset) wordMoveDirections = ( - (offset - start + 1) * -1, + (offset - start) * -1, end - offset ) res = self.move( @@ -64,7 +64,10 @@ def move(self, unit, direction, endPoint=None): if res != 0: return direction else: - return res + if self.move(textInfos.UNIT_CHARACTER, -1): + return self.move(unit, direction, endPoint=endPoint) + else: + return res return super(consoleUIATextInfo, self).move(unit, direction, endPoint) def expand(self, unit): @@ -106,8 +109,6 @@ def _getCurrentOffset(self): res += chars else: break - # Subtract 1 from res since UIA seems to wrap around - res -= 1 return res def _getWordOffsets(self, offset): From bc55777de9702055eb087c3990253fbf4e8a466f Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Mon, 3 Jun 2019 19:02:44 -0400 Subject: [PATCH 08/15] Review actions (without changes to expand) --- source/NVDAObjects/UIA/winConsoleUIA.py | 48 +++++++++++++++---------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 6219e86f1ac..ce0f743b92d 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -36,22 +36,20 @@ def __init__(self, obj, position, _rangeObj=None): ) def move(self, unit, direction, endPoint=None): + oldRange=None if not self._isCaret: # Insure we haven't gone beyond the visible text. # UIA adds thousands of blank lines to the end of the console. visiRanges = self.obj.UIATextPattern.GetVisibleRanges() - lastVisiRange = visiRanges.GetElement(visiRanges.length - 1) - if self._rangeObj.CompareEndPoints( - UIAHandler.TextPatternRangeEndpoint_Start, - lastVisiRange, - UIAHandler.TextPatternRangeEndpoint_End - ) >= 0: - return 0 + if visiRanges.length > 0: + firstVisiRange = visiRanges.GetElement(0) + lastVisiRange = visiRanges.GetElement(visiRanges.length - 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. - offset = self._getCurrentOffset() + offset = self._getCurrentOffsetInThisLine() index = 1 if direction > 0 else 0 - start, end = self._getWordOffsets(offset) + start, end = self._getWordOffsetsInThisLine(offset) wordMoveDirections = ( (offset - start) * -1, end - offset @@ -64,11 +62,26 @@ def move(self, unit, direction, endPoint=None): if res != 0: return direction else: - if self.move(textInfos.UNIT_CHARACTER, -1): + if self.move(textInfos.UNIT_CHARACTER, -1): # Reset word boundaries to move to the previous word return self.move(unit, direction, endPoint=endPoint) else: return res - return super(consoleUIATextInfo, self).move(unit, direction, endPoint) + 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 + return res def expand(self, unit): if unit == textInfos.UNIT_WORD: @@ -93,7 +106,7 @@ def _get_text(self): return self._getCurrentWord() return super(consoleUIATextInfo, self)._get_text() - def _getCurrentOffset(self): + def _getCurrentOffsetInThisLine(self): lineInfo = self.copy() lineInfo.expand(textInfos.UNIT_LINE) charInfo = self.copy() @@ -111,7 +124,7 @@ def _getCurrentOffset(self): break return res - def _getWordOffsets(self, offset): + def _getWordOffsetsInThisLine(self, offset): lineInfo = self.copy() lineInfo.expand(textInfos.UNIT_LINE) lineText = lineInfo.text @@ -140,8 +153,8 @@ def _getCurrentWord(self): lineInfo = self.copy() lineInfo.expand(textInfos.UNIT_LINE) lineText = lineInfo.text - offset = self._getCurrentOffset() - start, end = self._getWordOffsets(offset) + offset = self._getCurrentOffsetInThisLine() + start, end = self._getWordOffsetsInThisLine(offset) return lineText[start:end] @@ -153,10 +166,7 @@ class winConsoleUIA(Terminal): def _reportNewText(self, line): # Additional typed character filtering beyond that in LiveText - if ( - self._isTyping - and time.time() - self._lastCharTime <= self._TYPING_TIMEOUT - ): + if self._isTyping and time.time() - self._lastCharTime <= self._TYPING_TIMEOUT: return super(winConsoleUIA, self)._reportNewText(line) From 6f52524911e05347f01f3b24c36d8e5f0c85b6e8 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Mon, 3 Jun 2019 19:15:28 -0400 Subject: [PATCH 09/15] Re-implement expand. Note: this new approach includes characters after a line break in the current word. --- source/NVDAObjects/UIA/winConsoleUIA.py | 36 ++++++++++++------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index ce0f743b92d..5487ed09c6f 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -19,7 +19,6 @@ class consoleUIATextInfo(UIATextInfo): _expandCollapseBeforeReview = False _isCaret = False - _expandedToWord = False def __init__(self, obj, position, _rangeObj=None): super(consoleUIATextInfo, self).__init__(obj, position, _rangeObj) @@ -85,27 +84,26 @@ def move(self, unit, direction, endPoint=None): def expand(self, unit): if unit == textInfos.UNIT_WORD: - self._expandedToWord = True + # UIA doesn't implement word movement, so we need to do it manually. + offset = self._getCurrentOffsetInThisLine() + start, end = self._getWordOffsetsInThisLine(offset) + wordEndPoints = ( + (offset - start) * -1, + end - offset - 1 + ) + self._rangeObj.MoveEndpointByUnit( + UIAHandler.TextPatternRangeEndpoint_Start, + UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER], + wordEndPoints[0] + ) + self._rangeObj.MoveEndpointByUnit( + UIAHandler.TextPatternRangeEndpoint_End, + UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER], + wordEndPoints[1] + ) else: - self._expandedToWord = False return super(consoleUIATextInfo, self).expand(unit) - def collapse(self): - self._expandedToWord = False - return super(consoleUIATextInfo, self).collapse() - - def getTextWithFields(self, formatConfig=None): - if self._expandedToWord: - return [self.text] - return super(consoleUIATextInfo, self).getTextWithFields( - formatConfig=formatConfig - ) - - def _get_text(self): - if self._expandedToWord: - return self._getCurrentWord() - return super(consoleUIATextInfo, self)._get_text() - def _getCurrentOffsetInThisLine(self): lineInfo = self.copy() lineInfo.expand(textInfos.UNIT_LINE) From c2386b9b4bbf897ec9be9ce298ac3334542c8f9e Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Tue, 4 Jun 2019 01:19:48 -0400 Subject: [PATCH 10/15] Review actions. --- source/NVDAObjects/UIA/winConsoleUIA.py | 32 ++++++++++--------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 5487ed09c6f..c6a4c1d7bb6 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -91,16 +91,18 @@ def expand(self, unit): (offset - start) * -1, end - offset - 1 ) - self._rangeObj.MoveEndpointByUnit( - UIAHandler.TextPatternRangeEndpoint_Start, - UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER], - wordEndPoints[0] - ) - self._rangeObj.MoveEndpointByUnit( - UIAHandler.TextPatternRangeEndpoint_End, - UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER], - wordEndPoints[1] - ) + if wordEndPoints[0]: + self._rangeObj.MoveEndpointByUnit( + UIAHandler.TextPatternRangeEndpoint_Start, + UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER], + wordEndPoints[0] + ) + if wordEndPoints[1]: + self._rangeObj.MoveEndpointByUnit( + UIAHandler.TextPatternRangeEndpoint_End, + UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER], + wordEndPoints[1] + ) else: return super(consoleUIATextInfo, self).expand(unit) @@ -144,17 +146,9 @@ def _getWordOffsetsInThisLine(self, offset): ) return ( start.value, - min(end.value, len(lineText)) + min(end.value, len(lineText) - 2) ) - def _getCurrentWord(self): - lineInfo = self.copy() - lineInfo.expand(textInfos.UNIT_LINE) - lineText = lineInfo.text - offset = self._getCurrentOffsetInThisLine() - start, end = self._getWordOffsetsInThisLine(offset) - return lineText[start:end] - class winConsoleUIA(Terminal): _TextInfo = consoleUIATextInfo From 6557f0431addbf9a56e802b92b4cc48b25bc58c1 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Tue, 4 Jun 2019 03:38:06 -0400 Subject: [PATCH 11/15] Review actions. --- source/NVDAObjects/UIA/winConsoleUIA.py | 48 ++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 3996d3eff91..95ba20393a0 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -19,25 +19,22 @@ class consoleUIATextInfo(UIATextInfo): _expandCollapseBeforeReview = False - _isCaret = False def __init__(self, obj, position, _rangeObj=None): super(consoleUIATextInfo, self).__init__(obj, position, _rangeObj) - if position == textInfos.POSITION_CARET: - self._isCaret = True - if isAtLeastWin10(1903): - # The UIA implementation in 1903 causes the caret to be - # off-by-one, so move it one position to the right - # to compensate. - self._rangeObj.MoveEndpointByUnit( - UIAHandler.TextPatternRangeEndpoint_Start, - UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER], - 1 - ) + if position == textInfos.POSITION_CARET and isAtLeastWin10(1903): + # The UIA implementation in 1903 causes the caret to be + # off-by-one, so move it one position to the right + # to compensate. + self._rangeObj.MoveEndpointByUnit( + UIAHandler.TextPatternRangeEndpoint_Start, + UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER], + 1 + ) def move(self, unit, direction, endPoint=None): oldRange=None - if not self._isCaret: + if self.basePosition != textInfos.POSITION_CARET: # Insure we haven't gone beyond the visible text. # UIA adds thousands of blank lines to the end of the console. visiRanges = self.obj.UIATextPattern.GetVisibleRanges() @@ -47,9 +44,11 @@ def move(self, unit, direction, endPoint=None): 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. - offset = self._getCurrentOffsetInThisLine() + lineInfo = self.copy() + lineInfo.expand(textInfos.UNIT_LINE) + offset = self._getCurrentOffsetInThisLine(lineInfo) index = 1 if direction > 0 else 0 - start, end = self._getWordOffsetsInThisLine(offset) + start, end = self._getWordOffsetsInThisLine(offset, lineInfo) wordMoveDirections = ( (offset - start) * -1, end - offset @@ -86,8 +85,10 @@ def move(self, unit, direction, endPoint=None): def expand(self, unit): if unit == textInfos.UNIT_WORD: # UIA doesn't implement word movement, so we need to do it manually. - offset = self._getCurrentOffsetInThisLine() - start, end = self._getWordOffsetsInThisLine(offset) + lineInfo = self.copy() + lineInfo.expand(textInfos.UNIT_LINE) + offset = self._getCurrentOffsetInThisLine(lineInfo) + start, end = self._getWordOffsetsInThisLine(offset, lineInfo) wordEndPoints = ( (offset - start) * -1, end - offset - 1 @@ -107,13 +108,14 @@ def expand(self, unit): else: return super(consoleUIATextInfo, self).expand(unit) - def _getCurrentOffsetInThisLine(self): - lineInfo = self.copy() - lineInfo.expand(textInfos.UNIT_LINE) + def _getCurrentOffsetInThisLine(self, lineInfo): charInfo = self.copy() res = 0 chars = None - while True: + while charInfo.compareEndPoints( + lineInfo, + "startToEnd" + ) <= 0: charInfo.expand(textInfos.UNIT_CHARACTER) chars = charInfo.move(textInfos.UNIT_CHARACTER, -1) * -1 if chars != 0 and charInfo.compareEndPoints( @@ -125,9 +127,7 @@ def _getCurrentOffsetInThisLine(self): break return res - def _getWordOffsetsInThisLine(self, offset): - lineInfo = self.copy() - lineInfo.expand(textInfos.UNIT_LINE) + def _getWordOffsetsInThisLine(self, offset, lineInfo): lineText = lineInfo.text # Convert NULL and non-breaking space to space to make sure # that words will break on them From 4e9bbe0e330427acb0966e657e57aede91e7a989 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Tue, 4 Jun 2019 13:58:58 -0400 Subject: [PATCH 12/15] Review actions. --- source/NVDAObjects/UIA/winConsoleUIA.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 95ba20393a0..a3eb05db4bd 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -38,9 +38,10 @@ def move(self, unit, direction, endPoint=None): # Insure we haven't gone beyond the visible text. # UIA adds thousands of blank lines to the end of the console. visiRanges = self.obj.UIATextPattern.GetVisibleRanges() - if visiRanges.length > 0: + visiLength = visiRanges.length + if visiLength > 0: firstVisiRange = visiRanges.GetElement(0) - lastVisiRange = visiRanges.GetElement(visiRanges.length - 1) + 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. From e4e96ef913df9acfb0d841dd0f67f36144d3410f Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Wed, 5 Jun 2019 03:16:18 -0400 Subject: [PATCH 13/15] Fix forward movement across lines. --- source/NVDAObjects/UIA/winConsoleUIA.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 99767640ad3..fdb6f48e353 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -130,7 +130,7 @@ def _getCurrentOffsetInThisLine(self, lineInfo): return res def _getWordOffsetsInThisLine(self, offset, lineInfo): - lineText = lineInfo.text + lineText = lineInfo.text or u" " # Convert NULL and non-breaking space to space to make sure # that words will break on them lineText = lineText.translate({0: u' ', 0xa0: u' '}) From d00a15be3528dd9f93821eb0e0391658731d55ff Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 5 Jun 2019 22:15:15 +1000 Subject: [PATCH 14/15] UIAWinConsole support: Improve logic of moving previous word to no longer jump over the last word on the previous line. --- source/NVDAObjects/UIA/winConsoleUIA.py | 62 ++++++++++++++++++------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index a3eb05db4bd..7ac02bc84ec 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -45,28 +45,54 @@ def move(self, unit, direction, endPoint=None): 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. + # Relative to the current line, calculate our offset and the current word's offsets. lineInfo = self.copy() lineInfo.expand(textInfos.UNIT_LINE) offset = self._getCurrentOffsetInThisLine(lineInfo) - index = 1 if direction > 0 else 0 start, end = self._getWordOffsetsInThisLine(offset, lineInfo) - wordMoveDirections = ( - (offset - start) * -1, - end - offset - ) - res = self.move( - textInfos.UNIT_CHARACTER, - wordMoveDirections[index], - endPoint=endPoint - ) - if res != 0: - return direction + if direction>0: + # Moving in a forward direction, we can just jump to the end offset of the current word and we're done. + res = self.move( + textInfos.UNIT_CHARACTER, + end-offset, + endPoint=endPoint + ) else: - if self.move(textInfos.UNIT_CHARACTER, -1): # Reset word boundaries to move to the previous word - return self.move(unit, direction, endPoint=endPoint) - else: - return res - res = super(consoleUIATextInfo, self).move(unit, direction, endPoint) + # Moving backwards + wordStartDistance=(offset-start) * -1 + if wordStartDistance<0: + # We are after the beginning of a word. + # So first move back to the start of the word. + self.move( + textInfos.UNIT_CHARACTER, + wordStartDistance, + endPoint=endPoint + ) + offset+=wordStartDistance + # Try to move one character back before the start of the word. + res=self.move(textInfos.UNIT_CHARACTER,-1,endPoint=endPoint) + if res==0: + return 0 + offset-=1 + # We are now positioned within the previous word. + if offset<0: + # We've moved on to the previous line. + # Recalculate the current offset based on the new line we are now on. + lineInfo = self.copy() + 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) + wordStartDistance=(offset-start) * -1 + if wordStartDistance<0: + self.move( + textInfos.UNIT_CHARACTER, + wordStartDistance, + endPoint=endPoint + ) + 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, @@ -148,7 +174,7 @@ def _getWordOffsetsInThisLine(self, offset, lineInfo): ) return ( start.value, - min(end.value, len(lineText) - 2) + min(end.value, max(1,len(lineText) - 2)) ) From 9fef53167a26dde212d26e9f1c4bca1b0e6b5863 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Wed, 5 Jun 2019 12:38:35 -0400 Subject: [PATCH 15/15] Style. --- source/NVDAObjects/UIA/winConsoleUIA.py | 40 ++++++++++++++----------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index ea83f299ceb..b972dcb6602 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -34,7 +34,7 @@ def __init__(self, obj, position, _rangeObj=None): ) def move(self, unit, direction, endPoint=None): - oldRange=None + oldRange = None if self.basePosition != textInfos.POSITION_CARET: # Insure we haven't gone beyond the visible text. # UIA adds thousands of blank lines to the end of the console. @@ -43,25 +43,27 @@ def move(self, unit, direction, endPoint=None): if visiLength > 0: firstVisiRange = visiRanges.GetElement(0) lastVisiRange = visiRanges.GetElement(visiLength - 1) - oldRange=self._rangeObj.clone() + 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. - # Relative to the current line, calculate our offset and the current word's offsets. + # Relative to the current line, calculate our offset + # and the current word's offsets. lineInfo = self.copy() lineInfo.expand(textInfos.UNIT_LINE) offset = self._getCurrentOffsetInThisLine(lineInfo) start, end = self._getWordOffsetsInThisLine(offset, lineInfo) - if direction>0: - # Moving in a forward direction, we can just jump to the end offset of the current word and we're done. + if direction > 0: + # Moving in a forward direction, we can just jump to the + # end offset of the current word and we're done. res = self.move( textInfos.UNIT_CHARACTER, - end-offset, + end - offset, endPoint=endPoint ) else: # Moving backwards - wordStartDistance=(offset-start) * -1 - if wordStartDistance<0: + wordStartDistance = (offset - start) * -1 + if wordStartDistance < 0: # We are after the beginning of a word. # So first move back to the start of the word. self.move( @@ -69,30 +71,32 @@ def move(self, unit, direction, endPoint=None): wordStartDistance, endPoint=endPoint ) - offset+=wordStartDistance + offset += wordStartDistance # Try to move one character back before the start of the word. - res=self.move(textInfos.UNIT_CHARACTER,-1,endPoint=endPoint) - if res==0: + res = self.move(textInfos.UNIT_CHARACTER, -1, endPoint=endPoint) + if res == 0: return 0 - offset-=1 + offset -= 1 # We are now positioned within the previous word. - if offset<0: + if offset < 0: # We've moved on to the previous line. # Recalculate the current offset based on the new line we are now on. lineInfo = self.copy() 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. + + # 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) - wordStartDistance=(offset-start) * -1 - if wordStartDistance<0: + wordStartDistance = (offset - start) * -1 + if wordStartDistance < 0: self.move( textInfos.UNIT_CHARACTER, wordStartDistance, endPoint=endPoint ) - else: # moving by a unit other than word + else: # moving by a unit other than word res = super(consoleUIATextInfo, self).move(unit, direction, endPoint) if oldRange and ( self._rangeObj.CompareEndPoints( @@ -175,7 +179,7 @@ def _getWordOffsetsInThisLine(self, offset, lineInfo): ) return ( start.value, - min(end.value, max(1,len(lineText) - 2)) + min(end.value, max(1, len(lineText) - 2)) )