2b8b034b89
This PR makes use of the UiaRenderer by attaching it to the TerminalControl and setting up selectionChanged events for accessibility. Part 1: attaching the UiaRenderer The uiaRenderer is treated very similarly to the dxRenderer. We have a unique_ptr ref to it in the TermControl. This gets populated when the TermControlAutomationPeer is created (thus enabling accessibility). To prevent every TermControl from sending signals simultaneously, we specifically only enable whichever one is in an active pane. The UiaRenderer needs to send encoded events to the automation provider (in this case, TermControlAutomationPeer). We needed our own automation events so that we can reuse this model for ConHost. This is the purpose of IUiaEventDispatcher. We need a dispatcher for the UiaRenderer. Otherwise, we would do a lot of work to find out when to fire an event, but we wouldn't have a way of doing that. Part 2: hooking up selection events This provides a little bit of polish to hooking it up before. Primarily to actually make it work. This includes returning S_FALSE instead of E_NOTIMPL. The main thing here really is just how to detect if a selection has changed. This also shows how clean adding more events will be in the future!
199 lines
7.6 KiB
C++
199 lines
7.6 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
#include <UIAutomationCore.h>
|
|
#include "TermControlAutomationPeer.h"
|
|
#include "TermControl.h"
|
|
#include "TermControlAutomationPeer.g.cpp"
|
|
|
|
#include "XamlUiaTextRange.h"
|
|
|
|
using namespace Microsoft::Console::Types;
|
|
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
|
|
|
|
namespace UIA
|
|
{
|
|
using ::ITextRangeProvider;
|
|
using ::SupportedTextSelection;
|
|
}
|
|
|
|
namespace XamlAutomation
|
|
{
|
|
using winrt::Windows::UI::Xaml::Automation::SupportedTextSelection;
|
|
using winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple;
|
|
using winrt::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider;
|
|
}
|
|
|
|
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|
{
|
|
TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* owner) :
|
|
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner) // pass owner to FrameworkElementAutomationPeer
|
|
{
|
|
THROW_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, owner, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this)));
|
|
};
|
|
|
|
// Method Description:
|
|
// - Signals the ui automation client that the terminal's selection has changed and should be updated
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void TermControlAutomationPeer::SignalSelectionChanged()
|
|
{
|
|
Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [&]() {
|
|
// The event that is raised when the text selection is modified.
|
|
RaiseAutomationEvent(AutomationEvents::TextPatternOnTextSelectionChanged);
|
|
});
|
|
}
|
|
|
|
// Method Description:
|
|
// - Signals the ui automation client that the terminal's output has changed and should be updated
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void TermControlAutomationPeer::SignalTextChanged()
|
|
{
|
|
Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [&]() {
|
|
// The event that is raised when textual content is modified.
|
|
RaiseAutomationEvent(AutomationEvents::TextPatternOnTextChanged);
|
|
});
|
|
}
|
|
|
|
// Method Description:
|
|
// - Signals the ui automation client that the cursor's state has changed and should be updated
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void TermControlAutomationPeer::SignalCursorChanged()
|
|
{
|
|
Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [&]() {
|
|
// The event that is raised when the text was changed in an edit control.
|
|
RaiseAutomationEvent(AutomationEvents::TextEditTextChanged);
|
|
});
|
|
}
|
|
|
|
winrt::hstring TermControlAutomationPeer::GetClassNameCore() const
|
|
{
|
|
return L"TermControl";
|
|
}
|
|
|
|
AutomationControlType TermControlAutomationPeer::GetAutomationControlTypeCore() const
|
|
{
|
|
return AutomationControlType::Text;
|
|
}
|
|
|
|
winrt::hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
|
|
{
|
|
// TODO GitHub #2142: Localize string
|
|
return L"TerminalControl";
|
|
}
|
|
|
|
winrt::Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const
|
|
{
|
|
switch (patternInterface)
|
|
{
|
|
case PatternInterface::Text:
|
|
return *this;
|
|
break;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
#pragma region ITextProvider
|
|
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection()
|
|
{
|
|
SAFEARRAY* pReturnVal;
|
|
THROW_IF_FAILED(_uiaProvider->GetSelection(&pReturnVal));
|
|
return WrapArrayOfTextRangeProviders(pReturnVal);
|
|
}
|
|
|
|
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges()
|
|
{
|
|
SAFEARRAY* pReturnVal;
|
|
THROW_IF_FAILED(_uiaProvider->GetVisibleRanges(&pReturnVal));
|
|
return WrapArrayOfTextRangeProviders(pReturnVal);
|
|
}
|
|
|
|
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement)
|
|
{
|
|
UIA::ITextRangeProvider* returnVal;
|
|
// ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr
|
|
THROW_IF_FAILED(_uiaProvider->RangeFromChild(/* IRawElementProviderSimple */ nullptr,
|
|
&returnVal));
|
|
|
|
auto parentProvider = this->ProviderFromPeer(*this);
|
|
auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
|
|
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
|
}
|
|
|
|
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation)
|
|
{
|
|
UIA::ITextRangeProvider* returnVal;
|
|
THROW_IF_FAILED(_uiaProvider->RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal));
|
|
|
|
auto parentProvider = this->ProviderFromPeer(*this);
|
|
auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
|
|
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
|
}
|
|
|
|
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::DocumentRange()
|
|
{
|
|
UIA::ITextRangeProvider* returnVal;
|
|
THROW_IF_FAILED(_uiaProvider->get_DocumentRange(&returnVal));
|
|
|
|
auto parentProvider = this->ProviderFromPeer(*this);
|
|
auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
|
|
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
|
}
|
|
|
|
Windows::UI::Xaml::Automation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection()
|
|
{
|
|
UIA::SupportedTextSelection returnVal;
|
|
THROW_IF_FAILED(_uiaProvider->get_SupportedTextSelection(&returnVal));
|
|
return static_cast<XamlAutomation::SupportedTextSelection>(returnVal);
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
RECT TermControlAutomationPeer::GetBoundingRectWrapped()
|
|
{
|
|
auto rect = GetBoundingRectangle();
|
|
return {
|
|
gsl::narrow<LONG>(rect.X),
|
|
gsl::narrow<LONG>(rect.Y),
|
|
gsl::narrow<LONG>(rect.X + rect.Width),
|
|
gsl::narrow<LONG>(rect.Y + rect.Height)
|
|
};
|
|
}
|
|
|
|
// Method Description:
|
|
// - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders
|
|
// Arguments:
|
|
// - SAFEARRAY of UIA::UiaTextRange (ITextRangeProviders)
|
|
// Return Value:
|
|
// - com_array of Xaml Wrapped UiaTextRange (ITextRangeProviders)
|
|
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
|
|
{
|
|
// transfer ownership of UiaTextRanges to this new vector
|
|
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::UiaTextRange>(textRanges);
|
|
int count = gsl::narrow<int>(providers.size());
|
|
|
|
std::vector<XamlAutomation::ITextRangeProvider> vec;
|
|
vec.reserve(count);
|
|
auto parentProvider = this->ProviderFromPeer(*this);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
auto xutr = winrt::make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
|
|
vec.emplace_back(xutr.as<XamlAutomation::ITextRangeProvider>());
|
|
}
|
|
|
|
winrt::com_array<XamlAutomation::ITextRangeProvider> result{ vec };
|
|
|
|
return result;
|
|
}
|
|
}
|