@@ -25,19 +25,29 @@ class consoleUIATextInfo(UIATextInfo):
2525 _expandCollapseBeforeReview = False
2626
2727 def __init__ (self , obj , position , _rangeObj = None ):
28+ # We want to limit textInfos to just the visible part of the console.
29+ # Therefore we specifically handle POSITION_FIRST, POSITION_LAST and POSITION_ALL.
30+ # We could use IUIAutomationTextRange::getVisibleRanges, but it seems very broken in consoles
31+ # once more than a few screens worth of content has been written to the console.
32+ # Therefore we resort to using IUIAutomationTextPattern::rangeFromPoint
33+ # for the top left, and bottom right of the console window.
34+ if position is textInfos .POSITION_FIRST :
35+ _rangeObj = self .__class__ (obj , obj .location .topLeft )._rangeObj
36+ elif position is textInfos .POSITION_LAST :
37+ # Asking for the range at the bottom right of the window
38+ # Seems to sometimes ignore the x coordinate.
39+ # Therefore use the bottom left, then move the to last character on that line.
40+ tempInfo = self .__class__ (obj , obj .location .bottomLeft )
41+ tempInfo .expand (textInfos .UNIT_LINE )
42+ UIATextInfo .move (tempInfo ,textInfos .UNIT_CHARACTER ,- 1 ,endPoint = "end" )
43+ tempInfo .setEndPoint (tempInfo ,"startToEnd" )
44+ _rangeObj = tempInfo ._rangeObj
45+ elif position is textInfos .POSITION_ALL :
46+ first = self .__class__ (obj , textInfos .POSITION_FIRST )
47+ last = self .__class__ (obj , textInfos .POSITION_LAST )
48+ first .setEndPoint (last , "endToEnd" )
49+ _rangeObj = first ._rangeObj
2850 super (consoleUIATextInfo , self ).__init__ (obj , position , _rangeObj )
29- # Re-implement POSITION_FIRST and POSITION_LAST in terms of
30- # visible ranges to fix review top/bottom scripts.
31- if position == textInfos .POSITION_FIRST :
32- visiRanges = self .obj .UIATextPattern .GetVisibleRanges ()
33- firstVisiRange = visiRanges .GetElement (0 )
34- self ._rangeObj = firstVisiRange
35- self .collapse ()
36- elif position == textInfos .POSITION_LAST :
37- visiRanges = self .obj .UIATextPattern .GetVisibleRanges ()
38- lastVisiRange = visiRanges .GetElement (visiRanges .length - 1 )
39- self ._rangeObj = lastVisiRange
40- self .collapse (True )
4151
4252 def collapse (self , end = False ):
4353 """Works around a UIA bug on Windows 10 1803 and later."""
@@ -54,14 +64,12 @@ def collapse(self, end=False):
5464 )
5565
5666 def move (self , unit , direction , endPoint = None ):
57- oldRange = None
67+ oldInfo = None
5868 if self .basePosition != textInfos .POSITION_CARET :
5969 # Insure we haven't gone beyond the visible text.
6070 # UIA adds thousands of blank lines to the end of the console.
61- visiRanges = self .obj .UIATextPattern .GetVisibleRanges ()
62- visiLength = visiRanges .length
63- if visiLength > 0 :
64- oldRange = self ._rangeObj .clone ()
71+ boundingInfo = self .obj .makeTextInfo (textInfos .POSITION_ALL )
72+ oldInfo = self .copy ()
6573 if unit == textInfos .UNIT_WORD and direction != 0 :
6674 # UIA doesn't implement word movement, so we need to do it manually.
6775 # Relative to the current line, calculate our offset
@@ -118,11 +126,17 @@ def move(self, unit, direction, endPoint=None):
118126 endPoint )
119127 try :
120128 if (
121- oldRange
122- and isTextRangeOffscreen (self ._rangeObj , visiRanges )
123- and not isTextRangeOffscreen (oldRange , visiRanges )
129+ oldInfo
130+ and (
131+ self .compareEndPoints (boundingInfo , "startToStart" ) < 0 or
132+ self .compareEndPoints (boundingInfo , "startToEnd" ) >= 0
133+ )
134+ and not (
135+ oldInfo .compareEndPoints (boundingInfo , "startToStart" ) < 0 or
136+ oldInfo .compareEndPoints (boundingInfo , "startToEnd" ) >= 0
137+ )
124138 ):
125- self ._rangeObj = oldRange
139+ self ._rangeObj = oldInfo . _rangeObj
126140 return 0
127141 except (COMError , RuntimeError ):
128142 pass
@@ -273,11 +287,12 @@ def _get_TextInfo(self):
273287 return consoleUIATextInfo
274288
275289 def _getTextLines (self ):
276- # Filter out extraneous empty lines from UIA
277- ptr = self .UIATextPattern .GetVisibleRanges ()
278- res = [ptr .GetElement (i ).GetText (- 1 ) for i in range (ptr .length )]
279- return res
280-
290+ # This override of _getTextLines takes advantage of the fact that
291+ # the console text contains linefeeds for every line
292+ # Thus a simple string splitlines is much faster than splitting by unit line.
293+ ti = self .makeTextInfo (textInfos .POSITION_ALL )
294+ text = ti .text or ""
295+ return text .splitlines ()
281296
282297def findExtraOverlayClasses (obj , clsList ):
283298 if obj .UIAElement .cachedAutomationId == "Text Area" :
0 commit comments