-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
[wpf] Add UIA events #14097
[wpf] Add UIA events #14097
Conversation
Does this new control use the same UIA class name? How can I test it? |
2d8e599
to
9fd73c7
Compare
- remove extra static window - improve window initialization order - pass WM_GETOBJECT to HwndTerminal - set accessibility view to raw on TermControl - rename UIA class name to WPFTermControl - fix build for WpfTerminalTestNetCore (Debug and Release)
if (WM_NCCREATE == uMsg) | ||
{ | ||
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast | ||
auto cs = reinterpret_cast<CREATESTRUCT*>(lParam); |
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.
BTW you can now use std::bit_cast
for this. But it's not particularly important.
// the key event's character (i.e. the "A" key) matches | ||
// the output character (i.e. "a" or "A" text). | ||
// We can assume that the output character resulted from | ||
// the pressed key, so we can ignore it. |
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's no way this works with Unicode, right? ._.
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.
Probably not, but that should be fine. We're really just using this to suppress key events. So, when you press the A key, normally you'd hear "A" from pressing the key and "A" from rendering the key. If we suppress that second event, it sounds/feels more natural.
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.
memory questions!
Hello @DHowett! 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 (
|
Adds UIA events to the WPF control for the following items: - selection changed - text changed (and output) - cursor changed Similar to the architecture of the UWP TermControl, we added a `HwndTerminalAutomationPeer` which acts as the `TermControlAutomationPeer` in UWP. However, we don't need a XAML wrapper here, so really we just need it to inherit from `TermControlUiaProvider` (the `ITextProvider` implementation shared across conhost and WT) and `IUiaEventDispatcher` (the event dispatching interface that is responsible for signaling the screen reader that something has changed). As with WT, we need to record key events to remove the local echo. These recorded events are matched up with the output text. Each sequential match is removed in the output text so that it's not read by the screen reader. As with WT, a `UiaEngine` was added to the renderer and it is set up when a UIA client is detected. WT would normally stop sending events when focus was lost from the control. We do the same here. `TermControlUiaProvider` was upgraded to support property values. Such properties include class name and control type. These align with those set in `TermControlAutomationPeer`. Realistically, those should point to these, but that requires a lot more work and a localization burden (because we need to move the localized word "terminal"). `HwndTerminalAutomationPeer` takes this a step further and overrides the class name to be `WPFTermControl`. This allows screen readers to provide special handling for the `WPFTermControl` vs the UWP term control since they will be updating at different speeds. To build the WPF test app, I had to mess with the dependencies a little bit. Really just add the atlas engine and uia renderer to the build steps. The initialization order with `WM_NCCREATE` was changed to match that of Windows Terminal (BaseWindow/IslandWindow). This is safer now. I also removed the `static` window because it was unnecessary. WPF's HwndHost likes to mark the `WM_GETOBJECT` message as handled to force the usage of the WPF automation peer. We now explicitly mark it as not handled and don't return an automation peer. This forces the message to go down to the HwndTerminal where we return terminal's UiaProvider. TermContol (the top-most layer in the UIA tree) would pop up and not do anything. This PR also overrides the automation peer at that layer and marks IsContentElement/IsControlElement=false (the equivalent to AccessibilityView=Raw). This makes the layer only appear in the UIA tree if you are using the raw view (i.e. you know what you're doing and you want to see each individual layer even if you can't directly interact with it). Tested with Narrator/NVDA using WpfTerminalTestNetCore project in our repo. - [X] New output is read out (not just key events, but also other output text) - [X] Local echo does not occur (i.e. pressing 'A' should only read 'A' once, not twice [key event and rendered letter]). - [X] selection events are read out properly - [X] cursor change events are read out properly (tested with text cursor indicator preview in Settings App > Accessibility > Text Cursor) NOTE: test this with Release builds. Debug builds may be too slow and not read out properly Closes #12642 (cherry picked from commit 5608cf1) Service-Card-Id: 86081128 Service-Version: 1.15
Adds UIA events to the WPF control for the following items: - selection changed - text changed (and output) - cursor changed ### Automation Peer Similar to the architecture of the UWP TermControl, we added a `HwndTerminalAutomationPeer` which acts as the `TermControlAutomationPeer` in UWP. However, we don't need a XAML wrapper here, so really we just need it to inherit from `TermControlUiaProvider` (the `ITextProvider` implementation shared across conhost and WT) and `IUiaEventDispatcher` (the event dispatching interface that is responsible for signaling the screen reader that something has changed). ### Removing the local echo As with WT, we need to record key events to remove the local echo. These recorded events are matched up with the output text. Each sequential match is removed in the output text so that it's not read by the screen reader. ### Detecting what to send events for As with WT, a `UiaEngine` was added to the renderer and it is set up when a UIA client is detected. WT would normally stop sending events when focus was lost from the control. We do the same here. ### Automation properties `TermControlUiaProvider` was upgraded to support property values. Such properties include class name and control type. These align with those set in `TermControlAutomationPeer`. Realistically, those should point to these, but that requires a lot more work and a localization burden (because we need to move the localized word "terminal"). `HwndTerminalAutomationPeer` takes this a step further and overrides the class name to be `WPFTermControl`. This allows screen readers to provide special handling for the `WPFTermControl` vs the UWP term control since they will be updating at different speeds. ### Build fixes To build the WPF test app, I had to mess with the dependencies a little bit. Really just add the atlas engine and uia renderer to the build steps. ### HwndTerminal initialization The initialization order with `WM_NCCREATE` was changed to match that of Windows Terminal (BaseWindow/IslandWindow). This is safer now. I also removed the `static` window because it was unnecessary. ### Handling `WM_GETOBJECT` WPF's HwndHost likes to mark the `WM_GETOBJECT` message as handled to force the usage of the WPF automation peer. We now explicitly mark it as not handled and don't return an automation peer. This forces the message to go down to the HwndTerminal where we return terminal's UiaProvider. ### Remove TermControl layer from UIA tree TermContol (the top-most layer in the UIA tree) would pop up and not do anything. This PR also overrides the automation peer at that layer and marks IsContentElement/IsControlElement=false (the equivalent to AccessibilityView=Raw). This makes the layer only appear in the UIA tree if you are using the raw view (i.e. you know what you're doing and you want to see each individual layer even if you can't directly interact with it). ## Validation Steps Performed Tested with Narrator/NVDA using WpfTerminalTestNetCore project in our repo. - [X] New output is read out (not just key events, but also other output text) - [X] Local echo does not occur (i.e. pressing 'A' should only read 'A' once, not twice [key event and rendered letter]). - [X] selection events are read out properly - [X] cursor change events are read out properly (tested with text cursor indicator preview in Settings App > Accessibility > Text Cursor) NOTE: test this with Release builds. Debug builds may be too slow and not read out properly Closes #12642 (cherry picked from commit 5608cf1) Service-Card-Id: 86081129 Service-Version: 1.16
🎉 Handy links: |
🎉 Handy links: |
Adds UIA events to the WPF control for the following items:
Automation Peer
Similar to the architecture of the UWP TermControl, we added a
HwndTerminalAutomationPeer
which acts as theTermControlAutomationPeer
in UWP. However, we don't need a XAMLwrapper here, so really we just need it to inherit from
TermControlUiaProvider
(theITextProvider
implementation sharedacross conhost and WT) and
IUiaEventDispatcher
(the event dispatchinginterface that is responsible for signaling the screen reader that
something has changed).
Removing the local echo
As with WT, we need to record key events to remove the local echo. These
recorded events are matched up with the output text. Each sequential
match is removed in the output text so that it's not read by the screen
reader.
Detecting what to send events for
As with WT, a
UiaEngine
was added to the renderer and it is set upwhen a UIA client is detected. WT would normally stop sending events
when focus was lost from the control. We do the same here.
Automation properties
TermControlUiaProvider
was upgraded to support property values. Suchproperties include class name and control type. These align with those
set in
TermControlAutomationPeer
. Realistically, those should point tothese, but that requires a lot more work and a localization burden
(because we need to move the localized word "terminal").
HwndTerminalAutomationPeer
takes this a step further and overrides theclass name to be
WPFTermControl
. This allows screen readers to providespecial handling for the
WPFTermControl
vs the UWP term control sincethey will be updating at different speeds.
Build fixes
To build the WPF test app, I had to mess with the dependencies a little
bit. Really just add the atlas engine and uia renderer to the build
steps.
HwndTerminal initialization
The initialization order with
WM_NCCREATE
was changed to match that ofWindows Terminal (BaseWindow/IslandWindow). This is safer now. I also
removed the
static
window because it was unnecessary.Handling
WM_GETOBJECT
WPF's HwndHost likes to mark the
WM_GETOBJECT
message as handled toforce the usage of the WPF automation peer. We now explicitly mark it as
not handled and don't return an automation peer. This forces the message
to go down to the HwndTerminal where we return terminal's UiaProvider.
Remove TermControl layer from UIA tree
TermContol (the top-most layer in the UIA tree) would pop up and not do
anything. This PR also overrides the automation peer at that layer and
marks IsContentElement/IsControlElement=false (the equivalent to
AccessibilityView=Raw). This makes the layer only appear in the UIA tree
if you are using the raw view (i.e. you know what you're doing and you
want to see each individual layer even if you can't directly interact
with it).
Validation Steps Performed
Tested with Narrator/NVDA using WpfTerminalTestNetCore project in our
repo.
text)
once, not twice [key event and rendered letter]).
cursor indicator preview in Settings App > Accessibility > Text
Cursor)
NOTE: test this with Release builds. Debug builds may be too slow and
not read out properly
Closes #12642