hook up UIA tree to WPF control (#4548)

This PR hooks up the existing UIA implementation to the WPF control. Some existing code that was specific to the UWP terminal control could be shared so that has been refactored to a common location as well.

## Validation Steps Performed
WPF control was brought up in UISpy and the UIA tree was verified. NVDA was then used to check that screen readers were operating properly.
This commit is contained in:
Zoey Riordan 2020-02-24 15:17:55 -08:00 committed by GitHub
parent b8e33560f9
commit 4def49c45e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 419 additions and 139 deletions

View file

@ -3,6 +3,7 @@
#include "pch.h" #include "pch.h"
#include "HwndTerminal.hpp" #include "HwndTerminal.hpp"
#include "../../types/TermControlUiaProvider.hpp"
#include <DefaultSettings.h> #include <DefaultSettings.h>
#include "../../renderer/base/Renderer.hpp" #include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp" #include "../../renderer/dx/DxRenderer.hpp"
@ -14,13 +15,27 @@ using namespace ::Microsoft::Terminal::Core;
static LPCWSTR term_window_class = L"HwndTerminalClass"; static LPCWSTR term_window_class = L"HwndTerminalClass";
static LRESULT CALLBACK HwndTerminalWndProc( LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
HWND hwnd, HWND hwnd,
UINT uMsg, UINT uMsg,
WPARAM wParam, WPARAM wParam,
LPARAM lParam) noexcept LPARAM lParam) noexcept
{ {
return DefWindowProcW(hwnd, uMsg, wParam, lParam); #pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
HwndTerminal* terminal = reinterpret_cast<HwndTerminal*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (terminal)
{
switch (uMsg)
{
case WM_GETOBJECT:
if (lParam == UiaRootObjectId)
{
return UiaReturnRawElementProvider(hwnd, wParam, lParam, terminal->_GetUiaProvider());
}
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
} }
static bool RegisterTermClass(HINSTANCE hInstance) noexcept static bool RegisterTermClass(HINSTANCE hInstance) noexcept
@ -32,7 +47,7 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
} }
wc.style = 0; wc.style = 0;
wc.lpfnWndProc = HwndTerminalWndProc; wc.lpfnWndProc = HwndTerminal::HwndTerminalWndProc;
wc.cbClsExtra = 0; wc.cbClsExtra = 0;
wc.cbWndExtra = 0; wc.cbWndExtra = 0;
wc.hInstance = hInstance; wc.hInstance = hInstance;
@ -47,7 +62,10 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
HwndTerminal::HwndTerminal(HWND parentHwnd) : HwndTerminal::HwndTerminal(HWND parentHwnd) :
_desiredFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8 }, _desiredFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8 },
_actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8, false } _actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8, false },
_uiaProvider{ nullptr },
_uiaProviderInitialized{ false },
_currentDpi{ USER_DEFAULT_SCREEN_DPI }
{ {
HINSTANCE hInstance = wil::GetModuleInstanceHandle(); HINSTANCE hInstance = wil::GetModuleInstanceHandle();
@ -69,6 +87,9 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
nullptr, nullptr,
hInstance, hInstance,
nullptr)); nullptr));
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
SetWindowLongPtr(_hwnd.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
} }
} }
@ -141,8 +162,19 @@ void HwndTerminal::RegisterWriteCallback(const void _stdcall callback(wchar_t*))
}); });
} }
::Microsoft::Console::Types::IUiaData* HwndTerminal::GetUiaData() const noexcept
{
return _terminal.get();
}
HWND HwndTerminal::GetHwnd() const noexcept
{
return _hwnd.get();
}
void HwndTerminal::_UpdateFont(int newDpi) void HwndTerminal::_UpdateFont(int newDpi)
{ {
_currentDpi = newDpi;
auto lock = _terminal->LockForWriting(); auto lock = _terminal->LockForWriting();
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't // TODO: MSFT:20895307 If the font doesn't exist, this doesn't
@ -150,6 +182,33 @@ void HwndTerminal::_UpdateFont(int newDpi)
_renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont); _renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont);
} }
IRawElementProviderSimple* HwndTerminal::_GetUiaProvider() noexcept
{
if (nullptr == _uiaProvider && !_uiaProviderInitialized)
{
std::unique_lock<std::shared_mutex> lock;
try
{
#pragma warning(suppress : 26441) // The lock is named, this appears to be a false positive
lock = _terminal->LockForWriting();
if (_uiaProviderInitialized)
{
return _uiaProvider.Get();
}
LOG_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, this->GetUiaData(), this));
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
_uiaProvider = nullptr;
}
_uiaProviderInitialized = true;
}
return _uiaProvider.Get();
}
HRESULT HwndTerminal::Refresh(const SIZE windowSize, _Out_ COORD* dimensions) HRESULT HwndTerminal::Refresh(const SIZE windowSize, _Out_ COORD* dimensions)
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, dimensions); RETURN_HR_IF_NULL(E_INVALIDARG, dimensions);
@ -186,10 +245,29 @@ void HwndTerminal::SendOutput(std::wstring_view data)
HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal) HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal)
{ {
auto _terminal = std::make_unique<HwndTerminal>(parentHwnd); // In order for UIA to hook up properly there needs to be a "static" window hosting the
// inner win32 control. If the static window is not present then WM_GETOBJECT messages
// will not reach the child control, and the uia element will not be present in the tree.
auto _hostWindow = CreateWindowEx(
0,
L"static",
nullptr,
WS_CHILD |
WS_CLIPCHILDREN |
WS_CLIPSIBLINGS |
WS_VISIBLE,
0,
0,
0,
0,
parentHwnd,
nullptr,
nullptr,
0);
auto _terminal = std::make_unique<HwndTerminal>(_hostWindow);
RETURN_IF_FAILED(_terminal->Initialize()); RETURN_IF_FAILED(_terminal->Initialize());
*hwnd = _terminal->_hwnd.get(); *hwnd = _hostWindow;
*terminal = _terminal.release(); *terminal = _terminal.release();
return S_OK; return S_OK;
@ -217,6 +295,15 @@ HRESULT _stdcall TerminalTriggerResize(void* terminal, double width, double heig
{ {
const auto publicTerminal = static_cast<HwndTerminal*>(terminal); const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
LOG_IF_WIN32_BOOL_FALSE(SetWindowPos(
publicTerminal->GetHwnd(),
nullptr,
0,
0,
static_cast<int>(width),
static_cast<int>(height),
0));
const SIZE windowSize{ static_cast<short>(width), static_cast<short>(height) }; const SIZE windowSize{ static_cast<short>(width), static_cast<short>(height) };
return publicTerminal->Refresh(windowSize, dimensions); return publicTerminal->Refresh(windowSize, dimensions);
} }
@ -413,3 +500,35 @@ void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible)
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal); const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
publicTerminal->_terminal->SetCursorVisible(visible); publicTerminal->_terminal->SetCursorVisible(visible);
} }
COORD HwndTerminal::GetFontSize() const
{
return _actualFont.GetSize();
}
RECT HwndTerminal::GetBounds() const noexcept
{
RECT windowRect;
GetWindowRect(_hwnd.get(), &windowRect);
return windowRect;
}
RECT HwndTerminal::GetPadding() const noexcept
{
return { 0 };
}
double HwndTerminal::GetScaleFactor() const noexcept
{
return static_cast<double>(_currentDpi) / static_cast<double>(USER_DEFAULT_SCREEN_DPI);
}
void HwndTerminal::ChangeViewport(const SMALL_RECT NewWindow)
{
_terminal->UserScrollViewport(NewWindow.Top);
}
HRESULT HwndTerminal::GetHostUiaProvider(IRawElementProviderSimple** provider) noexcept
{
return UiaHostProviderFromHwnd(_hwnd.get(), provider);
}

View file

@ -6,6 +6,9 @@
#include "../../renderer/base/Renderer.hpp" #include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp" #include "../../renderer/dx/DxRenderer.hpp"
#include "../../cascadia/TerminalCore/Terminal.hpp" #include "../../cascadia/TerminalCore/Terminal.hpp"
#include <UIAutomationCore.h>
#include "../../types/IControlAccessibilityInfo.h"
#include "../../types/TermControlUiaProvider.hpp"
using namespace Microsoft::Console::VirtualTerminal; using namespace Microsoft::Console::VirtualTerminal;
@ -39,20 +42,35 @@ __declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible); __declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
}; };
struct HwndTerminal struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
{ {
public: public:
HwndTerminal(HWND hwnd); HwndTerminal(HWND hwnd);
HwndTerminal(const HwndTerminal&) = default;
HwndTerminal(HwndTerminal&&) = default;
HwndTerminal& operator=(const HwndTerminal&) = default;
HwndTerminal& operator=(HwndTerminal&&) = default;
~HwndTerminal() = default;
HRESULT Initialize(); HRESULT Initialize();
void SendOutput(std::wstring_view data); void SendOutput(std::wstring_view data);
HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions); HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions);
void RegisterScrollCallback(std::function<void(int, int, int)> callback); void RegisterScrollCallback(std::function<void(int, int, int)> callback);
void RegisterWriteCallback(const void _stdcall callback(wchar_t*)); void RegisterWriteCallback(const void _stdcall callback(wchar_t*));
::Microsoft::Console::Types::IUiaData* GetUiaData() const noexcept;
HWND GetHwnd() const noexcept;
static LRESULT CALLBACK HwndTerminalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
private: private:
wil::unique_hwnd _hwnd; wil::unique_hwnd _hwnd;
FontInfoDesired _desiredFont; FontInfoDesired _desiredFont;
FontInfo _actualFont; FontInfo _actualFont;
int _currentDpi;
bool _uiaProviderInitialized;
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal; std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
@ -74,4 +92,13 @@ private:
friend void _stdcall TerminalBlinkCursor(void* terminal); friend void _stdcall TerminalBlinkCursor(void* terminal);
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible); friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
void _UpdateFont(int newDpi); void _UpdateFont(int newDpi);
IRawElementProviderSimple* _GetUiaProvider() noexcept;
// Inherited via IControlAccessibilityInfo
COORD GetFontSize() const override;
RECT GetBounds() const noexcept override;
double GetScaleFactor() const noexcept override;
void ChangeViewport(const SMALL_RECT NewWindow) override;
HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) noexcept override;
RECT GetPadding() const noexcept override;
}; };

View file

@ -1,4 +1,8 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // If this is not defined, windows.h includes commdlg.h which defines FindText globally and conflicts with UIAutomation ITextRangeProvider.
#endif
#include <LibraryIncludes.h> #include <LibraryIncludes.h>

View file

@ -11,6 +11,7 @@
using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types;
using namespace winrt::Windows::UI::Xaml::Automation::Peers; using namespace winrt::Windows::UI::Xaml::Automation::Peers;
using namespace winrt::Windows::Graphics::Display;
namespace UIA namespace UIA
{ {
@ -28,9 +29,10 @@ namespace XamlAutomation
namespace winrt::Microsoft::Terminal::TerminalControl::implementation namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{ {
TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* owner) : TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* owner) :
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner) // pass owner to FrameworkElementAutomationPeer TermControlAutomationPeerT<TermControlAutomationPeer>(*owner), // pass owner to FrameworkElementAutomationPeer
_termControl{ owner }
{ {
THROW_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, owner, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this))); THROW_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, _termControl->GetUiaData(), this));
}; };
// Method Description: // Method Description:
@ -159,7 +161,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
#pragma endregion #pragma endregion
RECT TermControlAutomationPeer::GetBoundingRectWrapped() #pragma region IControlAccessibilityInfo
COORD TermControlAutomationPeer::GetFontSize() const
{
return _termControl->GetActualFont().GetSize();
}
RECT TermControlAutomationPeer::GetBounds() const
{ {
auto rect = GetBoundingRectangle(); auto rect = GetBoundingRectangle();
return { return {
@ -170,6 +178,36 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}; };
} }
HRESULT TermControlAutomationPeer::GetHostUiaProvider(IRawElementProviderSimple** provider)
{
RETURN_HR_IF(E_INVALIDARG, provider == nullptr);
*provider = nullptr;
return S_OK;
}
RECT TermControlAutomationPeer::GetPadding() const
{
auto padding = _termControl->GetPadding();
return {
gsl::narrow_cast<LONG>(padding.Left),
gsl::narrow_cast<LONG>(padding.Top),
gsl::narrow_cast<LONG>(padding.Right),
gsl::narrow_cast<LONG>(padding.Bottom)
};
}
double TermControlAutomationPeer::GetScaleFactor() const
{
return DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
}
void TermControlAutomationPeer::ChangeViewport(const SMALL_RECT NewWindow)
{
_termControl->ScrollViewport(NewWindow.Top);
}
#pragma endregion
// Method Description: // Method Description:
// - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders // - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders
// Arguments: // Arguments:
@ -179,7 +217,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges) winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
{ {
// transfer ownership of UiaTextRanges to this new vector // transfer ownership of UiaTextRanges to this new vector
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::UiaTextRange>(textRanges); auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(textRanges);
int count = gsl::narrow<int>(providers.size()); int count = gsl::narrow<int>(providers.size());
std::vector<XamlAutomation::ITextRangeProvider> vec; std::vector<XamlAutomation::ITextRangeProvider> vec;

View file

@ -27,14 +27,16 @@ Author(s):
#include "TermControl.h" #include "TermControl.h"
#include "TermControlAutomationPeer.g.h" #include "TermControlAutomationPeer.g.h"
#include <winrt/Microsoft.Terminal.TerminalControl.h> #include <winrt/Microsoft.Terminal.TerminalControl.h>
#include "TermControlUiaProvider.hpp" #include "../types/TermControlUiaProvider.hpp"
#include "../types/IUiaEventDispatcher.h" #include "../types/IUiaEventDispatcher.h"
#include "../types/IControlAccessibilityInfo.h"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{ {
struct TermControlAutomationPeer : struct TermControlAutomationPeer :
public TermControlAutomationPeerT<TermControlAutomationPeer>, public TermControlAutomationPeerT<TermControlAutomationPeer>,
::Microsoft::Console::Types::IUiaEventDispatcher ::Microsoft::Console::Types::IUiaEventDispatcher,
::Microsoft::Console::Types::IControlAccessibilityInfo
{ {
public: public:
TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* owner); TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* owner);
@ -59,11 +61,21 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider DocumentRange(); Windows::UI::Xaml::Automation::Provider::ITextRangeProvider DocumentRange();
#pragma endregion #pragma endregion
#pragma region IControlAccessibilityInfo Pattern
// Inherited via IControlAccessibilityInfo
virtual COORD GetFontSize() const override;
virtual RECT GetBounds() const override;
virtual RECT GetPadding() const override;
virtual double GetScaleFactor() const override;
virtual void ChangeViewport(SMALL_RECT NewWindow) override;
virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) override;
#pragma endregion
RECT GetBoundingRectWrapped(); RECT GetBoundingRectWrapped();
private: private:
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider; ::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* _termControl;
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges); winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges);
}; };
} }

View file

@ -36,7 +36,6 @@
<ClInclude Include="SearchBoxControl.h"> <ClInclude Include="SearchBoxControl.h">
<DependentUpon>SearchBoxControl.xaml</DependentUpon> <DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClInclude> </ClInclude>
<ClInclude Include="TermControlUiaProvider.hpp" />
<ClInclude Include="TermControl.h"> <ClInclude Include="TermControl.h">
<DependentUpon>TermControl.idl</DependentUpon> <DependentUpon>TermControl.idl</DependentUpon>
</ClInclude> </ClInclude>
@ -46,7 +45,6 @@
<ClInclude Include="TSFInputControl.h"> <ClInclude Include="TSFInputControl.h">
<DependentUpon>TSFInputControl.idl</DependentUpon> <DependentUpon>TSFInputControl.idl</DependentUpon>
</ClInclude> </ClInclude>
<ClInclude Include="UiaTextRange.hpp" />
<ClInclude Include="XamlUiaTextRange.h" /> <ClInclude Include="XamlUiaTextRange.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -57,7 +55,6 @@
<ClCompile Include="SearchBoxControl.cpp"> <ClCompile Include="SearchBoxControl.cpp">
<DependentUpon>SearchBoxControl.xaml</DependentUpon> <DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClCompile> </ClCompile>
<ClCompile Include="TermControlUiaProvider.cpp" />
<ClCompile Include="TermControl.cpp"> <ClCompile Include="TermControl.cpp">
<DependentUpon>TermControl.idl</DependentUpon> <DependentUpon>TermControl.idl</DependentUpon>
</ClCompile> </ClCompile>
@ -68,7 +65,6 @@
<ClCompile Include="TermControlAutomationPeer.cpp"> <ClCompile Include="TermControlAutomationPeer.cpp">
<DependentUpon>TermControlAutomationPeer.idl</DependentUpon> <DependentUpon>TermControlAutomationPeer.idl</DependentUpon>
</ClCompile> </ClCompile>
<ClCompile Include="UiaTextRange.cpp" />
<ClCompile Include="XamlUiaTextRange.cpp" /> <ClCompile Include="XamlUiaTextRange.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -3,7 +3,7 @@
#include "pch.h" #include "pch.h"
#include "XamlUiaTextRange.h" #include "XamlUiaTextRange.h"
#include "UiaTextRange.hpp" #include "../types/TermControlUiaTextRange.hpp"
#include <UIAutomationClient.h> #include <UIAutomationClient.h>
// the same as COR_E_NOTSUPPORTED // the same as COR_E_NOTSUPPORTED

View file

@ -22,7 +22,7 @@ Author(s):
#include "TermControlAutomationPeer.h" #include "TermControlAutomationPeer.h"
#include <UIAutomationCore.h> #include <UIAutomationCore.h>
#include "UiaTextRange.hpp" #include "../types/TermControlUiaTextRange.hpp"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{ {

View file

@ -7,6 +7,7 @@ namespace Microsoft.Terminal.Wpf
{ {
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows.Automation.Provider;
#pragma warning disable SA1600 // Elements should be documented #pragma warning disable SA1600 // Elements should be documented
internal static class NativeMethods internal static class NativeMethods
@ -36,6 +37,8 @@ namespace Microsoft.Terminal.Wpf
/// </summary> /// </summary>
WM_MOUSEACTIVATE = 0x0021, WM_MOUSEACTIVATE = 0x0021,
WM_GETOBJECT = 0x003D,
/// <summary> /// <summary>
/// The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place in the Z order has changed as a result of a call to the SetWindowPos function or another window-management function. /// The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place in the Z order has changed as a result of a call to the SetWindowPos function or another window-management function.
/// </summary> /// </summary>

View file

@ -72,6 +72,8 @@ namespace Microsoft.Terminal.Wpf
/// </summary> /// </summary>
internal int Columns { get; private set; } internal int Columns { get; private set; }
internal IntPtr Hwnd => this.hwnd;
/// <summary> /// <summary>
/// Sets the connection to the terminal backend. /// Sets the connection to the terminal backend.
/// </summary> /// </summary>
@ -172,7 +174,6 @@ namespace Microsoft.Terminal.Wpf
protected override HandleRef BuildWindowCore(HandleRef hwndParent) protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{ {
var dpiScale = VisualTreeHelper.GetDpi(this); var dpiScale = VisualTreeHelper.GetDpi(this);
NativeMethods.CreateTerminal(hwndParent.Handle, out this.hwnd, out this.terminal); NativeMethods.CreateTerminal(hwndParent.Handle, out this.hwnd, out this.terminal);
this.scrollCallback = this.OnScroll; this.scrollCallback = this.OnScroll;

View file

@ -13,7 +13,7 @@ using namespace Microsoft::WRL;
using Microsoft::Console::Interactivity::ServiceLocator; using Microsoft::Console::Interactivity::ServiceLocator;
// degenerate range constructor. // degenerate range constructor.
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters) HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters) noexcept
{ {
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters); return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters);
} }
@ -22,7 +22,7 @@ HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElem
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor, const Cursor& cursor,
const std::wstring_view wordDelimiters) const std::wstring_view wordDelimiters) noexcept
{ {
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor, wordDelimiters); return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor, wordDelimiters);
} }
@ -32,7 +32,7 @@ HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
const COORD start, const COORD start,
const COORD end, const COORD end,
const std::wstring_view wordDelimiters) const std::wstring_view wordDelimiters) noexcept
{ {
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, wordDelimiters); return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, wordDelimiters);
} }

View file

@ -29,20 +29,20 @@ namespace Microsoft::Console::Interactivity::Win32
// degenerate range // degenerate range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData, HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter); _In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
// degenerate range at cursor position // degenerate range at cursor position
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData, HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor, const Cursor& cursor,
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter); _In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
// specific endpoint range // specific endpoint range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData, HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
_In_ const COORD start, _In_ const COORD start,
_In_ const COORD end, _In_ const COORD end,
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter); _In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
// range from a UiaPoint // range from a UiaPoint
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData, HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,

View file

@ -0,0 +1,43 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- IControlAccessibilityInfo.h
Abstract:
- This serves as the interface defining all information known by the control
hosting the terminal renderer that is needed for the UI Automation Tree.
Author(s):
- Zoey Riordan (zorio) Feb-2020
--*/
#pragma once
#include <wtypes.h>
namespace Microsoft::Console::Types
{
class IControlAccessibilityInfo
{
public:
virtual ~IControlAccessibilityInfo() = 0;
virtual COORD GetFontSize() const = 0;
virtual RECT GetBounds() const = 0;
virtual RECT GetPadding() const = 0;
virtual double GetScaleFactor() const = 0;
virtual void ChangeViewport(const SMALL_RECT NewWindow) = 0;
virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) = 0;
protected:
IControlAccessibilityInfo() = default;
IControlAccessibilityInfo(const IControlAccessibilityInfo&) = default;
IControlAccessibilityInfo(IControlAccessibilityInfo&&) = default;
IControlAccessibilityInfo& operator=(const IControlAccessibilityInfo&) = default;
IControlAccessibilityInfo& operator=(IControlAccessibilityInfo&&) = default;
};
inline IControlAccessibilityInfo::~IControlAccessibilityInfo() {}
}

View file

@ -38,7 +38,7 @@ namespace Microsoft::Console::Types
public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom | WRL::InhibitFtmBase>, IRawElementProviderSimple, IRawElementProviderFragment, ITextProvider> public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom | WRL::InhibitFtmBase>, IRawElementProviderSimple, IRawElementProviderFragment, ITextProvider>
{ {
public: public:
HRESULT RuntimeClassInitialize(_In_ IUiaData* pData, _In_ std::wstring_view wordDelimiters = UiaTextRangeBase::DefaultWordDelimiter) noexcept; virtual HRESULT RuntimeClassInitialize(_In_ IUiaData* pData, _In_ std::wstring_view wordDelimiters = UiaTextRangeBase::DefaultWordDelimiter) noexcept;
ScreenInfoUiaProviderBase(const ScreenInfoUiaProviderBase&) = default; ScreenInfoUiaProviderBase(const ScreenInfoUiaProviderBase&) = default;
ScreenInfoUiaProviderBase(ScreenInfoUiaProviderBase&&) = default; ScreenInfoUiaProviderBase(ScreenInfoUiaProviderBase&&) = default;
@ -104,9 +104,9 @@ namespace Microsoft::Console::Types
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr) = 0; _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr) = 0;
// weak reference to IUiaData // weak reference to IUiaData
IUiaData* _pData; IUiaData* _pData{ nullptr };
std::wstring _wordDelimiters; std::wstring _wordDelimiters{};
private: private:
// this is used to prevent the object from // this is used to prevent the object from
@ -120,7 +120,7 @@ namespace Microsoft::Console::Types
// eventually overflowing the stack. // eventually overflowing the stack.
// We aren't using this as a cheap locking // We aren't using this as a cheap locking
// mechanism for multi-threaded code. // mechanism for multi-threaded code.
std::map<EVENTID, bool> _signalFiringMapping; std::map<EVENTID, bool> _signalFiringMapping{};
const COORD _getScreenBufferCoords() const; const COORD _getScreenBufferCoords() const;
const TextBuffer& _getTextBuffer() const noexcept; const TextBuffer& _getTextBuffer() const noexcept;

View file

@ -1,22 +1,21 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
#include "pch.h" #include "precomp.h"
#include "TermControlUiaProvider.hpp" #include "TermControlUiaProvider.hpp"
#include "TermControl.h"
using namespace Microsoft::Terminal; using namespace Microsoft::Terminal;
using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types;
using namespace Microsoft::WRL; using namespace Microsoft::WRL;
HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* termControl, #pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
_In_ std::function<RECT(void)> GetBoundingRect) HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ ::Microsoft::Console::Types::IUiaData* const uiaData,
_In_ ::Microsoft::Console::Types::IControlAccessibilityInfo* controlInfo) noexcept
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, termControl); RETURN_HR_IF_NULL(E_INVALIDARG, uiaData);
RETURN_IF_FAILED(ScreenInfoUiaProviderBase::RuntimeClassInitialize(termControl->GetUiaData())); RETURN_IF_FAILED(ScreenInfoUiaProviderBase::RuntimeClassInitialize(uiaData));
_getBoundingRect = GetBoundingRect; _controlInfo = controlInfo;
_termControl = termControl;
// TODO GitHub #1914: Re-attach Tracing to UIA Tree // TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); //Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr);
@ -24,8 +23,10 @@ HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ winrt::Microsoft::Te
} }
IFACEMETHODIMP TermControlUiaProvider::Navigate(_In_ NavigateDirection direction, IFACEMETHODIMP TermControlUiaProvider::Navigate(_In_ NavigateDirection direction,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) noexcept
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
// TODO GitHub #1914: Re-attach Tracing to UIA Tree // TODO GitHub #1914: Re-attach Tracing to UIA Tree
/*ApiMsgNavigate apiMsg; /*ApiMsgNavigate apiMsg;
apiMsg.Direction = direction; apiMsg.Direction = direction;
@ -56,18 +57,29 @@ IFACEMETHODIMP TermControlUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRec
// TODO GitHub #1914: Re-attach Tracing to UIA Tree // TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr); //Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr);
RECT rc = _getBoundingRect(); const RECT rc = _controlInfo->GetBounds();
pRect->left = rc.left; pRect->left = rc.left;
pRect->top = rc.top; pRect->top = rc.top;
pRect->width = rc.right - rc.left; pRect->width = static_cast<double>(rc.right) - static_cast<double>(rc.left);
pRect->height = rc.bottom - rc.top; pRect->height = static_cast<double>(rc.bottom) - static_cast<double>(rc.top);
return S_OK; return S_OK;
} }
IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) IFACEMETHODIMP TermControlUiaProvider::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) noexcept
{ {
try
{
return _controlInfo->GetHostUiaProvider(ppProvider);
}
CATCH_RETURN();
}
IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
// TODO GitHub #1914: Re-attach Tracing to UIA Tree // TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr); //Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr);
try try
@ -87,17 +99,22 @@ IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybe
const COORD TermControlUiaProvider::GetFontSize() const const COORD TermControlUiaProvider::GetFontSize() const
{ {
return _termControl->GetActualFont().GetSize(); return _controlInfo->GetFontSize();
} }
const winrt::Windows::UI::Xaml::Thickness TermControlUiaProvider::GetPadding() const const RECT TermControlUiaProvider::GetPadding() const
{ {
return _termControl->GetPadding(); return _controlInfo->GetPadding();
}
const double TermControlUiaProvider::GetScaleFactor() const
{
return _controlInfo->GetScaleFactor();
} }
void TermControlUiaProvider::ChangeViewport(const SMALL_RECT NewWindow) void TermControlUiaProvider::ChangeViewport(const SMALL_RECT NewWindow)
{ {
_termControl->ScrollViewport(NewWindow.Top); _controlInfo->ChangeViewport(NewWindow);
} }
HRESULT TermControlUiaProvider::GetSelectionRange(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr) HRESULT TermControlUiaProvider::GetSelectionRange(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
@ -112,8 +129,8 @@ HRESULT TermControlUiaProvider::GetSelectionRange(_In_ IRawElementProviderSimple
_pData->GetTextBuffer().GetSize().IncrementInBounds(end, true); _pData->GetTextBuffer().GetSize().IncrementInBounds(end, true);
// TODO GH #4509: Box Selection is misrepresented here as a line selection. // TODO GH #4509: Box Selection is misrepresented here as a line selection.
UiaTextRange* result = nullptr; TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters)); RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters));
*ppUtr = result; *ppUtr = result;
return S_OK; return S_OK;
} }
@ -122,8 +139,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr); RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr; *ppUtr = nullptr;
UiaTextRange* result = nullptr; TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, wordDelimiters)); RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, wordDelimiters));
*ppUtr = result; *ppUtr = result;
return S_OK; return S_OK;
} }
@ -135,8 +152,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr); RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr; *ppUtr = nullptr;
UiaTextRange* result = nullptr; TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, cursor, wordDelimiters)); RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, cursor, wordDelimiters));
*ppUtr = result; *ppUtr = result;
return S_OK; return S_OK;
} }
@ -149,8 +166,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr); RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr; *ppUtr = nullptr;
UiaTextRange* result = nullptr; TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters)); RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters));
*ppUtr = result; *ppUtr = result;
return S_OK; return S_OK;
} }
@ -162,8 +179,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{ {
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr); RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr; *ppUtr = nullptr;
UiaTextRange* result = nullptr; TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, point, wordDelimiters)); RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, point, wordDelimiters));
*ppUtr = result; *ppUtr = result;
return S_OK; return S_OK;
} }

View file

@ -19,32 +19,29 @@ Author(s):
#pragma once #pragma once
#include "..\types\ScreenInfoUiaProviderBase.h" #include "ScreenInfoUiaProviderBase.h"
#include "..\types\UiaTextRangeBase.hpp" #include "UiaTextRangeBase.hpp"
#include "UiaTextRange.hpp" #include "IControlAccessibilityInfo.h"
#include "TermControlUiaTextRange.hpp"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
struct TermControl;
}
namespace Microsoft::Terminal namespace Microsoft::Terminal
{ {
class TermControlUiaProvider : public Microsoft::Console::Types::ScreenInfoUiaProviderBase class TermControlUiaProvider : public Microsoft::Console::Types::ScreenInfoUiaProviderBase
{ {
public: public:
TermControlUiaProvider() = default; TermControlUiaProvider() = default;
HRESULT RuntimeClassInitialize(_In_ winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* termControl, HRESULT RuntimeClassInitialize(_In_ ::Microsoft::Console::Types::IUiaData* const uiaData,
_In_ std::function<RECT()> GetBoundingRect); _In_ ::Microsoft::Console::Types::IControlAccessibilityInfo* controlInfo) noexcept;
// IRawElementProviderFragment methods // IRawElementProviderFragment methods
IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, IFACEMETHODIMP Navigate(_In_ NavigateDirection direction,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override; _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) noexcept override;
IFACEMETHODIMP get_HostRawElementProvider(IRawElementProviderSimple** ppProvider) noexcept override;
IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) override; IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) override;
IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) override; IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) noexcept override;
const COORD GetFontSize() const; const COORD GetFontSize() const;
const winrt::Windows::UI::Xaml::Thickness GetPadding() const; const RECT GetPadding() const;
const double GetScaleFactor() const;
void ChangeViewport(const SMALL_RECT NewWindow) override; void ChangeViewport(const SMALL_RECT NewWindow) override;
protected: protected:
@ -73,7 +70,6 @@ namespace Microsoft::Terminal
_COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override; _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
private: private:
std::function<RECT(void)> _getBoundingRect; ::Microsoft::Console::Types::IControlAccessibilityInfo* _controlInfo{ nullptr };
winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* _termControl;
}; };
} }

View file

@ -1,59 +1,60 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
#include "pch.h" #include "precomp.h"
#include "UiaTextRange.hpp" #include "TermControlUiaTextRange.hpp"
#include "TermControlUiaProvider.hpp" #include "TermControlUiaProvider.hpp"
using namespace Microsoft::Terminal; using namespace Microsoft::Terminal;
using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types;
using namespace Microsoft::WRL; using namespace Microsoft::WRL;
using namespace winrt::Windows::Graphics::Display;
// degenerate range constructor. // degenerate range constructor.
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters) HRESULT TermControlUiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters) noexcept
{ {
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters); return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters);
} }
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, HRESULT TermControlUiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor, const Cursor& cursor,
const std::wstring_view wordDelimiters) const std::wstring_view wordDelimiters) noexcept
{ {
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor, wordDelimiters); return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor, wordDelimiters);
} }
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, HRESULT TermControlUiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
const COORD start, const COORD start,
const COORD end, const COORD end,
const std::wstring_view wordDelimiters) const std::wstring_view wordDelimiters) noexcept
{ {
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, wordDelimiters); return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, wordDelimiters);
} }
// returns a degenerate text range of the start of the row closest to the y value of point // returns a degenerate text range of the start of the row closest to the y value of point
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, #pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
_In_ IRawElementProviderSimple* const pProvider, HRESULT TermControlUiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
const UiaPoint point, _In_ IRawElementProviderSimple* const pProvider,
const std::wstring_view wordDelimiters) const UiaPoint point,
const std::wstring_view wordDelimiters)
{ {
RETURN_IF_FAILED(UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters)); RETURN_IF_FAILED(UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters));
Initialize(point); Initialize(point);
return S_OK; return S_OK;
} }
HRESULT UiaTextRange::RuntimeClassInitialize(const UiaTextRange& a) #pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
HRESULT TermControlUiaTextRange::RuntimeClassInitialize(const TermControlUiaTextRange& a) noexcept
{ {
return UiaTextRangeBase::RuntimeClassInitialize(a); return UiaTextRangeBase::RuntimeClassInitialize(a);
} }
IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) IFACEMETHODIMP TermControlUiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal)
{ {
RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr);
*ppRetVal = nullptr; *ppRetVal = nullptr;
auto hr = MakeAndInitialize<UiaTextRange>(ppRetVal, *this); const auto hr = MakeAndInitialize<TermControlUiaTextRange>(ppRetVal, *this);
if (hr != S_OK) if (hr != S_OK)
{ {
@ -78,9 +79,9 @@ IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider*
return S_OK; return S_OK;
} }
void UiaTextRange::_ChangeViewport(const SMALL_RECT NewWindow) void TermControlUiaTextRange::_ChangeViewport(const SMALL_RECT NewWindow)
{ {
auto provider = static_cast<TermControlUiaProvider*>(_pProvider); const gsl::not_null<TermControlUiaProvider*> provider = static_cast<TermControlUiaProvider*>(_pProvider);
provider->ChangeViewport(NewWindow); provider->ChangeViewport(NewWindow);
} }
@ -91,11 +92,11 @@ void UiaTextRange::_ChangeViewport(const SMALL_RECT NewWindow)
// (0,0) is the top-left of the app window // (0,0) is the top-left of the app window
// Return Value: // Return Value:
// - <none> // - <none>
void UiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const void TermControlUiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const
{ {
auto provider = static_cast<TermControlUiaProvider*>(_pProvider); const gsl::not_null<TermControlUiaProvider*> provider = static_cast<TermControlUiaProvider*>(_pProvider);
auto includeOffsets = [](long clientPos, double termControlPos, double padding, double scaleFactor) { const auto includeOffsets = [](long clientPos, double termControlPos, double padding, double scaleFactor) {
auto result = base::ClampedNumeric<double>(clientPos); auto result = base::ClampedNumeric<double>(clientPos);
result += padding; result += padding;
result *= scaleFactor; result *= scaleFactor;
@ -111,10 +112,10 @@ void UiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const
const auto padding = provider->GetPadding(); const auto padding = provider->GetPadding();
// Get scale factor for display // Get scale factor for display
const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel(); const auto scaleFactor = provider->GetScaleFactor();
clientPoint->x = includeOffsets(clientPoint->x, boundingRect.left, padding.Left, scaleFactor); clientPoint->x = includeOffsets(clientPoint->x, boundingRect.left, padding.left, scaleFactor);
clientPoint->y = includeOffsets(clientPoint->y, boundingRect.top, padding.Top, scaleFactor); clientPoint->y = includeOffsets(clientPoint->y, boundingRect.top, padding.top, scaleFactor);
} }
// Method Description: // Method Description:
@ -124,11 +125,11 @@ void UiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const
// (0,0) is the top-left of the screen // (0,0) is the top-left of the screen
// Return Value: // Return Value:
// - <none> // - <none>
void UiaTextRange::_TranslatePointFromScreen(LPPOINT screenPoint) const void TermControlUiaTextRange::_TranslatePointFromScreen(LPPOINT screenPoint) const
{ {
auto provider = static_cast<TermControlUiaProvider*>(_pProvider); const gsl::not_null<TermControlUiaProvider*> provider = static_cast<TermControlUiaProvider*>(_pProvider);
auto includeOffsets = [](long screenPos, double termControlPos, double padding, double scaleFactor) { const auto includeOffsets = [](long screenPos, double termControlPos, double padding, double scaleFactor) {
auto result = base::ClampedNumeric<double>(screenPos); auto result = base::ClampedNumeric<double>(screenPos);
result -= termControlPos; result -= termControlPos;
result /= scaleFactor; result /= scaleFactor;
@ -144,17 +145,17 @@ void UiaTextRange::_TranslatePointFromScreen(LPPOINT screenPoint) const
const auto padding = provider->GetPadding(); const auto padding = provider->GetPadding();
// Get scale factor for display // Get scale factor for display
const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel(); const auto scaleFactor = provider->GetScaleFactor();
screenPoint->x = includeOffsets(screenPoint->x, boundingRect.left, padding.Left, scaleFactor); screenPoint->x = includeOffsets(screenPoint->x, boundingRect.left, padding.left, scaleFactor);
screenPoint->y = includeOffsets(screenPoint->y, boundingRect.top, padding.Top, scaleFactor); screenPoint->y = includeOffsets(screenPoint->y, boundingRect.top, padding.top, scaleFactor);
} }
const COORD UiaTextRange::_getScreenFontSize() const const COORD TermControlUiaTextRange::_getScreenFontSize() const
{ {
// Do NOT get the font info from IRenderData. It is a dummy font info. // Do NOT get the font info from IRenderData. It is a dummy font info.
// Instead, the font info is saved in the TermControl. So we have to // Instead, the font info is saved in the TermControl. So we have to
// ask our parent to get it for us. // ask our parent to get it for us.
auto provider = static_cast<TermControlUiaProvider*>(_pProvider); const gsl::not_null<const TermControlUiaProvider*> provider = static_cast<TermControlUiaProvider*>(_pProvider);
return provider->GetFontSize(); return provider->GetFontSize();
} }

View file

@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation
Licensed under the MIT license. Licensed under the MIT license.
Module Name: Module Name:
- UiaTextRange.hpp - TermControlUiaTextRange.hpp
Abstract: Abstract:
- This module provides UI Automation access to the text of the console - This module provides UI Automation access to the text of the console
@ -20,28 +20,28 @@ Author(s):
namespace Microsoft::Terminal namespace Microsoft::Terminal
{ {
class UiaTextRange final : public Microsoft::Console::Types::UiaTextRangeBase class TermControlUiaTextRange final : public Microsoft::Console::Types::UiaTextRangeBase
{ {
public: public:
UiaTextRange() = default; TermControlUiaTextRange() = default;
// degenerate range // degenerate range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData, HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter); _In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
// degenerate range at cursor position // degenerate range at cursor position
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData, HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor, const Cursor& cursor,
const std::wstring_view wordDelimiters = DefaultWordDelimiter); const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
// specific endpoint range // specific endpoint range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData, HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
const COORD start, const COORD start,
const COORD end, const COORD end,
const std::wstring_view wordDelimiters = DefaultWordDelimiter); const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
// range from a UiaPoint // range from a UiaPoint
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData, HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
@ -49,7 +49,7 @@ namespace Microsoft::Terminal
const UiaPoint point, const UiaPoint point,
const std::wstring_view wordDelimiters = DefaultWordDelimiter); const std::wstring_view wordDelimiters = DefaultWordDelimiter);
HRESULT RuntimeClassInitialize(const UiaTextRange& a); HRESULT RuntimeClassInitialize(const TermControlUiaTextRange& a) noexcept;
IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override;

View file

@ -58,25 +58,26 @@ namespace Microsoft::Console::Types
static constexpr std::wstring_view DefaultWordDelimiter{ &UNICODE_SPACE, 1 }; static constexpr std::wstring_view DefaultWordDelimiter{ &UNICODE_SPACE, 1 };
// degenerate range // degenerate range
HRESULT RuntimeClassInitialize(_In_ IUiaData* pData, virtual HRESULT RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
_In_ std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept; _In_ std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept;
// degenerate range at cursor position // degenerate range at cursor position
HRESULT RuntimeClassInitialize(_In_ IUiaData* pData, virtual HRESULT RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
_In_ const Cursor& cursor, _In_ const Cursor& cursor,
_In_ std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept; _In_ std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept;
// specific endpoint range // specific endpoint range
HRESULT RuntimeClassInitialize(_In_ IUiaData* pData, virtual HRESULT RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider, _In_ IRawElementProviderSimple* const pProvider,
_In_ const COORD start, _In_ const COORD start,
_In_ const COORD end, _In_ const COORD end,
_In_ std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept; _In_ std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept;
HRESULT RuntimeClassInitialize(const UiaTextRangeBase& a) noexcept; virtual HRESULT RuntimeClassInitialize(const UiaTextRangeBase& a) noexcept;
UiaTextRangeBase(const UiaTextRangeBase&) = default;
UiaTextRangeBase(UiaTextRangeBase&&) = default; UiaTextRangeBase(UiaTextRangeBase&&) = default;
UiaTextRangeBase& operator=(const UiaTextRangeBase&) = default; UiaTextRangeBase& operator=(const UiaTextRangeBase&) = default;
UiaTextRangeBase& operator=(UiaTextRangeBase&&) = default; UiaTextRangeBase& operator=(UiaTextRangeBase&&) = default;
@ -127,11 +128,11 @@ namespace Microsoft::Console::Types
protected: protected:
UiaTextRangeBase() = default; UiaTextRangeBase() = default;
IUiaData* _pData; IUiaData* _pData{ nullptr };
IRawElementProviderSimple* _pProvider; IRawElementProviderSimple* _pProvider{ nullptr };
std::wstring _wordDelimiters; std::wstring _wordDelimiters{};
virtual void _ChangeViewport(const SMALL_RECT NewWindow) = 0; virtual void _ChangeViewport(const SMALL_RECT NewWindow) = 0;
virtual void _TranslatePointToScreen(LPPOINT clientPoint) const = 0; virtual void _TranslatePointToScreen(LPPOINT clientPoint) const = 0;
@ -141,13 +142,13 @@ namespace Microsoft::Console::Types
// used to debug objects passed back and forth // used to debug objects passed back and forth
// between the provider and the client // between the provider and the client
IdType _id; IdType _id{};
// measure units in the form [_start, _end). // measure units in the form [_start, _end).
// These are in the TextBuffer coordinate space. // These are in the TextBuffer coordinate space.
// NOTE: _start is inclusive, but _end is exclusive // NOTE: _start is inclusive, but _end is exclusive
COORD _start; COORD _start{};
COORD _end; COORD _end{};
// This is used by tracing to extract the text value // This is used by tracing to extract the text value
// that the UiaTextRange currently encompasses. // that the UiaTextRange currently encompasses.

View file

@ -24,6 +24,8 @@
<ClCompile Include="..\ThemeUtils.cpp" /> <ClCompile Include="..\ThemeUtils.cpp" />
<ClCompile Include="..\UiaTextRangeBase.cpp" /> <ClCompile Include="..\UiaTextRangeBase.cpp" />
<ClCompile Include="..\UiaTracing.cpp" /> <ClCompile Include="..\UiaTracing.cpp" />
<ClCompile Include="..\TermControlUiaTextRange.cpp" />
<ClCompile Include="..\TermControlUiaProvider.cpp" />
<ClCompile Include="..\Utf16Parser.cpp" /> <ClCompile Include="..\Utf16Parser.cpp" />
<ClCompile Include="..\Viewport.cpp" /> <ClCompile Include="..\Viewport.cpp" />
<ClCompile Include="..\WindowBufferSizeEvent.cpp" /> <ClCompile Include="..\WindowBufferSizeEvent.cpp" />
@ -36,6 +38,7 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="..\IBaseData.h" /> <ClInclude Include="..\IBaseData.h" />
<ClInclude Include="..\IConsoleWindow.hpp" /> <ClInclude Include="..\IConsoleWindow.hpp" />
<ClInclude Include="..\IControlAccessibilityInfo.h" />
<ClInclude Include="..\inc\CodepointWidthDetector.hpp" /> <ClInclude Include="..\inc\CodepointWidthDetector.hpp" />
<ClInclude Include="..\inc\convert.hpp" /> <ClInclude Include="..\inc\convert.hpp" />
<ClInclude Include="..\inc\Environment.hpp" /> <ClInclude Include="..\inc\Environment.hpp" />
@ -48,6 +51,8 @@
<ClInclude Include="..\IUiaData.h" /> <ClInclude Include="..\IUiaData.h" />
<ClInclude Include="..\IUiaEventDispatcher.h" /> <ClInclude Include="..\IUiaEventDispatcher.h" />
<ClInclude Include="..\IUiaWindow.h" /> <ClInclude Include="..\IUiaWindow.h" />
<ClInclude Include="..\TermControlUiaTextRange.hpp" />
<ClInclude Include="..\TermControlUiaProvider.hpp" />
<ClInclude Include="..\precomp.h" /> <ClInclude Include="..\precomp.h" />
<ClInclude Include="..\ScreenInfoUiaProviderBase.h" /> <ClInclude Include="..\ScreenInfoUiaProviderBase.h" />
<ClInclude Include="..\UiaTextRangeBase.hpp" /> <ClInclude Include="..\UiaTextRangeBase.hpp" />

View file

@ -75,6 +75,12 @@
<ClCompile Include="..\UiaTracing.cpp"> <ClCompile Include="..\UiaTracing.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\TermControlUiaProvider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\TermControlUiaTextRange.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\inc\IInputEvent.hpp"> <ClInclude Include="..\inc\IInputEvent.hpp">
@ -101,6 +107,9 @@
<ClInclude Include="..\IConsoleWindow.hpp"> <ClInclude Include="..\IConsoleWindow.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\IControlAccessibilityInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\UiaTextRangeBase.hpp"> <ClInclude Include="..\UiaTextRangeBase.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -137,6 +146,12 @@
<ClInclude Include="..\WindowUiaProviderBase.hpp"> <ClInclude Include="..\WindowUiaProviderBase.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\TermControlUiaProvider.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\TermControlUiaTextRange.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\IUiaWindow.h"> <ClInclude Include="..\IUiaWindow.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>

View file

@ -45,6 +45,8 @@ SOURCES= \
..\WindowUiaProviderBase.cpp \ ..\WindowUiaProviderBase.cpp \
..\ScreenInfoUiaProviderBase.cpp \ ..\ScreenInfoUiaProviderBase.cpp \
..\UiaTextRangeBase.cpp \ ..\UiaTextRangeBase.cpp \
..\TermControlUiaProvider.cpp \
..\TermControlUiaTextRange.cpp \
INCLUDES= \ INCLUDES= \
$(INCLUDES); \ $(INCLUDES); \