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: rename is21H1Plus to isImprovedTextRangeAvailable #12094

Merged
merged 3 commits into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 45 additions & 34 deletions source/NVDAObjects/UIA/winConsoleUIA.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2019-2020 Bill Dengler
# Copyright (C) 2019-2021 Bill Dengler

import ctypes
import NVDAHelper
Expand All @@ -16,7 +16,7 @@
from ..window import Window


class consoleUIATextInfo(UIATextInfo):
class ConsoleUIATextInfo(UIATextInfo):
def __init__(self, obj, position, _rangeObj=None):
collapseToEnd = None
# We want to limit textInfos to just the visible part of the console.
Expand All @@ -33,7 +33,7 @@ def __init__(self, obj, position, _rangeObj=None):
log.warning("Couldn't get bounding range for console", exc_info=True)
# Fall back to presenting the entire buffer.
_rangeObj, collapseToEnd = None, None
super(consoleUIATextInfo, self).__init__(obj, position, _rangeObj)
super(ConsoleUIATextInfo, self).__init__(obj, position, _rangeObj)
if collapseToEnd is not None:
self.collapse(end=collapseToEnd)

Expand Down Expand Up @@ -90,26 +90,17 @@ def move(self, unit, direction, endPoint=None):

def _move(self, unit, direction, endPoint=None):
"Perform a move without respect to bounding."
return super(consoleUIATextInfo, self).move(unit, direction, endPoint)
return super(ConsoleUIATextInfo, self).move(unit, direction, endPoint)

def __ne__(self, other):
"""Support more accurate caret move detection."""
return not self == other

def _get_text(self):
# #10036: return a space if the text range is empty.
# Consoles don't actually store spaces, the character is merely left blank.
res = super(consoleUIATextInfo, self)._get_text()
if not res:
return ' '
else:
return res


class consoleUIATextInfoPre21H1(consoleUIATextInfo):
"""Fixes expand/collapse on end inclusive UIA text ranges, uses rangeFromPoint
instead of broken GetVisibleRanges for bounding, and implements word
movement support."""
class ConsoleUIATextInfoWorkaroundEndInclusive(ConsoleUIATextInfo):
"""Implementation of various workarounds for pre-microsoft/terminal#4018
conhost: fixes expand/collapse, uses rangeFromPoint instead of broken
GetVisibleRanges for bounding, and implements word movement support."""
def _getBoundingRange(self, obj, position):
# 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.
Expand Down Expand Up @@ -139,12 +130,12 @@ def _getBoundingRange(self, obj, position):
return (_rangeObj, None)

def collapse(self, end=False):
"""Works around a UIA bug on Windows 10 versions before 21H1.
"""Works around a UIA bug on conhost versions before microsoft/terminal#4018.
When collapsing, consoles seem to incorrectly push the start of the
textRange back one character.
Correct this by bringing the start back up to where the end is."""
oldInfo = self.copy()
super(consoleUIATextInfo, self).collapse(end=end)
super(ConsoleUIATextInfo, self).collapse(end=end)
if not end:
self._rangeObj.MoveEndpointByRange(
UIAHandler.TextPatternRangeEndpoint_Start,
Expand All @@ -153,7 +144,7 @@ def collapse(self, end=False):
)

def compareEndPoints(self, other, which):
"""Works around a UIA bug on Windows 10 versions before 21H1.
"""Works around a UIA bug on conhost versions before microsoft/terminal#4018.
Even when a console textRange's start and end have been moved to the
same position, the console incorrectly reports the end as being
past the start.
Expand All @@ -168,7 +159,7 @@ def compareEndPoints(self, other, which):

def setEndPoint(self, other, which):
"""Override of L{textInfos.TextInfo.setEndPoint}.
Works around a UIA bug on Windows 10 versions before 21H1 that means we can
Works around a UIA bug on conhost versions before microsoft/terminal#4018 that means we can
not trust the "end" endpoint of a collapsed (empty) text range
for comparisons.
"""
Expand Down Expand Up @@ -205,11 +196,11 @@ def expand(self, unit):
wordEndPoints[1]
)
else:
return super(consoleUIATextInfo, self).expand(unit)
return super(ConsoleUIATextInfo, self).expand(unit)

def _move(self, unit, direction, endPoint=None):
if unit == textInfos.UNIT_WORD and direction != 0:
# On Windows 10 versions before 21H1, UIA doesn't implement word
# On conhost versions before microsoft/terminal#4018, 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.
Expand Down Expand Up @@ -261,8 +252,7 @@ def _move(self, unit, direction, endPoint=None):
endPoint=endPoint
)
else: # moving by a unit other than word
res = super(consoleUIATextInfo, self).move(unit, direction,
endPoint)
res = super(ConsoleUIATextInfo, self).move(unit, direction, endPoint)
if not endPoint:
# #10191: IUIAutomationTextRange::move in consoles does not correctly produce a collapsed range
# after moving.
Expand Down Expand Up @@ -309,7 +299,7 @@ def _getWordOffsetsInThisLine(self, offset, lineInfo):
)

def _isCollapsed(self):
"""Works around a UIA bug on Windows 10 versions before 21H1 that means we
"""Works around a UIA bug on conhost versions before microsoft/terminal#4018 that means we
cannot trust the "end" endpoint of a collapsed (empty) text range
for comparisons.
Instead we check to see if we can get the first character from the
Expand All @@ -322,6 +312,15 @@ def _get_isCollapsed(self):
# Check if it has no text.
return self._isCollapsed()

def _get_text(self):
# #10036: return a space if the text range is empty.
# Consoles don't actually store spaces, the character is merely left blank.
res = super()._get_text()
if not res:
return ' '
else:
return res


class consoleUIAWindow(Window):
# This is the parent of the console text area, which sometimes gets focus after the text area.
Expand All @@ -348,19 +347,31 @@ def _get_windowThreadID(self):
threadID = super().windowThreadID
return threadID

def _get_is21H1Plus(self):
"Returns whether this is a newer version of Windows Console with an improved UIA implementation."
def _get_isImprovedTextRangeAvailable(self):
"""This property determines whether microsoft/terminal#4495
and by extension microsoft/terminal#4018 are present in this conhost.
In consoles before these PRs, a number of workarounds were needed
in our UIA implementation. However, these do not fix all bugs and are
problematic on newer console releases. This property is therefore used
internally to determine whether to activate workarounds and as a
convenience when debugging.
"""
# microsoft/terminal#4495: In newer consoles,
# IUIAutomationTextRange::getVisibleRanges returns one visible range.
# Therefore, if exactly one range is returned, it is almost definitely a newer console.
return self.UIATextPattern.GetVisibleRanges().length == 1

def _get_TextInfo(self):
"""Overriding _get_TextInfo and thus the TextInfo property
"""Overriding _get_ConsoleUIATextInfo and thus the ConsoleUIATextInfo property
on NVDAObjects.UIA.UIA
consoleUIATextInfo bounds review to the visible text.
ConsoleUIATextInfoPre21H1 fixes expand/collapse and implements word
movement."""
return consoleUIATextInfo if self.is21H1Plus else consoleUIATextInfoPre21H1
ConsoleUIATextInfo bounds review to the visible text.
ConsoleUIATextInfoWorkaroundEndInclusive fixes expand/collapse and implements
word movement."""
return (
ConsoleUIATextInfo
if self.isImprovedTextRangeAvailable
else ConsoleUIATextInfoWorkaroundEndInclusive
)

def detectPossibleSelectionChange(self):
try:
Expand All @@ -384,4 +395,4 @@ def findExtraOverlayClasses(obj, clsList):

class WinTerminalUIA(EnhancedTermTypedCharSupport):
def _get_TextInfo(self):
return consoleUIATextInfo
return ConsoleUIATextInfo
6 changes: 6 additions & 0 deletions user_docs/en/changes.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ This release also drops support for Adobe Flash.
- ``speech.speechMode_beeps`` becomes ``speech.SpeechMode.beeps``
- ``speech.speechMode_talk`` becomes ``speech.SpeechMode.talk``
- ``IAccessibleHandler.IAccessibleObjectIdentifierType`` is now ``IAccessibleHandler.types.IAccessibleObjectIdentifierType``. (#12367)
- The following in ``NVDAObjects.UIA.WinConsoleUIA`` have been changed (#12094)
- ``NVDAObjects.UIA.winConsoleUIA.is21H1Plus`` renamed ``NVDAObjects.UIA.winConsoleUIA.isImprovedTextRangeAvailable``.
- ``NVDAObjects.UIA.winConsoleUIA.consoleUIATextInfo`` renamed to start class name with upper case.
- ``NVDAObjects.UIA.winConsoleUIA.consoleUIATextInfoPre21H1`` renamed ``NVDAObjects.UIA.winConsoleUIA.ConsoleUIATextInfoWorkaroundEndInclusive``
- The implementation works around both end points being inclusive (in text ranges) before [microsoft/terminal PR 4018 https://github.com/microsoft/terminal/pull/4018]
- Workarounds for ``expand``, ``collapse``, ``compareEndPoints``, ``setEndPoint``, etc


= 2020.4 =
Expand Down