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

Properly initialize XamlUiaTextRange with ProviderFromPeer #11501

Merged
4 commits merged into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
40 changes: 25 additions & 15 deletions src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_controlPadding = padding;
}
void InteractivityAutomationPeer::RegisterParentProvider(Control::TermControlAutomationPeer parentProvider)
{
_parentProvider = parentProvider;
}

// Method Description:
// - Signals the ui automation client that the terminal's selection has
Expand Down Expand Up @@ -110,30 +114,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr
THROW_IF_FAILED(_uiaProvider->RangeFromChild(/* IRawElementProviderSimple */ nullptr,
&returnVal));

const auto parentProvider = this->ProviderFromPeer(*this);
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
return _CreateXamlUiaTextRange(returnVal);
}

XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation)
{
UIA::ITextRangeProvider* returnVal;
THROW_IF_FAILED(_uiaProvider->RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal));

const auto parentProvider = this->ProviderFromPeer(*this);
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
return _CreateXamlUiaTextRange(returnVal);
}

XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::DocumentRange()
{
UIA::ITextRangeProvider* returnVal;
THROW_IF_FAILED(_uiaProvider->get_DocumentRange(&returnVal));

const auto parentProvider = this->ProviderFromPeer(*this);
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
return _CreateXamlUiaTextRange(returnVal);
}

XamlAutomation::SupportedTextSelection InteractivityAutomationPeer::SupportedTextSelection()
Expand Down Expand Up @@ -180,6 +175,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
#pragma endregion

XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::_CreateXamlUiaTextRange(UIA::ITextRangeProvider* returnVal) const
{
// LOAD-BEARING: use _parentProvider->ProviderFromPeer(_parentProvider) instead of this->ProviderFromPeer(*this).
// Since we split the automation peer into TermControlAutomationPeer and InteractivityAutomationPeer,
// the using "this" returns null. This can cause issues with some UIA Client scenarios like word navigation in Narrator.
const auto parent{ _parentProvider.get() };
if (!parent)
{
return nullptr;
}
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parent.ProviderFromPeer(parent));
return xutr.as<XamlAutomation::ITextRangeProvider>();
};

// Method Description:
// - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders
// Arguments:
Expand All @@ -194,11 +203,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation

std::vector<XamlAutomation::ITextRangeProvider> vec;
vec.reserve(count);
auto parentProvider = this->ProviderFromPeer(*this);
for (int i = 0; i < count; i++)
{
auto xutr = make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
vec.emplace_back(xutr.as<XamlAutomation::ITextRangeProvider>());
if (auto xutr = _CreateXamlUiaTextRange(providers[i].detach()))
{
vec.emplace_back(std::move(xutr));
}
}

com_array<XamlAutomation::ITextRangeProvider> result{ vec };
Expand Down
4 changes: 4 additions & 0 deletions src/cascadia/TerminalControl/InteractivityAutomationPeer.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation

void SetControlBounds(const Windows::Foundation::Rect bounds);
void SetControlPadding(const Core::Padding padding);
void RegisterParentProvider(Control::TermControlAutomationPeer parentProvider);

#pragma region IUiaEventDispatcher
void SignalSelectionChanged() override;
Expand Down Expand Up @@ -74,8 +75,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TYPED_EVENT(CursorChanged, IInspectable, IInspectable);

private:
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider _CreateXamlUiaTextRange(::ITextRangeProvider* returnVal) const;

::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
weak_ref<Control::TermControlAutomationPeer> _parentProvider;

til::rectangle _controlBounds{};
til::rectangle _controlPadding{};
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalControl/InteractivityAutomationPeer.idl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import "TermControlAutomationPeer.idl";

namespace Microsoft.Terminal.Control
{
[default_interface] runtimeclass InteractivityAutomationPeer :
Expand All @@ -10,6 +12,7 @@ namespace Microsoft.Terminal.Control

void SetControlBounds(Windows.Foundation.Rect bounds);
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
void RegisterParentProvider(TermControlAutomationPeer parentProvider);
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved

event Windows.Foundation.TypedEventHandler<Object, Object> SelectionChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> TextChanged;
Expand Down
14 changes: 9 additions & 5 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,10 +522,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// MSFT 33353327: We're purposefully not using _initializedTerminal to ensure we're fully initialized.
// Doing so makes us return nullptr when XAML requests an automation peer.
// Instead, we need to give XAML an automation peer, then fix it later.
if (!_IsClosing())
if (_IsClosing())
{
return nullptr;
}
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved

// create a custom automation peer with this code pattern:
// (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
if (!_automationPeer)
{
// create a custom automation peer with this code pattern:
// (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
if (const auto& interactivityAutoPeer{ _interactivity.OnCreateAutomationPeer() })
{
const auto margins{ SwapChainPanel().Margin() };
Expand All @@ -534,10 +539,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
margins.Right,
margins.Bottom };
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, padding, interactivityAutoPeer);
return _automationPeer;
}
}
return nullptr;
return _automationPeer;
}

// This is needed for TermControlAutomationPeer. We probably could find a
Expand Down
27 changes: 1 addition & 26 deletions src/cascadia/TerminalControl/TermControlAutomationPeer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_contentAutomationPeer.SelectionChanged([this](auto&&, auto&&) { SignalSelectionChanged(); });
_contentAutomationPeer.TextChanged([this](auto&&, auto&&) { SignalTextChanged(); });
_contentAutomationPeer.CursorChanged([this](auto&&, auto&&) { SignalCursorChanged(); });
_contentAutomationPeer.RegisterParentProvider(*this);
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
};

// Method Description:
Expand Down Expand Up @@ -226,30 +227,4 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}

#pragma endregion

// 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)
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
{
// transfer ownership of UiaTextRanges to this new vector
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(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 = make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
vec.emplace_back(xutr.as<XamlAutomation::ITextRangeProvider>());
}

com_array<XamlAutomation::ITextRangeProvider> result{ vec };

return result;
}
}
2 changes: 0 additions & 2 deletions src/cascadia/TerminalControl/TermControlAutomationPeer.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,5 @@ namespace winrt::Microsoft::Terminal::Control::implementation
private:
winrt::Microsoft::Terminal::Control::implementation::TermControl* _termControl;
Control::InteractivityAutomationPeer _contentAutomationPeer;

winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges);
};
}
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/XamlUiaTextRange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "../types/TermControlUiaTextRange.hpp"
#include <UIAutomationClient.h>
#include <UIAutomationCoreApi.h>
#include "../types/UiaTracing.h"

// the same as COR_E_NOTSUPPORTED
// we don't want to import the CLR headers to get it
Expand Down Expand Up @@ -182,6 +183,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation

XamlAutomation::IRawElementProviderSimple XamlUiaTextRange::GetEnclosingElement()
{
::Microsoft::Console::Types::UiaTracing::TextRange::GetEnclosingElement(*static_cast<::Microsoft::Console::Types::UiaTextRangeBase*>(_uiaProvider.get()));
return _parentProvider;
}

Expand Down