Custom WinRT implementation

This commit is contained in:
Leonard Hecker 2025-12-09 16:40:21 +01:00
parent 62312a8943
commit f02bfe06dc
5 changed files with 397 additions and 181 deletions

View File

@ -49,7 +49,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void InteractivityAutomationPeer::ParentProvider(AutomationPeer parentProvider) void InteractivityAutomationPeer::ParentProvider(AutomationPeer parentProvider)
{ {
_parentProvider = parentProvider; // LOAD-BEARING: use _parentProvider->ProviderFromPeer(_parentProvider) instead of this->ProviderFromPeer(*this).
// Since we split the automation peer into TermControlAutomationPeer and InteractivityAutomationPeer,
// using "this" returns null. This can cause issues with some UIA Client scenarios like any navigation in Narrator.
_parentProvider = parentProvider ? parentProvider.as<IAutomationPeerProtected>().ProviderFromPeer(parentProvider) : nullptr;
} }
// Method Description: // Method Description:
@ -181,15 +184,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::_CreateXamlUiaTextRange(UIA::ITextRangeProvider* returnVal) const XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::_CreateXamlUiaTextRange(UIA::ITextRangeProvider* returnVal) const
{ {
// LOAD-BEARING: use _parentProvider->ProviderFromPeer(_parentProvider) instead of this->ProviderFromPeer(*this). if (!_parentProvider)
// Since we split the automation peer into TermControlAutomationPeer and InteractivityAutomationPeer,
// using "this" returns null. This can cause issues with some UIA Client scenarios like any navigation in Narrator.
const auto parent{ _parentProvider.get() };
if (!parent)
{ {
return nullptr; return nullptr;
} }
return winrt::make<XamlUiaTextRange>(returnVal, parent.as<IAutomationPeerProtected>().ProviderFromPeer(parent)); return XamlUiaTextRange::Create(returnVal, _parentProvider);
}; };
// Method Description: // Method Description:
@ -200,21 +199,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - com_array of Xaml Wrapped UiaTextRange (ITextRangeProviders) // - com_array of Xaml Wrapped UiaTextRange (ITextRangeProviders)
com_array<XamlAutomation::ITextRangeProvider> InteractivityAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges) com_array<XamlAutomation::ITextRangeProvider> InteractivityAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
{ {
// transfer ownership of UiaTextRanges to this new vector if (!_parentProvider)
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(textRanges);
auto count = gsl::narrow<int>(providers.size());
std::vector<XamlAutomation::ITextRangeProvider> vec;
vec.reserve(count);
for (auto i = 0; i < count; i++)
{ {
if (auto xutr = _CreateXamlUiaTextRange(providers[i].detach())) return {};
{
vec.emplace_back(std::move(xutr));
}
} }
com_array<XamlAutomation::ITextRangeProvider> result{ vec }; // transfer ownership of UiaTextRanges to this new vector
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(textRanges);
const auto len = gsl::narrow<uint32_t>(providers.size());
com_array<XamlAutomation::ITextRangeProvider> result{ len };
for (uint32_t i = 0; i < len; ++i)
{
result[i] = XamlUiaTextRange::Create(providers[i].detach(), _parentProvider);
}
return result; return result;
} }

View File

@ -80,7 +80,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider; ::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity; winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
weak_ref<Windows::UI::Xaml::Automation::Peers::AutomationPeer> _parentProvider; winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider{ nullptr };
til::rect _controlBounds{}; til::rect _controlBounds{};
til::rect _controlPadding{}; til::rect _controlPadding{};

View File

@ -121,6 +121,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// GH#13978: If the TermControl has already been removed from the UI tree, XAML might run into weird bugs. // GH#13978: If the TermControl has already been removed from the UI tree, XAML might run into weird bugs.
// This will prevent the `dispatcher.RunAsync` calls below from raising UIA events on the main thread. // This will prevent the `dispatcher.RunAsync` calls below from raising UIA events on the main thread.
_termControl = {}; _termControl = {};
// Solve the circular reference between us and the content automation peer.
_contentAutomationPeer.ParentProvider(nullptr);
} }
// Method Description: // Method Description:
@ -338,27 +341,43 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma region ITextProvider #pragma region ITextProvider
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection() com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection()
{ {
return _contentAutomationPeer.GetSelection(); auto ret = _contentAutomationPeer.GetSelection();
for (const auto& r : ret)
{
assert(((XamlUiaTextRange*)winrt::get_abi(r))->_refCount.load(std::memory_order_relaxed) == 1);
}
return ret;
} }
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges() com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges()
{ {
return _contentAutomationPeer.GetVisibleRanges(); auto ret = _contentAutomationPeer.GetVisibleRanges();
for (const auto& r : ret)
{
assert(((XamlUiaTextRange*)winrt::get_abi(r))->_refCount.load(std::memory_order_relaxed) == 1);
}
return ret;
} }
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement) XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement)
{ {
return _contentAutomationPeer.RangeFromChild(childElement); auto r = _contentAutomationPeer.RangeFromChild(childElement);
assert(((XamlUiaTextRange*)winrt::get_abi(r))->_refCount.load(std::memory_order_relaxed) == 1);
return r;
} }
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation) XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation)
{ {
return _contentAutomationPeer.RangeFromPoint(screenLocation); auto r = _contentAutomationPeer.RangeFromPoint(screenLocation);
assert(((XamlUiaTextRange*)winrt::get_abi(r))->_refCount.load(std::memory_order_relaxed) == 1);
return r;
} }
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::DocumentRange() XamlAutomation::ITextRangeProvider TermControlAutomationPeer::DocumentRange()
{ {
return _contentAutomationPeer.DocumentRange(); auto r = _contentAutomationPeer.DocumentRange();
assert(((XamlUiaTextRange*)winrt::get_abi(r))->_refCount.load(std::memory_order_relaxed) == 1);
return r;
} }
XamlAutomation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection() XamlAutomation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection()

View File

@ -33,6 +33,14 @@ static std::atomic<int64_t> refCount_XamlUiaTextRange;
namespace winrt::Microsoft::Terminal::Control::implementation namespace winrt::Microsoft::Terminal::Control::implementation
{ {
XamlAutomation::ITextRangeProvider XamlUiaTextRange::Create(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider)
{
const auto self = new XamlUiaTextRange(uiaProvider, parentProvider);
XamlAutomation::ITextRangeProvider provider{ nullptr };
winrt::attach_abi(provider, (ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider*)self);
return provider;
}
XamlUiaTextRange::XamlUiaTextRange(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider) : XamlUiaTextRange::XamlUiaTextRange(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider) :
_parentProvider{ std::move(parentProvider) } _parentProvider{ std::move(parentProvider) }
{ {
@ -46,225 +54,398 @@ namespace winrt::Microsoft::Terminal::Control::implementation
refCount_XamlUiaTextRange.fetch_sub(1, std::memory_order_relaxed); refCount_XamlUiaTextRange.fetch_sub(1, std::memory_order_relaxed);
} }
XamlAutomation::ITextRangeProvider XamlUiaTextRange::Clone() const IFACEMETHODIMP_(ULONG) XamlUiaTextRange::AddRef() noexcept
{ {
UIA::ITextRangeProvider* pReturn; return _refCount.fetch_add(1, std::memory_order_relaxed) + 1;
THROW_IF_FAILED(_uiaProvider->Clone(&pReturn));
return winrt::make<XamlUiaTextRange>(pReturn, _parentProvider);
} }
bool XamlUiaTextRange::Compare(XamlAutomation::ITextRangeProvider pRange) const IFACEMETHODIMP_(ULONG) XamlUiaTextRange::Release() noexcept
{ {
auto self = winrt::get_self<XamlUiaTextRange>(pRange); const auto count = _refCount.fetch_sub(1, std::memory_order_release) - 1;
if (count == 0)
{
std::atomic_thread_fence(std::memory_order_acquire);
delete this;
}
return count;
}
IFACEMETHODIMP XamlUiaTextRange::QueryInterface(REFIID riid, void** ppvObject) noexcept
{
if (!ppvObject)
{
return E_INVALIDARG;
}
*ppvObject = nullptr;
if (riid == __uuidof(ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider) ||
riid == __uuidof(IInspectable) ||
riid == __uuidof(IUnknown))
{
*ppvObject = static_cast<ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider*>(this);
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
IFACEMETHODIMP XamlUiaTextRange::GetIids(ULONG* iidCount, IID** iids) noexcept
{
if (!iidCount || !iids)
{
return E_INVALIDARG;
}
*iidCount = 1;
*iids = static_cast<IID*>(CoTaskMemAlloc(sizeof(IID)));
if (!*iids)
{
return E_OUTOFMEMORY;
}
(*iids)[0] = __uuidof(ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider);
return S_OK;
}
IFACEMETHODIMP XamlUiaTextRange::GetRuntimeClassName(HSTRING* className) noexcept
{
if (!className)
{
return E_INVALIDARG;
}
return WindowsCreateString(L"Microsoft.Terminal.Control.XamlUiaTextRange", 47, className);
}
IFACEMETHODIMP XamlUiaTextRange::GetTrustLevel(TrustLevel* trustLevel) noexcept
{
if (!trustLevel)
{
return E_INVALIDARG;
}
*trustLevel = TrustLevel::BaseTrust;
return S_OK;
}
IFACEMETHODIMP XamlUiaTextRange::Clone(ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider** result) noexcept
try
{
RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = nullptr;
UIA::ITextRangeProvider* pReturn;
RETURN_IF_FAILED(_uiaProvider->Clone(&pReturn));
auto xamlRange = new (std::nothrow) XamlUiaTextRange(pReturn, _parentProvider);
RETURN_IF_NULL_ALLOC(xamlRange);
*result = xamlRange;
return S_OK;
}
CATCH_RETURN();
IFACEMETHODIMP XamlUiaTextRange::Compare(ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider* textRangeProvider, boolean* result) noexcept
try
{
RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = false;
RETURN_HR_IF_NULL(E_INVALIDARG, textRangeProvider);
auto self = static_cast<XamlUiaTextRange*>(textRangeProvider);
BOOL returnVal; BOOL returnVal;
THROW_IF_FAILED(_uiaProvider->Compare(self->_uiaProvider.get(), &returnVal)); RETURN_IF_FAILED(_uiaProvider->Compare(self->_uiaProvider.get(), &returnVal));
return returnVal; *result = !!returnVal;
return S_OK;
} }
CATCH_RETURN();
int32_t XamlUiaTextRange::CompareEndpoints(XamlAutomation::TextPatternRangeEndpoint endpoint, IFACEMETHODIMP XamlUiaTextRange::CompareEndpoints(ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint,
XamlAutomation::ITextRangeProvider pTargetRange, ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider* textRangeProvider,
XamlAutomation::TextPatternRangeEndpoint targetEndpoint) ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint,
INT32* result) noexcept
try
{ {
auto self = winrt::get_self<XamlUiaTextRange>(pTargetRange); RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = 0;
RETURN_HR_IF_NULL(E_INVALIDARG, textRangeProvider);
auto self = static_cast<XamlUiaTextRange*>(textRangeProvider);
int32_t returnVal; int32_t returnVal;
THROW_IF_FAILED(_uiaProvider->CompareEndpoints(static_cast<UIA::TextPatternRangeEndpoint>(endpoint), RETURN_IF_FAILED(_uiaProvider->CompareEndpoints(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
self->_uiaProvider.get(), self->_uiaProvider.get(),
static_cast<UIA::TextPatternRangeEndpoint>(targetEndpoint), static_cast<UIA::TextPatternRangeEndpoint>(targetEndpoint),
&returnVal)); &returnVal));
return returnVal; *result = returnVal;
return S_OK;
} }
CATCH_RETURN();
void XamlUiaTextRange::ExpandToEnclosingUnit(XamlAutomation::TextUnit unit) const IFACEMETHODIMP XamlUiaTextRange::ExpandToEnclosingUnit(ABI::Windows::UI::Xaml::Automation::Text::TextUnit unit) noexcept
try
{ {
THROW_IF_FAILED(_uiaProvider->ExpandToEnclosingUnit(static_cast<UIA::TextUnit>(unit))); RETURN_IF_FAILED(_uiaProvider->ExpandToEnclosingUnit(static_cast<UIA::TextUnit>(unit)));
return S_OK;
} }
CATCH_RETURN();
XamlAutomation::ITextRangeProvider XamlUiaTextRange::FindAttribute(int32_t /*textAttributeId*/, IFACEMETHODIMP XamlUiaTextRange::FindAttribute(INT32 /*attributeId*/,
winrt::Windows::Foundation::IInspectable /*val*/, IInspectable* /*value*/,
bool /*searchBackward*/) boolean /*backward*/,
ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider** result) noexcept
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = nullptr;
// TODO GitHub #2161: potential accessibility improvement // TODO GitHub #2161: potential accessibility improvement
// we don't support this currently // we don't support this currently
throw winrt::hresult_not_implemented(); return E_NOTIMPL;
} }
XamlAutomation::ITextRangeProvider XamlUiaTextRange::FindText(winrt::hstring text, IFACEMETHODIMP XamlUiaTextRange::FindText(HSTRING text,
bool searchBackward, boolean searchBackward,
bool ignoreCase) boolean ignoreCase,
ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider** result) noexcept
try
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = nullptr;
UIA::ITextRangeProvider* pReturn; UIA::ITextRangeProvider* pReturn;
const auto queryText = wil::make_bstr(text.c_str()); uint32_t length;
const auto textPtr = WindowsGetStringRawBuffer(text, &length);
const auto queryText = wil::unique_bstr{ SysAllocStringLen(textPtr, length) };
THROW_IF_FAILED(_uiaProvider->FindText(queryText.get(), searchBackward, ignoreCase, &pReturn)); RETURN_IF_FAILED(_uiaProvider->FindText(queryText.get(), searchBackward, ignoreCase, &pReturn));
return winrt::make<XamlUiaTextRange>(pReturn, _parentProvider); if (pReturn)
{
auto xamlRange = new (std::nothrow) XamlUiaTextRange(pReturn, _parentProvider);
RETURN_IF_NULL_ALLOC(xamlRange);
*result = xamlRange;
}
return S_OK;
} }
CATCH_RETURN();
winrt::Windows::Foundation::IInspectable XamlUiaTextRange::GetAttributeValue(int32_t textAttributeId) const IFACEMETHODIMP XamlUiaTextRange::GetAttributeValue(INT32 attributeId, IInspectable** result) noexcept
try
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = nullptr;
// Call the function off of the underlying UiaTextRange. // Call the function off of the underlying UiaTextRange.
wil::unique_variant result; wil::unique_variant varResult;
THROW_IF_FAILED(_uiaProvider->GetAttributeValue(textAttributeId, result.addressof())); RETURN_IF_FAILED(_uiaProvider->GetAttributeValue(attributeId, varResult.addressof()));
// Convert the resulting VARIANT into a format that is consumable by XAML. // Convert the resulting VARIANT into a format that is consumable by XAML.
switch (result.vt) switch (varResult.vt)
{ {
case VT_BSTR: case VT_BSTR:
{ {
return box_value(result.bstrVal); auto value = winrt::box_value(varResult.bstrVal);
*result = static_cast<IInspectable*>(winrt::detach_abi(value));
return S_OK;
} }
case VT_I4: case VT_I4:
{ {
// Surprisingly, `long` is _not_ a WinRT type. auto value = winrt::box_value<int32_t>(varResult.lVal);
// So we have to use `int32_t` to make sure this is output properly. *result = static_cast<IInspectable*>(winrt::detach_abi(value));
// Otherwise, you'll get "Attribute does not exist" out the other end. return S_OK;
return box_value<int32_t>(result.lVal);
} }
case VT_R8: case VT_R8:
{ {
return box_value(result.dblVal); auto value = winrt::box_value(varResult.dblVal);
*result = static_cast<IInspectable*>(winrt::detach_abi(value));
return S_OK;
} }
case VT_BOOL: case VT_BOOL:
{ {
return box_value<bool>(result.boolVal); auto value = winrt::box_value<bool>(varResult.boolVal);
*result = static_cast<IInspectable*>(winrt::detach_abi(value));
return S_OK;
} }
case VT_UNKNOWN: case VT_UNKNOWN:
{ {
// This one is particularly special.
// We might return a special value like UiaGetReservedMixedAttributeValue
// or UiaGetReservedNotSupportedValue.
// Some text attributes may return a real value, however, none of those
// are supported at this time.
// So we need to figure out what was actually intended to be returned.
com_ptr<IUnknown> mixedAttributeVal; com_ptr<IUnknown> mixedAttributeVal;
UiaGetReservedMixedAttributeValue(mixedAttributeVal.put()); UiaGetReservedMixedAttributeValue(mixedAttributeVal.put());
if (result.punkVal == mixedAttributeVal.get()) if (varResult.punkVal == mixedAttributeVal.get())
{ {
return Windows::UI::Xaml::DependencyProperty::UnsetValue(); auto value = Windows::UI::Xaml::DependencyProperty::UnsetValue();
*result = static_cast<IInspectable*>(winrt::detach_abi(value));
return S_OK;
} }
[[fallthrough]]; [[fallthrough]];
} }
default: default:
{
// We _need_ to return XAML_E_NOT_SUPPORTED here. // We _need_ to return XAML_E_NOT_SUPPORTED here.
// Returning nullptr is an improper implementation of it being unsupported. return XAML_E_NOT_SUPPORTED;
// UIA Clients rely on this HRESULT to signify that the requested attribute is undefined.
// Anything else will result in the UIA Client refusing to read when navigating by word
// Magically, this doesn't affect other forms of navigation...
winrt::throw_hresult(XAML_E_NOT_SUPPORTED);
}
} }
} }
CATCH_RETURN();
void XamlUiaTextRange::GetBoundingRectangles(com_array<double>& returnValue) const IFACEMETHODIMP XamlUiaTextRange::GetBoundingRectangles(UINT32* returnValueLength, DOUBLE** returnValue) noexcept
try
{ {
returnValue = {}; RETURN_HR_IF_NULL(E_INVALIDARG, returnValueLength);
try RETURN_HR_IF_NULL(E_INVALIDARG, returnValue);
*returnValueLength = 0;
*returnValue = nullptr;
SAFEARRAY* pReturnVal;
RETURN_IF_FAILED(_uiaProvider->GetBoundingRectangles(&pReturnVal));
double* pVals;
RETURN_IF_FAILED(SafeArrayAccessData(pReturnVal, (void**)&pVals));
long lBound, uBound;
RETURN_IF_FAILED(SafeArrayGetLBound(pReturnVal, 1, &lBound));
RETURN_IF_FAILED(SafeArrayGetUBound(pReturnVal, 1, &uBound));
auto count = uBound - lBound + 1;
auto result = static_cast<DOUBLE*>(CoTaskMemAlloc(sizeof(DOUBLE) * count));
if (!result)
{ {
SAFEARRAY* pReturnVal; SafeArrayUnaccessData(pReturnVal);
THROW_IF_FAILED(_uiaProvider->GetBoundingRectangles(&pReturnVal)); SafeArrayDestroy(pReturnVal);
return E_OUTOFMEMORY;
double* pVals;
THROW_IF_FAILED(SafeArrayAccessData(pReturnVal, (void**)&pVals));
long lBound, uBound;
THROW_IF_FAILED(SafeArrayGetLBound(pReturnVal, 1, &lBound));
THROW_IF_FAILED(SafeArrayGetUBound(pReturnVal, 1, &uBound));
auto count = uBound - lBound + 1;
std::vector<double> vec;
vec.reserve(count);
for (auto i = 0; i < count; i++)
{
auto element = pVals[i];
vec.push_back(element);
}
winrt::com_array<double> result{ vec };
returnValue = std::move(result);
THROW_IF_FAILED(SafeArrayUnaccessData(pReturnVal));
THROW_IF_FAILED(SafeArrayDestroy(pReturnVal));
} }
CATCH_LOG();
}
XamlAutomation::IRawElementProviderSimple XamlUiaTextRange::GetEnclosingElement() for (auto i = 0; i < count; i++)
{
result[i] = pVals[i];
}
*returnValueLength = count;
*returnValue = result;
RETURN_IF_FAILED(SafeArrayUnaccessData(pReturnVal));
RETURN_IF_FAILED(SafeArrayDestroy(pReturnVal));
return S_OK;
}
CATCH_RETURN();
IFACEMETHODIMP XamlUiaTextRange::GetEnclosingElement(ABI::Windows::UI::Xaml::Automation::Provider::IIRawElementProviderSimple** result) noexcept
try
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = nullptr;
::Microsoft::Console::Types::UiaTracing::TextRange::GetEnclosingElement(*static_cast<::Microsoft::Console::Types::UiaTextRangeBase*>(_uiaProvider.get())); ::Microsoft::Console::Types::UiaTracing::TextRange::GetEnclosingElement(*static_cast<::Microsoft::Console::Types::UiaTextRangeBase*>(_uiaProvider.get()));
return _parentProvider; winrt::copy_to_abi(_parentProvider, (void*&)*result);
return S_OK;
} }
CATCH_RETURN();
winrt::hstring XamlUiaTextRange::GetText(int32_t maxLength) const IFACEMETHODIMP XamlUiaTextRange::GetText(INT32 maxLength, HSTRING* result) noexcept
try
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = nullptr;
wil::unique_bstr returnVal; wil::unique_bstr returnVal;
THROW_IF_FAILED(_uiaProvider->GetText(maxLength, returnVal.put())); RETURN_IF_FAILED(_uiaProvider->GetText(maxLength, returnVal.put()));
return winrt::hstring{ returnVal.get(), SysStringLen(returnVal.get()) };
}
int32_t XamlUiaTextRange::Move(XamlAutomation::TextUnit unit, RETURN_IF_FAILED(WindowsCreateString(returnVal.get(), SysStringLen(returnVal.get()), result));
int32_t count) return S_OK;
}
CATCH_RETURN();
IFACEMETHODIMP XamlUiaTextRange::Move(ABI::Windows::UI::Xaml::Automation::Text::TextUnit unit,
INT32 count,
INT32* result) noexcept
try
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = 0;
int returnVal; int returnVal;
THROW_IF_FAILED(_uiaProvider->Move(static_cast<UIA::TextUnit>(unit), RETURN_IF_FAILED(_uiaProvider->Move(static_cast<UIA::TextUnit>(unit),
count, count,
&returnVal)); &returnVal));
return returnVal; *result = returnVal;
return S_OK;
} }
CATCH_RETURN();
int32_t XamlUiaTextRange::MoveEndpointByUnit(XamlAutomation::TextPatternRangeEndpoint endpoint, IFACEMETHODIMP XamlUiaTextRange::MoveEndpointByUnit(ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint,
XamlAutomation::TextUnit unit, ABI::Windows::UI::Xaml::Automation::Text::TextUnit unit,
int32_t count) const INT32 count,
INT32* result) noexcept
try
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, result);
*result = 0;
int returnVal; int returnVal;
THROW_IF_FAILED(_uiaProvider->MoveEndpointByUnit(static_cast<UIA::TextPatternRangeEndpoint>(endpoint), RETURN_IF_FAILED(_uiaProvider->MoveEndpointByUnit(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
static_cast<UIA::TextUnit>(unit), static_cast<UIA::TextUnit>(unit),
count, count,
&returnVal)); &returnVal));
return returnVal; *result = returnVal;
return S_OK;
} }
CATCH_RETURN();
void XamlUiaTextRange::MoveEndpointByRange(XamlAutomation::TextPatternRangeEndpoint endpoint, IFACEMETHODIMP XamlUiaTextRange::MoveEndpointByRange(ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint,
XamlAutomation::ITextRangeProvider pTargetRange, ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider* textRangeProvider,
XamlAutomation::TextPatternRangeEndpoint targetEndpoint) const ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint) noexcept
try
{ {
auto self = winrt::get_self<XamlUiaTextRange>(pTargetRange); RETURN_HR_IF_NULL(E_INVALIDARG, textRangeProvider);
THROW_IF_FAILED(_uiaProvider->MoveEndpointByRange(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
/*pTargetRange*/ self->_uiaProvider.get(),
static_cast<UIA::TextPatternRangeEndpoint>(targetEndpoint)));
}
void XamlUiaTextRange::Select() const auto self = static_cast<XamlUiaTextRange*>(textRangeProvider);
RETURN_IF_FAILED(_uiaProvider->MoveEndpointByRange(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
self->_uiaProvider.get(),
static_cast<UIA::TextPatternRangeEndpoint>(targetEndpoint)));
return S_OK;
}
CATCH_RETURN();
IFACEMETHODIMP XamlUiaTextRange::Select() noexcept
try
{ {
THROW_IF_FAILED(_uiaProvider->Select()); RETURN_IF_FAILED(_uiaProvider->Select());
return S_OK;
} }
CATCH_RETURN();
void XamlUiaTextRange::AddToSelection() const IFACEMETHODIMP XamlUiaTextRange::AddToSelection() noexcept
{ {
// we don't support this // we don't support this
throw winrt::hresult_not_implemented(); return E_NOTIMPL;
} }
void XamlUiaTextRange::RemoveFromSelection() const IFACEMETHODIMP XamlUiaTextRange::RemoveFromSelection() noexcept
{ {
// we don't support this // we don't support this
throw winrt::hresult_not_implemented(); return E_NOTIMPL;
} }
void XamlUiaTextRange::ScrollIntoView(bool alignToTop) const IFACEMETHODIMP XamlUiaTextRange::ScrollIntoView(boolean alignToTop) noexcept
try
{ {
THROW_IF_FAILED(_uiaProvider->ScrollIntoView(alignToTop)); RETURN_IF_FAILED(_uiaProvider->ScrollIntoView(alignToTop));
return S_OK;
} }
CATCH_RETURN();
winrt::com_array<XamlAutomation::IRawElementProviderSimple> XamlUiaTextRange::GetChildren() const IFACEMETHODIMP XamlUiaTextRange::GetChildren(UINT32* resultLength,
ABI::Windows::UI::Xaml::Automation::Provider::IIRawElementProviderSimple*** result) noexcept
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, resultLength);
RETURN_HR_IF_NULL(E_INVALIDARG, result);
// we don't have any children // we don't have any children
return {}; *resultLength = 0;
*result = nullptr;
return S_OK;
} }
} }

View File

@ -23,50 +23,68 @@ Author(s):
#include "TermControlAutomationPeer.h" #include "TermControlAutomationPeer.h"
#include <UIAutomationCore.h> #include <UIAutomationCore.h>
#include "../types/TermControlUiaTextRange.hpp" #include "../types/TermControlUiaTextRange.hpp"
#include <windows.ui.xaml.automation.provider.h>
namespace winrt::Microsoft::Terminal::Control::implementation namespace winrt::Microsoft::Terminal::Control::implementation
{ {
class XamlUiaTextRange : class XamlUiaTextRange : public ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider
public winrt::implements<XamlUiaTextRange, Windows::UI::Xaml::Automation::Provider::ITextRangeProvider>
{ {
public: public:
static winrt::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider Create(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider);
XamlUiaTextRange(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider); XamlUiaTextRange(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider);
~XamlUiaTextRange(); ~XamlUiaTextRange();
// IUnknown
IFACEMETHODIMP_(ULONG) AddRef() noexcept override;
IFACEMETHODIMP_(ULONG) Release() noexcept override;
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppvObject) noexcept override;
// IInspectable
IFACEMETHODIMP GetIids(ULONG* iidCount, IID** iids) noexcept override;
IFACEMETHODIMP GetRuntimeClassName(HSTRING* className) noexcept override;
IFACEMETHODIMP GetTrustLevel(TrustLevel* trustLevel) noexcept override;
#pragma region ITextRangeProvider #pragma region ITextRangeProvider
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider Clone() const; IFACEMETHODIMP Clone(ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider** result) noexcept override;
bool Compare(Windows::UI::Xaml::Automation::Provider::ITextRangeProvider pRange) const; IFACEMETHODIMP Compare(ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider* textRangeProvider, boolean* result) noexcept override;
int32_t CompareEndpoints(Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint, IFACEMETHODIMP CompareEndpoints(ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint,
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider pTargetRange, ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider* textRangeProvider,
Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint); ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint,
void ExpandToEnclosingUnit(Windows::UI::Xaml::Automation::Text::TextUnit unit) const; INT32* result) noexcept override;
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider FindAttribute(int32_t textAttributeId, IFACEMETHODIMP ExpandToEnclosingUnit(ABI::Windows::UI::Xaml::Automation::Text::TextUnit unit) noexcept override;
winrt::Windows::Foundation::IInspectable val, IFACEMETHODIMP FindAttribute(INT32 attributeId,
bool searchBackward); IInspectable* value,
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider FindText(winrt::hstring text, boolean backward,
bool searchBackward, ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider** result) noexcept override;
bool ignoreCase); IFACEMETHODIMP FindText(HSTRING text,
winrt::Windows::Foundation::IInspectable GetAttributeValue(int32_t textAttributeId) const; boolean backward,
void GetBoundingRectangles(winrt::com_array<double>& returnValue) const; boolean ignoreCase,
Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple GetEnclosingElement(); ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider** result) noexcept override;
winrt::hstring GetText(int32_t maxLength) const; IFACEMETHODIMP GetAttributeValue(INT32 attributeId, IInspectable** result) noexcept override;
int32_t Move(Windows::UI::Xaml::Automation::Text::TextUnit unit, IFACEMETHODIMP GetBoundingRectangles(UINT32* returnValueLength, DOUBLE** returnValue) noexcept override;
int32_t count); IFACEMETHODIMP GetEnclosingElement(ABI::Windows::UI::Xaml::Automation::Provider::IIRawElementProviderSimple** result) noexcept override;
int32_t MoveEndpointByUnit(Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint, IFACEMETHODIMP GetText(INT32 maxLength, HSTRING* result) noexcept override;
Windows::UI::Xaml::Automation::Text::TextUnit unit, IFACEMETHODIMP Move(ABI::Windows::UI::Xaml::Automation::Text::TextUnit unit,
int32_t count) const; INT32 count,
void MoveEndpointByRange(Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint, INT32* result) noexcept override;
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider pTargetRange, IFACEMETHODIMP MoveEndpointByUnit(ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint,
Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint) const; ABI::Windows::UI::Xaml::Automation::Text::TextUnit unit,
void Select() const; INT32 count,
void AddToSelection() const; INT32* result) noexcept override;
void RemoveFromSelection() const; IFACEMETHODIMP MoveEndpointByRange(ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint,
void ScrollIntoView(bool alignToTop) const; ABI::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider* textRangeProvider,
winrt::com_array<Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple> GetChildren() const; ABI::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint) noexcept override;
IFACEMETHODIMP Select() noexcept override;
IFACEMETHODIMP AddToSelection() noexcept override;
IFACEMETHODIMP RemoveFromSelection() noexcept override;
IFACEMETHODIMP ScrollIntoView(boolean alignToTop) noexcept override;
IFACEMETHODIMP GetChildren(UINT32* resultLength,
ABI::Windows::UI::Xaml::Automation::Provider::IIRawElementProviderSimple*** result) noexcept override;
#pragma endregion ITextRangeProvider #pragma endregion ITextRangeProvider
private:
wil::com_ptr<::ITextRangeProvider> _uiaProvider; wil::com_ptr<::ITextRangeProvider> _uiaProvider;
Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider; Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider;
std::atomic<ULONG> _refCount{ 1 };
}; };
} }