Repository navigation
winConsoleUIATextInfo: use rangeFromPoint instead of broken getVisibleRanges #10559
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
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
1a8e690
WinConsoleUIATextInfo: As IUIAutomationTextPattern::getVisibleRanges…
michaelDCurran 3087fd3
Fix linting issues.
michaelDCurran 32b4e30
WinConsoleUIA: override _getTextLines with a faster implementation th…
michaelDCurran 44cb370
Fix linting issues.
michaelDCurran fe3cda9
Don't use isTextRangeOffscreen, and further improve POSITION_LAST to …
michaelDCurran 598b83f
Fix linting issues.
michaelDCurran 2224f50
WinConsoleUIATextInfo: ensure ranges are collapsed after moves.
michaelDCurran 2642e75
Remove TextInfo._expandCollapseBeforeReview as WinConsoleUIA no longe…
michaelDCurran ac8b273
Merge branch 'master' into i10119
michaelDCurran f9201d9
WinConsoleUIATextInfo: when calculating word / line offsets, grab tex…
michaelDCurran d1cadc5
Address review actions.
michaelDCurran ed458b3
Update source/NVDAObjects/UIA/winConsoleUIA.py
michaelDCurran File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -18,26 +18,35 @@ | |||||
|
|
||||||
|
|
||||||
| class consoleUIATextInfo(UIATextInfo): | ||||||
| #: At least on Windows 10 1903, expanding then collapsing the text info | ||||||
| #: caused review to get stuck, so disable it. | ||||||
| #: There may be no need to disable this anymore, but doing so doesn't seem | ||||||
| #: to do much good either. | ||||||
| _expandCollapseBeforeReview = False | ||||||
|
|
||||||
| def __init__(self, obj, position, _rangeObj=None): | ||||||
| # We want to limit textInfos to just the visible part of the console. | ||||||
| # Therefore we specifically handle POSITION_FIRST, POSITION_LAST and POSITION_ALL. | ||||||
| # We could use IUIAutomationTextRange::getVisibleRanges, but it seems very broken in consoles | ||||||
| # once more than a few screens worth of content has been written to the console. | ||||||
| # Therefore we resort to using IUIAutomationTextPattern::rangeFromPoint | ||||||
| # for the top left, and bottom right of the console window. | ||||||
| if position is textInfos.POSITION_FIRST: | ||||||
| _rangeObj = self.__class__(obj, obj.location.topLeft)._rangeObj | ||||||
| elif position is textInfos.POSITION_LAST: | ||||||
| # Asking for the range at the bottom right of the window | ||||||
| # Seems to sometimes ignore the x coordinate. | ||||||
| # Therefore use the bottom left, then move to the last character on that line. | ||||||
| tempInfo = self.__class__(obj, obj.location.bottomLeft) | ||||||
| tempInfo.expand(textInfos.UNIT_LINE) | ||||||
| # We must pull back the end by one character otherwise when we collapse to end, | ||||||
| # a console bug results in a textRange covering the entire console buffer! | ||||||
| # Strangely the *very* last character is a special blank point | ||||||
| # so we never seem to miss a real character. | ||||||
| UIATextInfo.move(tempInfo, textInfos.UNIT_CHARACTER, -1, endPoint="end") | ||||||
| tempInfo.setEndPoint(tempInfo, "startToEnd") | ||||||
| _rangeObj = tempInfo._rangeObj | ||||||
| elif position is textInfos.POSITION_ALL: | ||||||
| first = self.__class__(obj, textInfos.POSITION_FIRST) | ||||||
| last = self.__class__(obj, textInfos.POSITION_LAST) | ||||||
| first.setEndPoint(last, "endToEnd") | ||||||
| _rangeObj = first._rangeObj | ||||||
| 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 1803 and later.""" | ||||||
|
|
@@ -54,14 +63,12 @@ def collapse(self, end=False): | |||||
| ) | ||||||
|
|
||||||
| def move(self, unit, direction, endPoint=None): | ||||||
| oldRange = None | ||||||
| oldInfo = 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. | ||||||
| visiRanges = self.obj.UIATextPattern.GetVisibleRanges() | ||||||
| visiLength = visiRanges.length | ||||||
| if visiLength > 0: | ||||||
| oldRange = self._rangeObj.clone() | ||||||
| boundingInfo = self.obj.makeTextInfo(textInfos.POSITION_ALL) | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consistency with other code
Suggested change
|
||||||
| oldInfo = self.copy() | ||||||
| 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 | ||||||
|
|
@@ -116,16 +123,32 @@ def move(self, unit, direction, endPoint=None): | |||||
| else: # moving by a unit other than word | ||||||
| res = super(consoleUIATextInfo, self).move(unit, direction, | ||||||
| endPoint) | ||||||
| try: | ||||||
| if ( | ||||||
| oldRange | ||||||
| and isTextRangeOffscreen(self._rangeObj, visiRanges) | ||||||
| and not isTextRangeOffscreen(oldRange, visiRanges) | ||||||
| ): | ||||||
| self._rangeObj = oldRange | ||||||
| return 0 | ||||||
| except (COMError, RuntimeError): | ||||||
| pass | ||||||
| if not endPoint: | ||||||
| # #10191: IUIAutomationTextRange::move in consoles does not correctly produce a collapsed range | ||||||
| # after moving. | ||||||
| # Therefore manually collapse. | ||||||
| self.collapse() | ||||||
| # Console textRanges have access to the entire console buffer. | ||||||
| # However, we want to limit ourselves to onscreen text. | ||||||
| # Therefore, if the textInfo was originally visible, | ||||||
| # but we are now above or below the visible range, | ||||||
| # Restore the original textRange and pretend the move didn't work. | ||||||
| if oldInfo: | ||||||
| try: | ||||||
| if ( | ||||||
| ( | ||||||
| self.compareEndPoints(boundingInfo, "startToStart") < 0 | ||||||
| or self.compareEndPoints(boundingInfo, "startToEnd") >= 0 | ||||||
| ) | ||||||
| and not ( | ||||||
| oldInfo.compareEndPoints(boundingInfo, "startToStart") < 0 | ||||||
| or oldInfo.compareEndPoints(boundingInfo, "startToEnd") >= 0 | ||||||
| ) | ||||||
| ): | ||||||
| self._rangeObj = oldInfo._rangeObj | ||||||
| return 0 | ||||||
| except (COMError, RuntimeError): | ||||||
| pass | ||||||
| return res | ||||||
|
|
||||||
| def expand(self, unit): | ||||||
|
|
@@ -207,12 +230,12 @@ def _getCurrentOffsetInThisLine(self, lineInfo): | |||||
| # position a textInfo from the start of the line up to the current position. | ||||||
| charInfo = lineInfo.copy() | ||||||
| charInfo.setEndPoint(self, "endToStart") | ||||||
| text = charInfo.text | ||||||
| text = charInfo._rangeObj.getText(-1) | ||||||
| offset = textUtils.WideStringOffsetConverter(text).wideStringLength | ||||||
| return offset | ||||||
|
|
||||||
| def _getWordOffsetsInThisLine(self, offset, lineInfo): | ||||||
| lineText = lineInfo.text or u" " | ||||||
| lineText = lineInfo._rangeObj.getText(-1) | ||||||
| # Convert NULL and non-breaking space to space to make sure | ||||||
| # that words will break on them | ||||||
| lineText = lineText.translate({0: u' ', 0xa0: u' '}) | ||||||
|
|
@@ -282,11 +305,12 @@ def _get_TextInfo(self): | |||||
| return consoleUIATextInfo | ||||||
|
|
||||||
| def _getTextLines(self): | ||||||
|
LeonarddeR marked this conversation as resolved.
Resolved
|
||||||
| # 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 | ||||||
|
|
||||||
| # This override of _getTextLines takes advantage of the fact that | ||||||
| # the console text contains linefeeds for every line | ||||||
| # Thus a simple string splitlines is much faster than splitting by unit line. | ||||||
| ti = self.makeTextInfo(textInfos.POSITION_ALL) | ||||||
| text = ti.text or "" | ||||||
| return text.splitlines() | ||||||
|
|
||||||
| def findExtraOverlayClasses(obj, clsList): | ||||||
| if obj.UIAElement.cachedAutomationId == "Text Area": | ||||||
|
|
||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if we don't do this? We already expanded to line, so the endpoint should be the end of the line already?