-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Expose Text Attributes to UI Automation #10336
Conversation
src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp
Outdated
Show resolved
Hide resolved
src/types/UiaTextRangeBase.cpp
Outdated
// TODO CARLOS: how do I get the font size in points? | ||
if (queryFontSize == 0) //_pData->GetFontInfo().GetUnscaledSize()) | ||
{ | ||
Clone(ppRetVal); | ||
} |
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.
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.
You mean instead of pixels? I believe in the dx renderer is a conversion. You could move it to a helper
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.
I'm gonna punt on this one and file it as a follow-up because...
- we don't expect the font size to change within the buffer, so exposing it isn't urgent
- I'll have to do a different approach between ConHost and Windows Terminal here. And that just sounds like extra work for an already bulky PR.
7807ba6
to
2ae0ab8
Compare
Known Issues
Things that I should do
|
Please also consider regression testing against legacy console (i.e. disable the UIA feature flag and check text formatting). |
src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp
Outdated
Show resolved
Hide resolved
src/buffer/out/textBuffer.cpp
Outdated
// - limit - boundaries for the iterator to operate within | ||
// - until - X,Y position in buffer for last position for the iterator to read (inclusive) |
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 is the difference between a limit and an until? Why can they be different?
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.
Updated the comment to be more clear. Also renamed...
limit
-->bounds
until
-->limit
Theoretically, you could have...
bounds = bufferSize
&limit = (3,3)
: iterate fromat
throughlimit
(explores (4,3) --> (5,3) --> (6,3))bounds = (5,5)
&limit = (3,3)
: iterate fromat
throughlimit
acrossbounds
(explores (4,3) --> (5,3) --> (0,4))
THROW_IF_FAILED(_uiaProvider->GetAttributeValue(textAttributeId, &result)); | ||
|
||
// Convert the resulting VARIANT into a format that is consumable by XAML. | ||
switch (result.vt) |
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.
I'm surprised that nobody has made a C++ visitor specialization for a variant
src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp
Outdated
Show resolved
Hide resolved
UiaTracing::TextRange::GetAttributeValue(*this, attributeId, *pRetVal); | ||
return S_OK; | ||
} | ||
case UIA_IsReadOnlyAttributeId: | ||
{ | ||
pRetVal->vt = VT_BOOL; | ||
pRetVal->boolVal = VARIANT_FALSE; |
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.
well this rather depends: the entire buffer is technically "read-only"... it just happens to change when the connected application receives input.
A non-read-only buffer suggests that you could, e.g., select a region and delete it... or change its formatting or something.
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.
The Text Attribute ID docs:
Identifies the IsReadOnly text attribute, which indicates whether the text is read-only (TRUE) or can be modified (FALSE).
@codeofdusk made an interesting suggestion as to the interpretation of that text: append "by the user" at the end. In console, a user can interact with the buffer via key strokes, and those key strokes may modify any section of the buffer. Surprisingly, something like "text on a webpage" is also not read only! IsReadOnly
seems to be limited to text that is disabled and cannot be interacted with in any way.
2ae0ab8
to
2ee0cd8
Compare
2ee0cd8
to
a99fa3c
Compare
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.
I'm convinced that you should the COORD limit
logic into the 2 places that need it instead of TextBufferCellIterator
.
TextBufferCellIterator
at this point is already something of a magic hambeast that does everything and I actually think that concerns are better separated, by not having this logic in the basic iterator. Creating an iterator-adapter for instance would be rather clean, but just doing the loop-early-exit logic in those 2 places is also clean.
src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp
Outdated
Show resolved
Hide resolved
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.
14/17
@@ -276,6 +277,7 @@ class Microsoft::Terminal::Core::Terminal final : | |||
size_t _hyperlinkPatternId; | |||
|
|||
std::wstring _workingDirectory; | |||
std::optional<FontInfo> _fontInfo; |
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.
you may just want to drop the optional
and initialize this _fontInfo
directly with the fake fontinfo value in TerminalRenderData.cpp:R48.
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.
Then stomp it when the font changes.
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.
qq: why does this go through a value stored on the TerminalCore (which is a cache that could get out of sync) and not through some other component that knows what the font is?
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.
like: TSFInputControl had the same problem -- had to get the font -- and I am concerned that now there are two ways to get the font (new one and old one)
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.
So this is the part that sucks. IUiaData
is down in TerminalCore
. But all of the font info (_desiredFont
and _actualFont
) is actually up in ControlCore
. Because of this, we need to have ControlCore
tell TerminalCore
"hey, the font change to X" here.
TSFInputControl
looks like it works directly with the TerminalControl
(now ControlCore
since the split) to get what it needs.
src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp
Outdated
Show resolved
Hide resolved
@carlos-zamora @DHowett Since it might be interesting for others as well I'm going to post it here: C++ requires short-circuit evaluation and thus compilers will absolutely refuse to eagerly evaluate or inline the left-hand side of a boolean operator. For instance if you write You can see the drastic difference here: https://godbolt.org/z/sd9qY8Meh However |
it appears that you have talked yourself around into wanting short-circuit evaluation after all ;) |
Derp. Sorry, I missed that.
You mean function pointer like C-style? Or is there a nicer way to encapsulate it? |
…lambda for attr verification
Checked in with Leonard offline. I made it so that we don't return the lambda. Instead, we basically "run the lambda" that we would've returned. |
@DHowett putting this on your radar @zadjii-msft idk if you wanted to take another look, but I'll leave you as assigned anyways haha. |
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.
I trust both other signers here, and I did a medium-close read of the code myself. Best way to know is to try it.
Hello @carlos-zamora! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
🎉 Handy links: |
Fix-up of #10964. Summary of the issue: The link to Expose Text Attributes to UI Automation microsoft/terminal#10336 was broken by t2t preproc directives. Additional clarification on "NVDA API levels" might be appreciated by some users.
Summary of the Pull Request
This implements
GetAttributeValue
andFindAttribute
forUiaTextRangeBase
(the sharedITextRangeProvider
for Conhost and Windows Terminal). This also updatesUiaTracing
to collect more useful information on these function calls.References
#7000 - Epic
Text Attribute Identifiers
ITextRangeProvider::GetAttributeValue
ITextRangeProvider::FindAttribute
PR Checklist
Detailed Description of the Pull Request / Additional comments
TextBuffer
:TextBufferCellIterator
that takes in an end position. This simplifies the logic drastically as we can now use this iterator to navigate through the text buffer. The iterator can also expose the position in the buffer.UiaTextRangeBase
:TextAttribute
s in the text buffer. To extract them, we generate an attribute verification function via_getAttrVerificationFn()
, then use that to verify if a given cell has the desired attribute.GetAttributeValue
: Retrieve the attribute verification of the first cell in the range. Then, verify that the entire range has that attribute by iterating through the text range. If a cell does not have that attribute, return the "reserved mixed attribute value".FindAttribute
: Iterate through the text range and leverage the attribute verification function to find the first contiguous range with that attribute. Then, make the end exclusive and output aUiaTextRangeBase
. This function must be able to perform a search backwards, so we abstract the "start" and "end" intoresultFirstAnchor
andresultSecondAnchor
, then perform post processing to output a validUiaTextRangeBase
.UiaTracing
:GetAttributeValue
: Log uia text range, desired attribute, resulting attribute metadata, and the type of the result.FindAttribute
: Log uia text range, desired attribute and attribute metadata, if we were searching backwards, the type of the result, and the resulting text range.AttributeType
is a nice way to understand/record if the result was either of the reserved UIA values, a normal result, or an error.UiaTextRangeTests
:GetAttributeValue
:_getAttrVerificationFn()
)FindAttribute
:IsItalic
. NOTE: I'm explicitly only testing one of the standard text attributes because the logic is largely the same between all of them and they leverage_getAttrVerificationFn()
.Validation Steps Performed