Only access ControlInteractivity through the projection (#10051)

## Summary of the Pull Request

This forces the `TermControl` to only use `ControlCore` and `ControlInteractivity` via their WinRT projections. We want this, because WinRT projections can be used across process boundaries. In the future, `ControlCore` and `ControlInteractivity` are going to be living in a different process entirely from `TermControl`. By enforcing this boundary now, we can make sure that they will work seamlessly in the future.

## References
* Tear-out: #1256
* Megathread: #5000
* Project: https://github.com/microsoft/terminal/projects/5

## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-50760270
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

Most all this was just converting pure c++ types to winrt types when possible. I've added a couple helper projections with `til` converters, which made most of this really easy.

The "`MouseButtonState` needs to be composed of `Int32`s instead of `bool`s" is MENTAL. I have no idea why this is, but when I had the control OOP in the sample, that would crash when trying to de-marshal the bools. BODGY.

The biggest changes are in the way the UIA stuff is hooked up. The UiaEngine needs to be attached directly to the `Renderer`, and it can't be easily projected, so it needs to live next to the `ControlCore`. But the `TermControlAutomationPeer` needed the `UiaEngine` to help implement some interfaces.

Now, there's a new layer we've introduced. `InteractivityAutomationPeer` does the `ITextProvider`, `IControlAccessibilityInfo` and the `IUiaEventDispatcher` thing. `TermControlAutomationPeer` now has a 
`InteractivityAutomationPeer` stashed inside itself, so that it can ask the interactivity layer to do the real work. We still need the `TermControlAutomationPeer` though, to be able to attach to the real UI tree.

## Validation Steps Performed

The terminal behaves basically the same as before.

Most importantly, I whipped out Accessibility Insights, and the Terminal looks the same as before.
This commit is contained in:
Mike Griese 2021-07-19 11:59:30 -05:00 committed by GitHub
parent 8947909121
commit 7f3bc3cb04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 968 additions and 349 deletions

View file

@ -1129,7 +1129,7 @@ namespace winrt::TerminalApp::implementation
// - Gets the taskbar state value from the last active control // - Gets the taskbar state value from the last active control
// Return Value: // Return Value:
// - The taskbar state of the last active control // - The taskbar state of the last active control
size_t AppLogic::GetLastActiveControlTaskbarState() uint64_t AppLogic::GetLastActiveControlTaskbarState()
{ {
if (_root) if (_root)
{ {
@ -1142,7 +1142,7 @@ namespace winrt::TerminalApp::implementation
// - Gets the taskbar progress value from the last active control // - Gets the taskbar progress value from the last active control
// Return Value: // Return Value:
// - The taskbar progress of the last active control // - The taskbar progress of the last active control
size_t AppLogic::GetLastActiveControlTaskbarProgress() uint64_t AppLogic::GetLastActiveControlTaskbarProgress()
{ {
if (_root) if (_root)
{ {

View file

@ -89,8 +89,8 @@ namespace winrt::TerminalApp::implementation
void WindowCloseButtonClicked(); void WindowCloseButtonClicked();
size_t GetLastActiveControlTaskbarState(); uint64_t GetLastActiveControlTaskbarState();
size_t GetLastActiveControlTaskbarProgress(); uint64_t GetLastActiveControlTaskbarProgress();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);

View file

@ -2020,11 +2020,11 @@ namespace winrt::TerminalApp::implementation
// - Gets the taskbar state value from the last active control // - Gets the taskbar state value from the last active control
// Return Value: // Return Value:
// - The taskbar state of the last active control // - The taskbar state of the last active control
size_t TerminalPage::GetLastActiveControlTaskbarState() uint64_t TerminalPage::GetLastActiveControlTaskbarState()
{ {
if (auto control{ _GetActiveControl() }) if (auto control{ _GetActiveControl() })
{ {
return gsl::narrow_cast<size_t>(control.TaskbarState()); return control.TaskbarState();
} }
return {}; return {};
} }
@ -2033,11 +2033,11 @@ namespace winrt::TerminalApp::implementation
// - Gets the taskbar progress value from the last active control // - Gets the taskbar progress value from the last active control
// Return Value: // Return Value:
// - The taskbar progress of the last active control // - The taskbar progress of the last active control
size_t TerminalPage::GetLastActiveControlTaskbarProgress() uint64_t TerminalPage::GetLastActiveControlTaskbarProgress()
{ {
if (auto control{ _GetActiveControl() }) if (auto control{ _GetActiveControl() })
{ {
return gsl::narrow_cast<size_t>(control.TaskbarProgress()); return control.TaskbarProgress();
} }
return {}; return {};
} }

View file

@ -83,8 +83,8 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::IDialogPresenter DialogPresenter() const; winrt::TerminalApp::IDialogPresenter DialogPresenter() const;
void DialogPresenter(winrt::TerminalApp::IDialogPresenter dialogPresenter); void DialogPresenter(winrt::TerminalApp::IDialogPresenter dialogPresenter);
size_t GetLastActiveControlTaskbarState(); uint64_t GetLastActiveControlTaskbarState();
size_t GetLastActiveControlTaskbarProgress(); uint64_t GetLastActiveControlTaskbarProgress();
void ShowKeyboardServiceWarning(); void ShowKeyboardServiceWarning();
winrt::hstring KeyboardServiceDisabledText(); winrt::hstring KeyboardServiceDisabledText();

View file

@ -426,7 +426,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Updates last hovered cell, renders / removes rendering of hyper-link if required // - Updates last hovered cell, renders / removes rendering of hyper-link if required
// Arguments: // Arguments:
// - terminalPosition: The terminal position of the pointer // - terminalPosition: The terminal position of the pointer
void ControlCore::UpdateHoveredCell(const std::optional<til::point>& terminalPosition) void ControlCore::SetHoveredCell(Core::Point pos)
{
_updateHoveredCell(std::optional<til::point>{ pos });
}
void ControlCore::ClearHoveredCell()
{
_updateHoveredCell(std::nullopt);
}
void ControlCore::_updateHoveredCell(const std::optional<til::point> terminalPosition)
{ {
if (terminalPosition == _lastHoveredCell) if (terminalPosition == _lastHoveredCell)
{ {
@ -477,7 +486,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(pos) }; return winrt::hstring{ _terminal->GetHyperlinkAtPosition(pos) };
} }
winrt::hstring ControlCore::GetHoveredUriText() const winrt::hstring ControlCore::HoveredUriText() const
{ {
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads. auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
if (_lastHoveredCell.has_value()) if (_lastHoveredCell.has_value())
@ -487,9 +496,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return {}; return {};
} }
std::optional<til::point> ControlCore::GetHoveredCell() const Windows::Foundation::IReference<Core::Point> ControlCore::HoveredCell() const
{ {
return _lastHoveredCell; return _lastHoveredCell.has_value() ? Windows::Foundation::IReference<Core::Point>{ _lastHoveredCell.value() } : nullptr;
} }
// Method Description: // Method Description:
@ -895,6 +904,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _actualFont; return _actualFont;
} }
winrt::Windows::Foundation::Size ControlCore::FontSize() const noexcept
{
const auto fontSize = GetFont().GetSize();
return {
::base::saturated_cast<float>(fontSize.X),
::base::saturated_cast<float>(fontSize.Y)
};
}
winrt::hstring ControlCore::FontFaceName() const noexcept
{
return winrt::hstring{ GetFont().GetFaceName() };
}
uint16_t ControlCore::FontWeight() const noexcept
{
return static_cast<uint16_t>(GetFont().GetWeight());
}
til::size ControlCore::FontSizeInDips() const til::size ControlCore::FontSizeInDips() const
{ {
const til::size fontSize{ GetFont().GetSize() }; const til::size fontSize{ GetFont().GetSize() };
@ -1077,10 +1104,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _settings.CopyOnSelect(); return _settings.CopyOnSelect();
} }
std::vector<std::wstring> ControlCore::SelectedText(bool trimTrailingWhitespace) const Windows::Foundation::Collections::IVector<winrt::hstring> ControlCore::SelectedText(bool trimTrailingWhitespace) const
{ {
// RetrieveSelectedTextFromBuffer will lock while it's reading // RetrieveSelectedTextFromBuffer will lock while it's reading
return _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace).text; const auto internalResult{ _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace).text };
auto result = winrt::single_threaded_vector<winrt::hstring>();
for (const auto& row : internalResult)
{
result.Append(winrt::hstring{ row });
}
return result;
} }
::Microsoft::Console::Types::IUiaData* ControlCore::GetUiaData() const ::Microsoft::Console::Types::IUiaData* ControlCore::GetUiaData() const
@ -1124,7 +1159,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
} }
void ControlCore::SetBackgroundOpacity(const float opacity) void ControlCore::SetBackgroundOpacity(const double opacity)
{ {
if (_renderEngine) if (_renderEngine)
{ {
@ -1176,7 +1211,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
} }
HANDLE ControlCore::GetSwapChainHandle() const uint64_t ControlCore::SwapChainHandle() const
{ {
// This is called by: // This is called by:
// * TermControl::RenderEngineSwapChainChanged, who is only registered // * TermControl::RenderEngineSwapChainChanged, who is only registered
@ -1184,7 +1219,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// * TermControl::_InitializeTerminal, after the call to Initialize, for // * TermControl::_InitializeTerminal, after the call to Initialize, for
// _AttachDxgiSwapChainToXaml. // _AttachDxgiSwapChainToXaml.
// In both cases, we'll have a _renderEngine by then. // In both cases, we'll have a _renderEngine by then.
return _renderEngine->GetSwapChainHandle(); return reinterpret_cast<uint64_t>(_renderEngine->GetSwapChainHandle());
} }
void ControlCore::_rendererWarning(const HRESULT hr) void ControlCore::_rendererWarning(const HRESULT hr)

View file

@ -48,15 +48,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void UpdateAppearance(const IControlAppearance& newAppearance); void UpdateAppearance(const IControlAppearance& newAppearance);
void SizeChanged(const double width, const double height); void SizeChanged(const double width, const double height);
void ScaleChanged(const double scale); void ScaleChanged(const double scale);
HANDLE GetSwapChainHandle() const; uint64_t SwapChainHandle() const;
void AdjustFontSize(int fontSizeDelta); void AdjustFontSize(int fontSizeDelta);
void ResetFontSize(); void ResetFontSize();
FontInfo GetFont() const; FontInfo GetFont() const;
til::size FontSizeInDips() const; til::size FontSizeInDips() const;
winrt::Windows::Foundation::Size FontSize() const noexcept;
winrt::hstring FontFaceName() const noexcept;
uint16_t FontWeight() const noexcept;
til::color BackgroundColor() const; til::color BackgroundColor() const;
void SetBackgroundOpacity(const float opacity); void SetBackgroundOpacity(const double opacity);
void SendInput(const winrt::hstring& wstr); void SendInput(const winrt::hstring& wstr);
void PasteText(const winrt::hstring& hstr); void PasteText(const winrt::hstring& hstr);
@ -67,10 +71,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ResumeRendering(); void ResumeRendering();
void UpdatePatternLocations(); void UpdatePatternLocations();
void UpdateHoveredCell(const std::optional<til::point>& terminalPosition); void SetHoveredCell(Core::Point terminalPosition);
void ClearHoveredCell();
winrt::hstring GetHyperlink(const til::point position) const; winrt::hstring GetHyperlink(const til::point position) const;
winrt::hstring GetHoveredUriText() const; winrt::hstring HoveredUriText() const;
std::optional<til::point> GetHoveredCell() const; Windows::Foundation::IReference<Core::Point> HoveredCell() const;
::Microsoft::Console::Types::IUiaData* GetUiaData() const; ::Microsoft::Console::Types::IUiaData* GetUiaData() const;
@ -119,7 +124,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool HasSelection() const; bool HasSelection() const;
bool CopyOnSelect() const; bool CopyOnSelect() const;
std::vector<std::wstring> SelectedText(bool trimTrailingWhitespace) const; Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
void SetSelectionAnchor(til::point const& position); void SetSelectionAnchor(til::point const& position);
void SetEndSelectionPoint(til::point const& position); void SetEndSelectionPoint(til::point const& position);
@ -232,6 +237,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _raiseReadOnlyWarning(); void _raiseReadOnlyWarning();
void _updateAntiAliasingMode(::Microsoft::Console::Render::DxEngine* const dxEngine); void _updateAntiAliasingMode(::Microsoft::Console::Render::DxEngine* const dxEngine);
void _connectionOutputHandler(const hstring& hstr); void _connectionOutputHandler(const hstring& hstr);
void _updateHoveredCell(const std::optional<til::point> terminalPosition);
friend class ControlUnitTests::ControlCoreTests; friend class ControlUnitTests::ControlCoreTests;
friend class ControlUnitTests::ControlInteractivityTests; friend class ControlUnitTests::ControlInteractivityTests;

View file

@ -8,9 +8,97 @@ import "EventArgs.idl";
namespace Microsoft.Terminal.Control namespace Microsoft.Terminal.Control
{ {
// This is a mirror of
// ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState,
// but projectable.
// !! LOAD BEARING !! If you make this a struct with Booleans (like they
// make the most sense as), then the app will crash trying to toss one of
// these across the process boundary. I haven't the damndest idea why.
[flags]
enum MouseButtonState
{
IsLeftButtonDown = 0x1,
IsMiddleButtonDown = 0x2,
IsRightButtonDown = 0x4
};
[default_interface] runtimeclass ControlCore : ICoreState [default_interface] runtimeclass ControlCore : ICoreState
{ {
ControlCore(IControlSettings settings, ControlCore(IControlSettings settings,
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
Boolean Initialize(Double actualWidth,
Double actualHeight,
Double compositionScale);
void UpdateSettings(IControlSettings settings);
void UpdateAppearance(IControlAppearance appearance);
UInt64 SwapChainHandle { get; };
Windows.Foundation.Size FontSize { get; };
String FontFaceName { get; };
UInt16 FontWeight { get; };
Boolean TrySendKeyEvent(Int16 vkey,
Int16 scanCode,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Boolean keyDown);
Boolean SendCharEvent(Char ch,
Int16 scanCode,
Microsoft.Terminal.Core.ControlKeyStates modifiers);
void SendInput(String text);
void PasteText(String text);
void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
void ClearHoveredCell();
void ResetFontSize();
void AdjustFontSize(Int32 fontSizeDelta);
void SizeChanged(Double width, Double height);
void ScaleChanged(Double scale);
void ToggleShaderEffects();
void ToggleReadOnlyMode();
Microsoft.Terminal.Core.Point CursorPosition { get; };
void ResumeRendering();
void BlinkAttributeTick();
void UpdatePatternLocations();
void Search(String text, Boolean goForward, Boolean caseSensitive);
void SetBackgroundOpacity(Double opacity);
Microsoft.Terminal.Core.Color BackgroundColor { get; };
Boolean HasSelection { get; };
IVector<String> SelectedText(Boolean trimTrailingWhitespace);
String HoveredUriText { get; };
Windows.Foundation.IReference<Microsoft.Terminal.Core.Point> HoveredCell { get; };
void Close();
void BlinkCursor();
Boolean IsInReadOnlyMode { get; };
Boolean CursorOn;
void EnablePainting();
event FontSizeChangedEventArgs FontSizeChanged;
event Windows.Foundation.TypedEventHandler<Object, CopyToClipboardEventArgs> CopyToClipboard;
event Windows.Foundation.TypedEventHandler<Object, TitleChangedEventArgs> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> WarningBell;
event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> BackgroundColorChanged;
event Windows.Foundation.TypedEventHandler<Object, ScrollPositionChangedArgs> ScrollPositionChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> CursorPositionChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> TaskbarProgressChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> ConnectionStateChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> HoveredHyperlinkChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> RendererEnteredErrorState;
event Windows.Foundation.TypedEventHandler<Object, Object> SwapChainChanged;
event Windows.Foundation.TypedEventHandler<Object, RendererWarningArgs> RendererWarning;
event Windows.Foundation.TypedEventHandler<Object, NoticeEventArgs> RaiseNotice;
event Windows.Foundation.TypedEventHandler<Object, TransparencyChangedEventArgs> TransparencyChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> ReceivedOutput;
}; };
} }

View file

@ -8,13 +8,15 @@
#include <unicode.hpp> #include <unicode.hpp>
#include <Utf16Parser.hpp> #include <Utf16Parser.hpp>
#include <Utils.h> #include <Utils.h>
#include <WinUser.h>
#include <LibraryResources.h> #include <LibraryResources.h>
#include "../../types/inc/GlyphWidth.hpp" #include "../../types/inc/GlyphWidth.hpp"
#include "../../types/inc/Utils.hpp" #include "../../types/inc/Utils.hpp"
#include "../../buffer/out/search.h" #include "../../buffer/out/search.h"
#include "InteractivityAutomationPeer.h"
#include "ControlInteractivity.g.cpp" #include "ControlInteractivity.g.cpp"
#include "TermControl.h"
using namespace ::Microsoft::Console::Types; using namespace ::Microsoft::Console::Types;
using namespace ::Microsoft::Console::VirtualTerminal; using namespace ::Microsoft::Console::VirtualTerminal;
@ -27,6 +29,15 @@ static constexpr unsigned int MAX_CLICK_COUNT = 3;
namespace winrt::Microsoft::Terminal::Control::implementation namespace winrt::Microsoft::Terminal::Control::implementation
{ {
static constexpr TerminalInput::MouseButtonState toInternalMouseState(const Control::MouseButtonState& state)
{
return TerminalInput::MouseButtonState{
WI_IsFlagSet(state, MouseButtonState::IsLeftButtonDown),
WI_IsFlagSet(state, MouseButtonState::IsMiddleButtonDown),
WI_IsFlagSet(state, MouseButtonState::IsRightButtonDown)
};
}
ControlInteractivity::ControlInteractivity(IControlSettings settings, ControlInteractivity::ControlInteractivity(IControlSettings settings,
TerminalConnection::ITerminalConnection connection) : TerminalConnection::ITerminalConnection connection) :
_touchAnchor{ std::nullopt }, _touchAnchor{ std::nullopt },
@ -37,6 +48,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core = winrt::make_self<ControlCore>(settings, connection); _core = winrt::make_self<ControlCore>(settings, connection);
} }
// Method Description:
// - Updates our internal settings. These settings should be
// interactivity-specific. Right now, we primarily update _rowsToScroll
// with the current value of SPI_GETWHEELSCROLLLINES.
// Arguments:
// - <none>
// Return Value:
// - <none>
void ControlInteractivity::UpdateSettings() void ControlInteractivity::UpdateSettings()
{ {
_updateSystemParameterSettings(); _updateSystemParameterSettings();
@ -50,9 +69,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_multiClickTimer = GetDoubleClickTime() * 1000; _multiClickTimer = GetDoubleClickTime() * 1000;
} }
winrt::com_ptr<ControlCore> ControlInteractivity::GetCore() Control::ControlCore ControlInteractivity::Core()
{ {
return _core; return *_core;
} }
// Method Description: // Method Description:
@ -85,11 +104,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _multiClickCounter; return _multiClickCounter;
} }
void ControlInteractivity::GainFocus() void ControlInteractivity::GotFocus()
{ {
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Enable());
}
_updateSystemParameterSettings(); _updateSystemParameterSettings();
} }
void ControlInteractivity::LostFocus()
{
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Disable());
}
}
// Method Description // Method Description
// - Updates internal params based on system parameters // - Updates internal params based on system parameters
void ControlInteractivity::_updateSystemParameterSettings() noexcept void ControlInteractivity::_updateSystemParameterSettings() noexcept
@ -156,7 +188,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core->PasteText(winrt::hstring{ wstr }); _core->PasteText(winrt::hstring{ wstr });
} }
void ControlInteractivity::PointerPressed(TerminalInput::MouseButtonState buttonState, void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const uint64_t timestamp, const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
@ -170,7 +202,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// GH#9396: we prioritize hyper-link over VT mouse events // GH#9396: we prioritize hyper-link over VT mouse events
auto hyperlink = _core->GetHyperlink(terminalPosition); auto hyperlink = _core->GetHyperlink(terminalPosition);
if (buttonState.isLeftButtonDown && if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) &&
ctrlEnabled && !hyperlink.empty()) ctrlEnabled && !hyperlink.empty())
{ {
const auto clickCount = _numberOfClicks(pixelPosition, timestamp); const auto clickCount = _numberOfClicks(pixelPosition, timestamp);
@ -182,9 +214,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
else if (_canSendVTMouseInput(modifiers)) else if (_canSendVTMouseInput(modifiers))
{ {
_core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState); _core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, toInternalMouseState(buttonState));
} }
else if (buttonState.isLeftButtonDown) else if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
{ {
const auto clickCount = _numberOfClicks(pixelPosition, timestamp); const auto clickCount = _numberOfClicks(pixelPosition, timestamp);
// This formula enables the number of clicks to cycle properly // This formula enables the number of clicks to cycle properly
@ -219,7 +251,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_singleClickTouchdownPos = std::nullopt; _singleClickTouchdownPos = std::nullopt;
} }
} }
else if (buttonState.isRightButtonDown) else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
{ {
// CopyOnSelect right click always pastes // CopyOnSelect right click always pastes
if (_core->CopyOnSelect() || !_core->HasSelection()) if (_core->CopyOnSelect() || !_core->HasSelection())
@ -238,7 +270,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_touchAnchor = contactPoint; _touchAnchor = contactPoint;
} }
void ControlInteractivity::PointerMoved(TerminalInput::MouseButtonState buttonState, void ControlInteractivity::PointerMoved(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused, const bool focused,
@ -249,9 +281,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Short-circuit isReadOnly check to avoid warning dialog // Short-circuit isReadOnly check to avoid warning dialog
if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers)) if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
{ {
_core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState); _core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, toInternalMouseState(buttonState));
} }
else if (focused && buttonState.isLeftButtonDown) else if (focused && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
{ {
if (_singleClickTouchdownPos) if (_singleClickTouchdownPos)
{ {
@ -279,7 +311,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
SetEndSelectionPoint(pixelPosition); SetEndSelectionPoint(pixelPosition);
} }
_core->UpdateHoveredCell(terminalPosition); _core->SetHoveredCell(terminalPosition);
} }
void ControlInteractivity::TouchMoved(const til::point newTouchPoint, void ControlInteractivity::TouchMoved(const til::point newTouchPoint,
@ -319,7 +351,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
} }
void ControlInteractivity::PointerReleased(TerminalInput::MouseButtonState buttonState, void ControlInteractivity::PointerReleased(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const til::point pixelPosition) const til::point pixelPosition)
@ -328,7 +360,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Short-circuit isReadOnly check to avoid warning dialog // Short-circuit isReadOnly check to avoid warning dialog
if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers)) if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
{ {
_core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState); _core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, toInternalMouseState(buttonState));
return; return;
} }
@ -366,7 +398,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ControlInteractivity::MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, bool ControlInteractivity::MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const int32_t delta, const int32_t delta,
const til::point pixelPosition, const til::point pixelPosition,
const TerminalInput::MouseButtonState state) const Control::MouseButtonState buttonState)
{ {
const til::point terminalPosition = _getTerminalPosition(pixelPosition); const til::point terminalPosition = _getTerminalPosition(pixelPosition);
@ -382,7 +414,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WM_MOUSEWHEEL, WM_MOUSEWHEEL,
modifiers, modifiers,
::base::saturated_cast<short>(delta), ::base::saturated_cast<short>(delta),
state); toInternalMouseState(buttonState));
} }
const auto ctrlPressed = modifiers.IsCtrlPressed(); const auto ctrlPressed = modifiers.IsCtrlPressed();
@ -398,7 +430,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
else else
{ {
_mouseScrollHandler(delta, pixelPosition, state.isLeftButtonDown); _mouseScrollHandler(delta, pixelPosition, WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown));
} }
return false; return false;
} }
@ -557,4 +589,36 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Convert the location in pixels to characters within the current viewport. // Convert the location in pixels to characters within the current viewport.
return til::point{ pixelPosition / fontSize }; return til::point{ pixelPosition / fontSize };
} }
// Method Description:
// - Creates an automation peer for the Terminal Control, enabling
// accessibility on our control.
// - Our implementation implements the ITextProvider pattern, and the
// IControlAccessibilityInfo, to connect to the UiaEngine, which must be
// attached to the core's renderer.
// - The TermControlAutomationPeer will connect this to the UI tree.
// Arguments:
// - None
// Return Value:
// - The automation peer for our control
Control::InteractivityAutomationPeer ControlInteractivity::OnCreateAutomationPeer()
try
{
auto autoPeer = winrt::make_self<implementation::InteractivityAutomationPeer>(this);
_uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get());
_core->AttachUiaEngine(_uiaEngine.get());
return *autoPeer;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return nullptr;
}
::Microsoft::Console::Types::IUiaData* ControlInteractivity::GetUiaData() const
{
return _core->GetUiaData();
}
} }

View file

@ -23,11 +23,6 @@
#include "ControlCore.h" #include "ControlCore.h"
namespace Microsoft::Console::VirtualTerminal
{
struct MouseButtonState;
}
namespace ControlUnitTests namespace ControlUnitTests
{ {
class ControlCoreTests; class ControlCoreTests;
@ -42,20 +37,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
ControlInteractivity(IControlSettings settings, ControlInteractivity(IControlSettings settings,
TerminalConnection::ITerminalConnection connection); TerminalConnection::ITerminalConnection connection);
void GainFocus(); void GotFocus();
void LostFocus();
void UpdateSettings(); void UpdateSettings();
void Initialize(); void Initialize();
winrt::com_ptr<ControlCore> GetCore(); Control::ControlCore Core();
Control::InteractivityAutomationPeer OnCreateAutomationPeer();
::Microsoft::Console::Types::IUiaData* GetUiaData() const;
#pragma region Input Methods #pragma region Input Methods
void PointerPressed(::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState buttonState, void PointerPressed(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const uint64_t timestamp, const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const til::point pixelPosition); const til::point pixelPosition);
void TouchPressed(const til::point contactPoint); void TouchPressed(const til::point contactPoint);
void PointerMoved(::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState buttonState, void PointerMoved(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused, const bool focused,
@ -63,7 +62,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TouchMoved(const til::point newTouchPoint, void TouchMoved(const til::point newTouchPoint,
const bool focused); const bool focused);
void PointerReleased(::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState buttonState, void PointerReleased(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const til::point pixelPosition); const til::point pixelPosition);
@ -72,7 +71,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, bool MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const int32_t delta, const int32_t delta,
const til::point pixelPosition, const til::point pixelPosition,
const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state); const Control::MouseButtonState state);
void UpdateScrollbar(const double newValue); void UpdateScrollbar(const double newValue);
@ -83,7 +82,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void RequestPasteTextFromClipboard(); void RequestPasteTextFromClipboard();
void SetEndSelectionPoint(const til::point pixelPosition); void SetEndSelectionPoint(const til::point pixelPosition);
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs);
private: private:
// NOTE: _uiaEngine must be ordered before _core.
//
// ControlCore::AttachUiaEngine receives a IRenderEngine as a raw pointer, which we own.
// We must ensure that we first destroy the ControlCore before the UiaEngine instance
// in order to safely resolve this unsafe pointer dependency. Otherwise a deallocated
// IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown.
// (C++ class members are destroyed in reverse order.)
std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine;
winrt::com_ptr<ControlCore> _core{ nullptr }; winrt::com_ptr<ControlCore> _core{ nullptr };
unsigned int _rowsToScroll; unsigned int _rowsToScroll;
double _internalScrollbarPosition{ 0.0 }; double _internalScrollbarPosition{ 0.0 };
@ -129,10 +141,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _sendPastedTextToConnection(std::wstring_view wstr); void _sendPastedTextToConnection(std::wstring_view wstr);
til::point _getTerminalPosition(const til::point& pixelPosition); til::point _getTerminalPosition(const til::point& pixelPosition);
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs);
friend class ControlUnitTests::ControlCoreTests; friend class ControlUnitTests::ControlCoreTests;
friend class ControlUnitTests::ControlInteractivityTests; friend class ControlUnitTests::ControlInteractivityTests;
}; };

View file

@ -5,6 +5,8 @@ import "ICoreState.idl";
import "IControlSettings.idl"; import "IControlSettings.idl";
import "ControlCore.idl"; import "ControlCore.idl";
import "EventArgs.idl"; import "EventArgs.idl";
import "InteractivityAutomationPeer.idl";
namespace Microsoft.Terminal.Control namespace Microsoft.Terminal.Control
{ {
@ -13,5 +15,51 @@ namespace Microsoft.Terminal.Control
{ {
ControlInteractivity(IControlSettings settings, ControlInteractivity(IControlSettings settings,
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
ControlCore Core { get; };
void UpdateSettings();
void Initialize();
void GotFocus();
void LostFocus();
InteractivityAutomationPeer OnCreateAutomationPeer();
Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference<CopyFormat> formats);
void RequestPasteTextFromClipboard();
void SetEndSelectionPoint(Microsoft.Terminal.Core.Point point);
void PointerPressed(MouseButtonState buttonState,
UInt32 pointerUpdateKind,
UInt64 timestamp,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition);
void TouchPressed(Microsoft.Terminal.Core.Point contactPoint);
void PointerMoved(MouseButtonState buttonState,
UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Boolean focused,
Microsoft.Terminal.Core.Point pixelPosition);
void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint,
Boolean focused);
void PointerReleased(MouseButtonState buttonState,
UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition);
void TouchReleased();
Boolean MouseWheel(Microsoft.Terminal.Core.ControlKeyStates modifiers,
Int32 delta,
Microsoft.Terminal.Core.Point pixelPosition,
MouseButtonState state);
void UpdateScrollbar(Double newValue);
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
event Windows.Foundation.TypedEventHandler<Object, ScrollPositionChangedArgs> ScrollPositionChanged;
event Windows.Foundation.TypedEventHandler<Object, PasteFromClipboardEventArgs> PasteFromClipboard;
}; };
} }

View file

@ -17,6 +17,7 @@ namespace Microsoft.Terminal.Control
Int32 ScrollOffset { get; }; Int32 ScrollOffset { get; };
Int32 ViewHeight { get; }; Int32 ViewHeight { get; };
Int32 BufferHeight { get; };
Boolean BracketedPasteEnabled { get; }; Boolean BracketedPasteEnabled { get; };

View file

@ -0,0 +1,208 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include <UIAutomationCore.h>
#include <LibraryResources.h>
#include "InteractivityAutomationPeer.h"
#include "InteractivityAutomationPeer.g.cpp"
#include "XamlUiaTextRange.h"
#include "../types/UiaTracing.h"
using namespace Microsoft::Console::Types;
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
using namespace winrt::Windows::Graphics::Display;
namespace UIA
{
using ::ITextRangeProvider;
using ::SupportedTextSelection;
}
namespace XamlAutomation
{
using winrt::Windows::UI::Xaml::Automation::SupportedTextSelection;
using winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple;
using winrt::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider;
}
namespace winrt::Microsoft::Terminal::Control::implementation
{
InteractivityAutomationPeer::InteractivityAutomationPeer(Control::implementation::ControlInteractivity* owner) :
_interactivity{ owner }
{
THROW_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, _interactivity->GetUiaData(), this));
};
void InteractivityAutomationPeer::SetControlBounds(const Windows::Foundation::Rect bounds)
{
_controlBounds = til::rectangle{ til::math::rounding, bounds };
}
void InteractivityAutomationPeer::SetControlPadding(const Core::Padding padding)
{
_controlPadding = padding;
}
// Method Description:
// - Signals the ui automation client that the terminal's selection has
// changed and should be updated
// - We will raise a new event, for out embedding control to be able to
// raise the event. AutomationPeer by itself doesn't hook up to the
// eventing mechanism, we need the FrameworkAutomationPeer to do that.
// Arguments:
// - <none>
// Return Value:
// - <none>
void InteractivityAutomationPeer::SignalSelectionChanged()
{
_SelectionChangedHandlers(*this, nullptr);
}
// Method Description:
// - Signals the ui automation client that the terminal's output has changed
// and should be updated
// - We will raise a new event, for out embedding control to be able to
// raise the event. AutomationPeer by itself doesn't hook up to the
// eventing mechanism, we need the FrameworkAutomationPeer to do that.
// Arguments:
// - <none>
// Return Value:
// - <none>
void InteractivityAutomationPeer::SignalTextChanged()
{
_TextChangedHandlers(*this, nullptr);
}
// Method Description:
// - Signals the ui automation client that the cursor's state has changed
// and should be updated
// - We will raise a new event, for out embedding control to be able to
// raise the event. AutomationPeer by itself doesn't hook up to the
// eventing mechanism, we need the FrameworkAutomationPeer to do that.
// Arguments:
// - <none>
// Return Value:
// - <none>
void InteractivityAutomationPeer::SignalCursorChanged()
{
_CursorChangedHandlers(*this, nullptr);
}
#pragma region ITextProvider
com_array<XamlAutomation::ITextRangeProvider> InteractivityAutomationPeer::GetSelection()
{
SAFEARRAY* pReturnVal;
THROW_IF_FAILED(_uiaProvider->GetSelection(&pReturnVal));
return WrapArrayOfTextRangeProviders(pReturnVal);
}
com_array<XamlAutomation::ITextRangeProvider> InteractivityAutomationPeer::GetVisibleRanges()
{
SAFEARRAY* pReturnVal;
THROW_IF_FAILED(_uiaProvider->GetVisibleRanges(&pReturnVal));
return WrapArrayOfTextRangeProviders(pReturnVal);
}
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement)
{
UIA::ITextRangeProvider* returnVal;
// ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr
THROW_IF_FAILED(_uiaProvider->RangeFromChild(/* IRawElementProviderSimple */ nullptr,
&returnVal));
const auto parentProvider = this->ProviderFromPeer(*this);
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
}
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>();
}
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>();
}
XamlAutomation::SupportedTextSelection InteractivityAutomationPeer::SupportedTextSelection()
{
UIA::SupportedTextSelection returnVal;
THROW_IF_FAILED(_uiaProvider->get_SupportedTextSelection(&returnVal));
return static_cast<XamlAutomation::SupportedTextSelection>(returnVal);
}
#pragma endregion
#pragma region IControlAccessibilityInfo
COORD InteractivityAutomationPeer::GetFontSize() const
{
return til::size{ til::math::rounding, _interactivity->Core().FontSize() };
}
RECT InteractivityAutomationPeer::GetBounds() const
{
return _controlBounds;
}
HRESULT InteractivityAutomationPeer::GetHostUiaProvider(IRawElementProviderSimple** provider)
{
RETURN_HR_IF(E_INVALIDARG, provider == nullptr);
*provider = nullptr;
return S_OK;
}
RECT InteractivityAutomationPeer::GetPadding() const
{
return _controlPadding;
}
double InteractivityAutomationPeer::GetScaleFactor() const
{
return DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
}
void InteractivityAutomationPeer::ChangeViewport(const SMALL_RECT NewWindow)
{
_interactivity->UpdateScrollbar(NewWindow.Top);
}
#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> InteractivityAutomationPeer::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;
}
}

View file

@ -0,0 +1,85 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- InteractivityAutomationPeer.h
Abstract:
- This module provides UI Automation access to the ControlInteractivity,
to support both automation tests and accessibility (screen
reading) applications.
- See TermControlAutomationPeer for more details on how UIA is implemented.
- This is the primary implementation of the ITextProvider interface, for the
TermControlAutomationPeer. The TermControlAutomationPeer will be attached to
the actual UI tree, via FrameworkElementAutomationPeer. However, the
ControlInteractivity is totally oblivious to the UI tree that might be hosting
it. So this class implements the actual text pattern for the buffer, because
it has access to the buffer. TermControlAutomationPeer can then call the
methods on this class to expose the implementation in the actual UI tree.
Author(s):
- Mike Griese (migrie), May 2021
--*/
#pragma once
#include "ControlInteractivity.h"
#include "InteractivityAutomationPeer.g.h"
#include "../types/TermControlUiaProvider.hpp"
#include "../types/IUiaEventDispatcher.h"
#include "../types/IControlAccessibilityInfo.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
struct InteractivityAutomationPeer :
public InteractivityAutomationPeerT<InteractivityAutomationPeer>,
::Microsoft::Console::Types::IUiaEventDispatcher,
::Microsoft::Console::Types::IControlAccessibilityInfo
{
public:
InteractivityAutomationPeer(Microsoft::Terminal::Control::implementation::ControlInteractivity* owner);
void SetControlBounds(const Windows::Foundation::Rect bounds);
void SetControlPadding(const Core::Padding padding);
#pragma region IUiaEventDispatcher
void SignalSelectionChanged() override;
void SignalTextChanged() override;
void SignalCursorChanged() override;
#pragma endregion
#pragma region ITextProvider Pattern
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider RangeFromPoint(Windows::Foundation::Point screenLocation);
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider RangeFromChild(Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple childElement);
com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetVisibleRanges();
com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetSelection();
Windows::UI::Xaml::Automation::SupportedTextSelection SupportedTextSelection();
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider DocumentRange();
#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
TYPED_EVENT(SelectionChanged, IInspectable, IInspectable);
TYPED_EVENT(TextChanged, IInspectable, IInspectable);
TYPED_EVENT(CursorChanged, IInspectable, IInspectable);
private:
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
til::rectangle _controlBounds{};
til::rectangle _controlPadding{};
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges);
};
}

View file

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Control
{
[default_interface] runtimeclass InteractivityAutomationPeer :
Windows.UI.Xaml.Automation.Peers.AutomationPeer,
Windows.UI.Xaml.Automation.Provider.ITextProvider
{
void SetControlBounds(Windows.Foundation.Rect bounds);
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
event Windows.Foundation.TypedEventHandler<Object, Object> SelectionChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> TextChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> CursorChanged;
}
}

View file

@ -8,7 +8,6 @@
#include <unicode.hpp> #include <unicode.hpp>
#include <Utf16Parser.hpp> #include <Utf16Parser.hpp>
#include <Utils.h> #include <Utils.h>
#include <WinUser.h>
#include <LibraryResources.h> #include <LibraryResources.h>
#include "../../types/inc/GlyphWidth.hpp" #include "../../types/inc/GlyphWidth.hpp"
#include "../../types/inc/Utils.hpp" #include "../../types/inc/Utils.hpp"
@ -44,6 +43,8 @@ constexpr const auto TerminalWarningBellInterval = std::chrono::milliseconds(100
DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::CopyFormat); DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::CopyFormat);
DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::MouseButtonState);
namespace winrt::Microsoft::Terminal::Control::implementation namespace winrt::Microsoft::Terminal::Control::implementation
{ {
TermControl::TermControl(IControlSettings settings, TermControl::TermControl(IControlSettings settings,
@ -60,33 +61,33 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
InitializeComponent(); InitializeComponent();
_interactivity = winrt::make_self<ControlInteractivity>(settings, connection); _interactivity = winrt::make<implementation::ControlInteractivity>(settings, connection);
_core = _interactivity->GetCore(); _core = _interactivity.Core();
// Use a manual revoker on the output event, so we can immediately stop // Use a manual revoker on the output event, so we can immediately stop
// worrying about it on destruction. // worrying about it on destruction.
_coreOutputEventToken = _core->ReceivedOutput({ this, &TermControl::_coreReceivedOutput }); _coreOutputEventToken = _core.ReceivedOutput({ this, &TermControl::_coreReceivedOutput });
// These events might all be triggered by the connection, but that // These events might all be triggered by the connection, but that
// should be drained and closed before we complete destruction. So these // should be drained and closed before we complete destruction. So these
// are safe. // are safe.
_core->ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); _core.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
_core->WarningBell({ this, &TermControl::_coreWarningBell }); _core.WarningBell({ this, &TermControl::_coreWarningBell });
_core->CursorPositionChanged({ this, &TermControl::_CursorPositionChanged }); _core.CursorPositionChanged({ this, &TermControl::_CursorPositionChanged });
// This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here. // This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here.
_core->RendererEnteredErrorState({ get_weak(), &TermControl::_RendererEnteredErrorState }); _core.RendererEnteredErrorState({ get_weak(), &TermControl::_RendererEnteredErrorState });
// These callbacks can only really be triggered by UI interactions. So // These callbacks can only really be triggered by UI interactions. So
// they don't need weak refs - they can't be triggered unless we're // they don't need weak refs - they can't be triggered unless we're
// alive. // alive.
_core->BackgroundColorChanged({ this, &TermControl::_BackgroundColorChangedHandler }); _core.BackgroundColorChanged({ this, &TermControl::_BackgroundColorChangedHandler });
_core->FontSizeChanged({ this, &TermControl::_coreFontSizeChanged }); _core.FontSizeChanged({ this, &TermControl::_coreFontSizeChanged });
_core->TransparencyChanged({ this, &TermControl::_coreTransparencyChanged }); _core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged });
_core->RaiseNotice({ this, &TermControl::_coreRaisedNotice }); _core.RaiseNotice({ this, &TermControl::_coreRaisedNotice });
_core->HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged }); _core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged });
_interactivity->OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); _interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
_interactivity->ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); _interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
// Initialize the terminal only once the swapchainpanel is loaded - that // Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout // way, we'll be able to query the real pixel size it got on layout
@ -128,7 +129,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
[weakThis = get_weak()]() { [weakThis = get_weak()]() {
if (auto control{ weakThis.get() }; !control->_IsClosing()) if (auto control{ weakThis.get() }; !control->_IsClosing())
{ {
control->_core->UpdatePatternLocations(); control->_core.UpdatePatternLocations();
} }
}); });
@ -187,16 +188,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// If a text is selected inside terminal, use it to populate the search box. // If a text is selected inside terminal, use it to populate the search box.
// If the search box already contains a value, it will be overridden. // If the search box already contains a value, it will be overridden.
if (_core->HasSelection()) if (_core.HasSelection())
{ {
// Currently we populate the search box only if a single line is selected. // Currently we populate the search box only if a single line is selected.
// Empirically, multi-line selection works as well on sample scenarios, // Empirically, multi-line selection works as well on sample scenarios,
// but since code paths differ, extra work is required to ensure correctness. // but since code paths differ, extra work is required to ensure correctness.
auto bufferText = _core->SelectedText(true); auto bufferText = _core.SelectedText(true);
if (bufferText.size() == 1) if (bufferText.Size() == 1)
{ {
const auto selectedLine{ til::at(bufferText, 0) }; const auto selectedLine{ bufferText.GetAt(0) };
_searchBox->PopulateTextbox(selectedLine.data()); _searchBox->PopulateTextbox(selectedLine);
} }
} }
@ -217,7 +218,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
else else
{ {
_core->Search(_searchBox->TextBox().Text(), goForward, false); _core.Search(_searchBox->TextBox().Text(), goForward, false);
} }
} }
@ -234,7 +235,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const bool goForward, const bool goForward,
const bool caseSensitive) const bool caseSensitive)
{ {
_core->Search(text, goForward, caseSensitive); _core.Search(text, goForward, caseSensitive);
} }
// Method Description: // Method Description:
@ -303,7 +304,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return; return;
} }
_core->UpdateSettings(_settings); _core.UpdateSettings(_settings);
// Update our control settings // Update our control settings
_ApplyUISettings(_settings); _ApplyUISettings(_settings);
@ -362,7 +363,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
foregroundBrush.Color(static_cast<til::color>(newAppearance.DefaultForeground())); foregroundBrush.Color(static_cast<til::color>(newAppearance.DefaultForeground()));
TSFInputControl().Foreground(foregroundBrush); TSFInputControl().Foreground(foregroundBrush);
_core->UpdateAppearance(newAppearance); _core.UpdateAppearance(newAppearance);
} }
// Method Description: // Method Description:
@ -373,12 +374,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none> // - <none>
void TermControl::SendInput(const winrt::hstring& wstr) void TermControl::SendInput(const winrt::hstring& wstr)
{ {
_core->SendInput(wstr); _core.SendInput(wstr);
} }
void TermControl::ToggleShaderEffects() void TermControl::ToggleShaderEffects()
{ {
_core->ToggleShaderEffects(); _core.ToggleShaderEffects();
} }
// Method Description: // Method Description:
@ -401,7 +402,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_changeBackgroundColor(bg); _changeBackgroundColor(bg);
// Apply padding as swapChainPanel's margin // Apply padding as swapChainPanel's margin
auto newMargin = _ParseThicknessFromPadding(newSettings.Padding()); auto newMargin = ParseThicknessFromPadding(newSettings.Padding());
SwapChainPanel().Margin(newMargin); SwapChainPanel().Margin(newMargin);
TSFInputControl().Margin(newMargin); TSFInputControl().Margin(newMargin);
@ -422,7 +423,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
ScrollBar().Visibility(Visibility::Visible); ScrollBar().Visibility(Visibility::Visible);
} }
_interactivity->UpdateSettings(); _interactivity.UpdateSettings();
if (_automationPeer)
{
_automationPeer.SetControlPadding(Core::Padding{ newMargin.Left,
newMargin.Top,
newMargin.Right,
newMargin.Bottom });
}
} }
// Method Description: // Method Description:
@ -471,7 +479,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
// GH#5098: Inform the engine of the new opacity of the default text background. // GH#5098: Inform the engine of the new opacity of the default text background.
_core->SetBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity())); _core.SetBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity()));
} }
else else
{ {
@ -479,7 +487,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
RootGrid().Background(solidColor); RootGrid().Background(solidColor);
// GH#5098: Inform the engine of the new opacity of the default text background. // GH#5098: Inform the engine of the new opacity of the default text background.
_core->SetBackgroundOpacity(1.0f); _core.SetBackgroundOpacity(1.0f);
} }
} }
@ -492,7 +500,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TermControl::_BackgroundColorChangedHandler(const IInspectable& /*sender*/, void TermControl::_BackgroundColorChangedHandler(const IInspectable& /*sender*/,
const IInspectable& /*args*/) const IInspectable& /*args*/)
{ {
til::color newBgColor{ _core->BackgroundColor() }; til::color newBgColor{ _core.BackgroundColor() };
_changeBackgroundColor(newBgColor); _changeBackgroundColor(newBgColor);
} }
@ -527,37 +535,25 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Return Value: // Return Value:
// - The automation peer for our control // - The automation peer for our control
Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer() Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer()
try
{ {
if (_initializedTerminal && !_IsClosing()) // only set up the automation peer if we're ready to go live if (_initializedTerminal && !_IsClosing()) // only set up the automation peer if we're ready to go live
{ {
// create a custom automation peer with this code pattern: // create a custom automation peer with this code pattern:
// (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers) // (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
auto autoPeer = winrt::make_self<implementation::TermControlAutomationPeer>(this); if (const auto& interactivityAutoPeer = _interactivity.OnCreateAutomationPeer())
{
_uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get()); _automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, interactivityAutoPeer);
_core->AttachUiaEngine(_uiaEngine.get()); return _automationPeer;
_automationPeer = *autoPeer; }
return _automationPeer;
} }
return nullptr; return nullptr;
} }
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return nullptr;
}
::Microsoft::Console::Types::IUiaData* TermControl::GetUiaData() const
{
return _core->GetUiaData();
}
// This is needed for TermControlAutomationPeer. We probably could find a // This is needed for TermControlAutomationPeer. We probably could find a
// clever way around asking the core for this. // clever way around asking the core for this.
til::point TermControl::GetFontSize() const til::point TermControl::GetFontSize() const
{ {
return _core->GetFont().GetSize(); return til::point{ til::math::rounding, _core.FontSize().Width, _core.FontSize().Height };
} }
const Windows::UI::Xaml::Thickness TermControl::GetPadding() const Windows::UI::Xaml::Thickness TermControl::GetPadding()
@ -567,7 +563,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TerminalConnection::ConnectionState TermControl::ConnectionState() const TerminalConnection::ConnectionState TermControl::ConnectionState() const
{ {
return _core->ConnectionState(); return _core.ConnectionState();
} }
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable /*args*/) winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable /*args*/)
@ -581,7 +577,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (auto control{ weakThis.get() }) if (auto control{ weakThis.get() })
{ {
const auto chainHandle = _core->GetSwapChainHandle(); const HANDLE chainHandle = reinterpret_cast<HANDLE>(control->_core.SwapChainHandle());
_AttachDxgiSwapChainToXaml(chainHandle); _AttachDxgiSwapChainToXaml(chainHandle);
} }
} }
@ -658,23 +654,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// after Enable, then it'll be possible to paint the frame once // after Enable, then it'll be possible to paint the frame once
// _before_ the warning handler is set up, and then warnings from // _before_ the warning handler is set up, and then warnings from
// the first paint will be ignored! // the first paint will be ignored!
_core->RendererWarning({ get_weak(), &TermControl::_RendererWarning }); _core.RendererWarning({ get_weak(), &TermControl::_RendererWarning });
const auto coreInitialized = _core->Initialize(panelWidth, const auto coreInitialized = _core.Initialize(panelWidth,
panelHeight, panelHeight,
panelScaleX); panelScaleX);
if (!coreInitialized) if (!coreInitialized)
{ {
return false; return false;
} }
_interactivity->Initialize(); _interactivity.Initialize();
_AttachDxgiSwapChainToXaml(_core->GetSwapChainHandle()); _AttachDxgiSwapChainToXaml(reinterpret_cast<HANDLE>(_core.SwapChainHandle()));
// Tell the DX Engine to notify us when the swap chain changes. We do // Tell the DX Engine to notify us when the swap chain changes. We do
// this after we initially set the swapchain so as to avoid unnecessary // this after we initially set the swapchain so as to avoid unnecessary
// callbacks (and locking problems) // callbacks (and locking problems)
_core->SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged }); _core.SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged });
// !! LOAD BEARING !! // !! LOAD BEARING !!
// Make sure you enable painting _AFTER_ calling _AttachDxgiSwapChainToXaml // Make sure you enable painting _AFTER_ calling _AttachDxgiSwapChainToXaml
@ -684,9 +680,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// issues where the Renderer starts trying to paint before we've // issues where the Renderer starts trying to paint before we've
// actually attached the swapchain to anything, and the DxEngine is not // actually attached the swapchain to anything, and the DxEngine is not
// prepared to handle that. // prepared to handle that.
_core->EnablePainting(); _core.EnablePainting();
auto bufferHeight = _core->BufferHeight(); auto bufferHeight = _core.BufferHeight();
ScrollBar().Maximum(bufferHeight - bufferHeight); ScrollBar().Maximum(bufferHeight - bufferHeight);
ScrollBar().Minimum(0); ScrollBar().Minimum(0);
@ -763,7 +759,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
modifiers |= ControlKeyStates::EnhancedKey; modifiers |= ControlKeyStates::EnhancedKey;
} }
const bool handled = _core->SendCharEvent(ch, scanCode, modifiers); const bool handled = _core.SendCharEvent(ch, scanCode, modifiers);
e.Handled(handled); e.Handled(handled);
} }
@ -775,7 +771,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool TermControl::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) bool TermControl::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
{ {
// Short-circuit isReadOnly check to avoid warning dialog // Short-circuit isReadOnly check to avoid warning dialog
if (_core->IsInReadOnlyMode()) if (_core.IsInReadOnlyMode())
{ {
return false; return false;
} }
@ -861,7 +857,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode); const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
// Short-circuit isReadOnly check to avoid warning dialog // Short-circuit isReadOnly check to avoid warning dialog
if (_core->IsInReadOnlyMode()) if (_core.IsInReadOnlyMode())
{ {
e.Handled(!keyDown || _TryHandleKeyBinding(vkey, scanCode, modifiers)); e.Handled(!keyDown || _TryHandleKeyBinding(vkey, scanCode, modifiers));
return; return;
@ -993,17 +989,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// This will prevent the system from trying to get the character out // This will prevent the system from trying to get the character out
// of it and sending us a CharacterReceived event. // of it and sending us a CharacterReceived event.
const auto handled = vkey ? const auto handled = vkey ?
_core->TrySendKeyEvent(vkey, _core.TrySendKeyEvent(vkey,
scanCode, scanCode,
modifiers, modifiers,
keyDown) : keyDown) :
true; true;
if (_cursorTimer) if (_cursorTimer)
{ {
// Manually show the cursor when a key is pressed. Restarting // Manually show the cursor when a key is pressed. Restarting
// the timer prevents flickering. // the timer prevents flickering.
_core->CursorOn(true); _core.CursorOn(true);
_cursorTimer->Start(); _cursorTimer->Start();
} }
@ -1057,16 +1053,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
const auto contactRect = point.Properties().ContactRect(); const auto contactRect = point.Properties().ContactRect();
auto anchor = til::point{ til::math::rounding, contactRect.X, contactRect.Y }; auto anchor = til::point{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity->TouchPressed(anchor); _interactivity.TouchPressed(anchor);
} }
else else
{ {
const auto cursorPosition = point.Position(); const auto cursorPosition = point.Position();
_interactivity->PointerPressed(TermControl::GetPressedMouseButtons(point), _interactivity.PointerPressed(TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point), TermControl::GetPointerUpdateKind(point),
point.Timestamp(), point.Timestamp(),
ControlKeyStates{ args.KeyModifiers() }, ControlKeyStates{ args.KeyModifiers() },
_toTerminalOrigin(cursorPosition)); _toTerminalOrigin(cursorPosition));
} }
args.Handled(true); args.Handled(true);
@ -1101,11 +1097,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse || if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen) type == Windows::Devices::Input::PointerDeviceType::Pen)
{ {
_interactivity->PointerMoved(TermControl::GetPressedMouseButtons(point), _interactivity.PointerMoved(TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point), TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()), ControlKeyStates(args.KeyModifiers()),
_focused, _focused,
pixelPosition); pixelPosition);
if (_focused && point.Properties().IsLeftButtonPressed()) if (_focused && point.Properties().IsLeftButtonPressed())
{ {
@ -1138,7 +1134,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto contactRect = point.Properties().ContactRect(); const auto contactRect = point.Properties().ContactRect();
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y }; til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity->TouchMoved(newTouchPoint, _focused); _interactivity.TouchMoved(newTouchPoint, _focused);
} }
args.Handled(true); args.Handled(true);
@ -1169,14 +1165,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse || if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen) type == Windows::Devices::Input::PointerDeviceType::Pen)
{ {
_interactivity->PointerReleased(TermControl::GetPressedMouseButtons(point), _interactivity.PointerReleased(TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point), TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()), ControlKeyStates(args.KeyModifiers()),
pixelPosition); pixelPosition);
} }
else if (type == Windows::Devices::Input::PointerDeviceType::Touch) else if (type == Windows::Devices::Input::PointerDeviceType::Touch)
{ {
_interactivity->TouchReleased(); _interactivity.TouchReleased();
} }
_TryStopAutoScroll(ptr.PointerId()); _TryStopAutoScroll(ptr.PointerId());
@ -1204,10 +1200,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto point = args.GetCurrentPoint(*this); const auto point = args.GetCurrentPoint(*this);
auto result = _interactivity->MouseWheel(ControlKeyStates{ args.KeyModifiers() }, auto result = _interactivity.MouseWheel(ControlKeyStates{ args.KeyModifiers() },
point.Properties().MouseWheelDelta(), point.Properties().MouseWheelDelta(),
_toTerminalOrigin(point.Position()), _toTerminalOrigin(point.Position()),
TermControl::GetPressedMouseButtons(point)); TermControl::GetPressedMouseButtons(point));
if (result) if (result)
{ {
args.Handled(true); args.Handled(true);
@ -1231,10 +1227,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const bool rightButtonDown) const bool rightButtonDown)
{ {
const auto modifiers = _GetPressedModifierKeys(); const auto modifiers = _GetPressedModifierKeys();
TerminalInput::MouseButtonState state{ leftButtonDown,
midButtonDown, Control::MouseButtonState state{};
rightButtonDown }; WI_SetFlagIf(state, Control::MouseButtonState::IsLeftButtonDown, leftButtonDown);
return _interactivity->MouseWheel(modifiers, delta, _toTerminalOrigin(location), state); WI_SetFlagIf(state, Control::MouseButtonState::IsMiddleButtonDown, midButtonDown);
WI_SetFlagIf(state, Control::MouseButtonState::IsRightButtonDown, rightButtonDown);
return _interactivity.MouseWheel(modifiers, delta, _toTerminalOrigin(location), state);
} }
// Method Description: // Method Description:
@ -1282,7 +1281,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - none // - none
void TermControl::ResetFontSize() void TermControl::ResetFontSize()
{ {
_core->ResetFontSize(); _core.ResetFontSize();
} }
// Method Description: // Method Description:
@ -1291,7 +1290,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - fontSizeDelta: The amount to increase or decrease the font size by. // - fontSizeDelta: The amount to increase or decrease the font size by.
void TermControl::AdjustFontSize(int fontSizeDelta) void TermControl::AdjustFontSize(int fontSizeDelta)
{ {
_core->AdjustFontSize(fontSizeDelta); _core.AdjustFontSize(fontSizeDelta);
} }
void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& /*sender*/, void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& /*sender*/,
@ -1306,7 +1305,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
const auto newValue = args.NewValue(); const auto newValue = args.NewValue();
_interactivity->UpdateScrollbar(newValue); _interactivity.UpdateScrollbar(newValue);
// User input takes priority over terminal events so cancel // User input takes priority over terminal events so cancel
// any pending scroll bar update if the user scrolls. // any pending scroll bar update if the user scrolls.
@ -1452,10 +1451,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// GH#5421: Enable the UiaEngine before checking for the SearchBox // GH#5421: Enable the UiaEngine before checking for the SearchBox
// That way, new selections are notified to automation clients. // That way, new selections are notified to automation clients.
if (_uiaEngine.get()) // The _uiaEngine lives in _interactivity, so call into there to enable it.
{ _interactivity.GotFocus();
THROW_IF_FAILED(_uiaEngine->Enable());
}
// If the searchbox is focused, we don't want TSFInputControl to think // If the searchbox is focused, we don't want TSFInputControl to think
// it has focus so it doesn't intercept IME input. We also don't want the // it has focus so it doesn't intercept IME input. We also don't want the
@ -1473,7 +1470,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (_cursorTimer) if (_cursorTimer)
{ {
// When the terminal focuses, show the cursor immediately // When the terminal focuses, show the cursor immediately
_core->CursorOn(true); _core.CursorOn(true);
_cursorTimer->Start(); _cursorTimer->Start();
} }
@ -1482,8 +1479,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_blinkTimer->Start(); _blinkTimer->Start();
} }
_interactivity->GainFocus();
// Only update the appearance here if an unfocused config exists - // Only update the appearance here if an unfocused config exists -
// if an unfocused config does not exist then we never would have switched // if an unfocused config does not exist then we never would have switched
// appearances anyway so there's no need to switch back upon gaining focus // appearances anyway so there's no need to switch back upon gaining focus
@ -1509,10 +1504,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_focused = false; _focused = false;
if (_uiaEngine.get()) // This will disable the accessibility notifications, because the
{ // UiaEngine lives in ControlInteractivity
THROW_IF_FAILED(_uiaEngine->Disable()); _interactivity.LostFocus();
}
if (TSFInputControl() != nullptr) if (TSFInputControl() != nullptr)
{ {
@ -1522,7 +1516,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (_cursorTimer) if (_cursorTimer)
{ {
_cursorTimer->Stop(); _cursorTimer->Stop();
_core->CursorOn(false); _core.CursorOn(false);
} }
if (_blinkTimer) if (_blinkTimer)
@ -1552,7 +1546,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
const auto newSize = e.NewSize(); const auto newSize = e.NewSize();
_core->SizeChanged(newSize.Width, newSize.Height); _core.SizeChanged(newSize.Width, newSize.Height);
if (_automationPeer)
{
_automationPeer.UpdateControlBounds();
}
} }
// Method Description: // Method Description:
@ -1587,7 +1586,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
const auto scaleX = sender.CompositionScaleX(); const auto scaleX = sender.CompositionScaleX();
_core->ScaleChanged(scaleX); _core.ScaleChanged(scaleX);
} }
// Method Description: // Method Description:
@ -1600,7 +1599,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
if (!_IsClosing()) if (!_IsClosing())
{ {
_core->BlinkCursor(); _core.BlinkCursor();
} }
} }
@ -1614,7 +1613,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
if (!_IsClosing()) if (!_IsClosing())
{ {
_core->BlinkAttributeTick(); _core.BlinkAttributeTick();
} }
} }
@ -1624,7 +1623,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - cursorPosition: in pixels, relative to the origin of the control // - cursorPosition: in pixels, relative to the origin of the control
void TermControl::_SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition) void TermControl::_SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition)
{ {
_interactivity->SetEndSelectionPoint(_toTerminalOrigin(cursorPosition)); _interactivity.SetEndSelectionPoint(_toTerminalOrigin(cursorPosition));
} }
// Method Description: // Method Description:
@ -1664,7 +1663,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring TermControl::Title() hstring TermControl::Title()
{ {
return _core->Title(); return _core.Title();
} }
hstring TermControl::GetProfileName() const hstring TermControl::GetProfileName() const
@ -1674,12 +1673,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring TermControl::WorkingDirectory() const hstring TermControl::WorkingDirectory() const
{ {
return _core->WorkingDirectory(); return _core.WorkingDirectory();
} }
bool TermControl::BracketedPasteEnabled() const noexcept bool TermControl::BracketedPasteEnabled() const noexcept
{ {
return _core->BracketedPasteEnabled(); return _core.BracketedPasteEnabled();
} }
// Method Description: // Method Description:
@ -1697,14 +1696,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return false; return false;
} }
return _interactivity->CopySelectionToClipboard(singleLine, formats); return _interactivity.CopySelectionToClipboard(singleLine, formats);
} }
// Method Description: // Method Description:
// - Initiate a paste operation. // - Initiate a paste operation.
void TermControl::PasteTextFromClipboard() void TermControl::PasteTextFromClipboard()
{ {
_interactivity->RequestPasteTextFromClipboard(); _interactivity.RequestPasteTextFromClipboard();
} }
void TermControl::Close() void TermControl::Close()
@ -1713,13 +1712,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
_closing = true; _closing = true;
_core->ReceivedOutput(_coreOutputEventToken); _core.ReceivedOutput(_coreOutputEventToken);
_RestorePointerCursorHandlers(*this, nullptr); _RestorePointerCursorHandlers(*this, nullptr);
// Disconnect the TSF input control so it doesn't receive EditContext events. // Disconnect the TSF input control so it doesn't receive EditContext events.
TSFInputControl().Close(); TSFInputControl().Close();
_autoScrollTimer.Stop(); _autoScrollTimer.Stop();
_core->Close(); _core.Close();
} }
} }
@ -1732,9 +1731,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
ScrollBar().Value(viewTop); ScrollBar().Value(viewTop);
} }
int TermControl::ScrollOffset() int TermControl::ScrollOffset() const
{ {
return _core->ScrollOffset(); return _core.ScrollOffset();
} }
// Function Description: // Function Description:
@ -1743,7 +1742,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - The height of the terminal in lines of text // - The height of the terminal in lines of text
int TermControl::ViewHeight() const int TermControl::ViewHeight() const
{ {
return _core->ViewHeight(); return _core.ViewHeight();
}
int TermControl::BufferHeight() const
{
return _core.BufferHeight();
} }
// Function Description: // Function Description:
@ -1844,7 +1848,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
double height = rows * fontSize.Y; double height = rows * fontSize.Y;
auto thickness = _ParseThicknessFromPadding(padding); const auto thickness = ParseThicknessFromPadding(padding);
// GH#2061 - make sure to account for the size the padding _will be_ scaled to // GH#2061 - make sure to account for the size the padding _will be_ scaled to
width += scale * (thickness.Left + thickness.Right); width += scale * (thickness.Left + thickness.Right);
height += scale * (thickness.Top + thickness.Bottom); height += scale * (thickness.Top + thickness.Bottom);
@ -1862,8 +1866,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - The dimensions of a single character of this control, in DIPs // - The dimensions of a single character of this control, in DIPs
winrt::Windows::Foundation::Size TermControl::CharacterDimensions() const winrt::Windows::Foundation::Size TermControl::CharacterDimensions() const
{ {
const auto fontSize = _core->GetFont().GetSize(); return _core.FontSize();
return { gsl::narrow_cast<float>(fontSize.X), gsl::narrow_cast<float>(fontSize.Y) };
} }
// Method Description: // Method Description:
@ -1879,9 +1882,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
if (_initializedTerminal) if (_initializedTerminal)
{ {
const auto fontSize = _core->GetFont().GetSize(); const auto fontSize = _core.FontSize();
double width = fontSize.X; double width = fontSize.Width;
double height = fontSize.Y; double height = fontSize.Height;
// Reserve additional space if scrollbar is intended to be visible // Reserve additional space if scrollbar is intended to be visible
if (_settings.ScrollState() == ScrollbarState::Visible) if (_settings.ScrollState() == ScrollbarState::Visible)
{ {
@ -1924,8 +1927,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - A dimension that would be aligned to the character grid. // - A dimension that would be aligned to the character grid.
float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension) float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension)
{ {
const auto fontSize = _core->GetFont().GetSize(); const auto fontSize = _core.FontSize();
const auto fontDimension = widthOrHeight ? fontSize.X : fontSize.Y; const auto fontDimension = widthOrHeight ? fontSize.Width : fontSize.Height;
const auto padding = GetPadding(); const auto padding = GetPadding();
auto nonTerminalArea = gsl::narrow_cast<float>(widthOrHeight ? auto nonTerminalArea = gsl::narrow_cast<float>(widthOrHeight ?
@ -1952,7 +1955,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Four Double values provide independent padding for 4 sides of the bounding rectangle // Four Double values provide independent padding for 4 sides of the bounding rectangle
// Return Value: // Return Value:
// - Windows::UI::Xaml::Thickness object // - Windows::UI::Xaml::Thickness object
Windows::UI::Xaml::Thickness TermControl::_ParseThicknessFromPadding(const hstring padding) Windows::UI::Xaml::Thickness TermControl::ParseThicknessFromPadding(const hstring padding)
{ {
const wchar_t singleCharDelim = L','; const wchar_t singleCharDelim = L',';
std::wstringstream tokenStream(padding.c_str()); std::wstringstream tokenStream(padding.c_str());
@ -2085,7 +2088,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return; return;
} }
_core->SendInput(text); _core.SendInput(text);
} }
// Method Description: // Method Description:
@ -2105,7 +2108,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return; return;
} }
const til::point cursorPos = _core->CursorPosition(); const til::point cursorPos = _core.CursorPosition();
Windows::Foundation::Point p = { ::base::ClampedNumeric<float>(cursorPos.x()), Windows::Foundation::Point p = { ::base::ClampedNumeric<float>(cursorPos.x()),
::base::ClampedNumeric<float>(cursorPos.y()) }; ::base::ClampedNumeric<float>(cursorPos.y()) };
eventArgs.CurrentPosition(p); eventArgs.CurrentPosition(p);
@ -2121,11 +2124,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TermControl::_FontInfoHandler(const IInspectable& /*sender*/, void TermControl::_FontInfoHandler(const IInspectable& /*sender*/,
const FontInfoEventArgs& eventArgs) const FontInfoEventArgs& eventArgs)
{ {
const auto fontInfo = _core->GetFont();
eventArgs.FontSize(CharacterDimensions()); eventArgs.FontSize(CharacterDimensions());
eventArgs.FontFace(fontInfo.GetFaceName()); eventArgs.FontFace(_core.FontFaceName());
::winrt::Windows::UI::Text::FontWeight weight; ::winrt::Windows::UI::Text::FontWeight weight;
weight.Weight = static_cast<uint16_t>(fontInfo.GetWeight()); weight.Weight = _core.FontWeight();
eventArgs.FontWeight(weight); eventArgs.FontWeight(weight);
} }
@ -2167,7 +2169,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
try try
{ {
Windows::Foundation::Uri link{ co_await e.DataView().GetApplicationLinkAsync() }; Windows::Foundation::Uri link{ co_await e.DataView().GetApplicationLinkAsync() };
_core->PasteText(link.AbsoluteUri()); _core.PasteText(link.AbsoluteUri());
} }
CATCH_LOG(); CATCH_LOG();
} }
@ -2176,7 +2178,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
try try
{ {
Windows::Foundation::Uri link{ co_await e.DataView().GetWebLinkAsync() }; Windows::Foundation::Uri link{ co_await e.DataView().GetWebLinkAsync() };
_core->PasteText(link.AbsoluteUri()); _core.PasteText(link.AbsoluteUri());
} }
CATCH_LOG(); CATCH_LOG();
} }
@ -2185,7 +2187,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
try try
{ {
auto text{ co_await e.DataView().GetTextAsync() }; auto text{ co_await e.DataView().GetTextAsync() };
_core->PasteText(text); _core.PasteText(text);
} }
CATCH_LOG(); CATCH_LOG();
} }
@ -2226,7 +2228,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
allPaths += fullPath; allPaths += fullPath;
} }
_core->PasteText(winrt::hstring{ allPaths }); _core.PasteText(winrt::hstring{ allPaths });
} }
} }
} }
@ -2321,7 +2323,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
// It's already loaded if we get here, so just hide it. // It's already loaded if we get here, so just hide it.
RendererFailedNotice().Visibility(Visibility::Collapsed); RendererFailedNotice().Visibility(Visibility::Collapsed);
_core->ResumeRendering(); _core.ResumeRendering();
} }
IControlSettings TermControl::Settings() const IControlSettings TermControl::Settings() const
@ -2340,25 +2342,25 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// hypothetical future where we allow an application to set the tab // hypothetical future where we allow an application to set the tab
// color with VT sequences like they're currently allowed to with the // color with VT sequences like they're currently allowed to with the
// title. // title.
return _core->TabColor(); return _core.TabColor();
} }
// Method Description: // Method Description:
// - Gets the internal taskbar state value // - Gets the internal taskbar state value
// Return Value: // Return Value:
// - The taskbar state of this control // - The taskbar state of this control
const size_t TermControl::TaskbarState() const noexcept const uint64_t TermControl::TaskbarState() const noexcept
{ {
return _core->TaskbarState(); return _core.TaskbarState();
} }
// Method Description: // Method Description:
// - Gets the internal taskbar progress value // - Gets the internal taskbar progress value
// Return Value: // Return Value:
// - The taskbar progress of this control // - The taskbar progress of this control
const size_t TermControl::TaskbarProgress() const noexcept const uint64_t TermControl::TaskbarProgress() const noexcept
{ {
return _core->TaskbarProgress(); return _core.TaskbarProgress();
} }
void TermControl::BellLightOn() void TermControl::BellLightOn()
@ -2418,15 +2420,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - True if the mode is read-only // - True if the mode is read-only
bool TermControl::ReadOnly() const noexcept bool TermControl::ReadOnly() const noexcept
{ {
return _core->IsInReadOnlyMode(); return _core.IsInReadOnlyMode();
} }
// Method Description: // Method Description:
// - Toggles the read-only flag, raises event describing the value change // - Toggles the read-only flag, raises event describing the value change
void TermControl::ToggleReadOnly() void TermControl::ToggleReadOnly()
{ {
_core->ToggleReadOnlyMode(); _core.ToggleReadOnlyMode();
_ReadOnlyChangedHandlers(*this, winrt::box_value(_core->IsInReadOnlyMode())); _ReadOnlyChangedHandlers(*this, winrt::box_value(_core.IsInReadOnlyMode()));
} }
// Method Description: // Method Description:
@ -2435,9 +2437,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Arguments: // Arguments:
// - sender: not used // - sender: not used
// - args: event data // - args: event data
void TermControl::_PointerExitedHandler(Windows::Foundation::IInspectable const& /*sender*/, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& /*e*/) void TermControl::_PointerExitedHandler(Windows::Foundation::IInspectable const& /*sender*/,
Windows::UI::Xaml::Input::PointerRoutedEventArgs const& /*e*/)
{ {
_core->UpdateHoveredCell(std::nullopt); _core.ClearHoveredCell();
} }
winrt::fire_and_forget TermControl::_hoveredHyperlinkChanged(IInspectable sender, winrt::fire_and_forget TermControl::_hoveredHyperlinkChanged(IInspectable sender,
@ -2447,10 +2450,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
co_await resume_foreground(Dispatcher()); co_await resume_foreground(Dispatcher());
if (auto self{ weakThis.get() }) if (auto self{ weakThis.get() })
{ {
auto lastHoveredCell = _core->GetHoveredCell(); auto lastHoveredCell = _core.HoveredCell();
if (lastHoveredCell) if (lastHoveredCell)
{ {
const auto uriText = _core->GetHoveredUriText(); const auto uriText = _core.HoveredUriText();
if (!uriText.empty()) if (!uriText.empty())
{ {
// Update the tooltip with the URI // Update the tooltip with the URI
@ -2465,8 +2468,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Compute the location of the top left corner of the cell in DIPS // Compute the location of the top left corner of the cell in DIPS
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top }; const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
const til::point startPos{ *lastHoveredCell }; const til::point startPos{ lastHoveredCell.Value() };
const til::size fontSize{ _core->GetFont().GetSize() }; const til::size fontSize{ til::math::rounding, _core.FontSize() };
const til::point posInPixels{ startPos * fontSize }; const til::point posInPixels{ startPos * fontSize };
const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() }; const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() };
const til::point locationInDIPs{ posInDIPs + marginsInDips }; const til::point locationInDIPs{ posInDIPs + marginsInDips };
@ -2502,11 +2505,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_RaiseNoticeHandlers(*this, eventArgs); _RaiseNoticeHandlers(*this, eventArgs);
} }
TerminalInput::MouseButtonState TermControl::GetPressedMouseButtons(const winrt::Windows::UI::Input::PointerPoint point) Control::MouseButtonState TermControl::GetPressedMouseButtons(const winrt::Windows::UI::Input::PointerPoint point)
{ {
return TerminalInput::MouseButtonState{ point.Properties().IsLeftButtonPressed(), Control::MouseButtonState state{};
point.Properties().IsMiddleButtonPressed(), WI_SetFlagIf(state, Control::MouseButtonState::IsLeftButtonDown, point.Properties().IsLeftButtonPressed());
point.Properties().IsRightButtonPressed() }; WI_SetFlagIf(state, Control::MouseButtonState::IsMiddleButtonDown, point.Properties().IsMiddleButtonPressed());
WI_SetFlagIf(state, Control::MouseButtonState::IsRightButtonDown, point.Properties().IsRightButtonPressed());
return state;
} }
unsigned int TermControl::GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point) unsigned int TermControl::GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point)

View file

@ -40,8 +40,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension); float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
#pragma region ICoreState #pragma region ICoreState
const size_t TaskbarState() const noexcept; const uint64_t TaskbarState() const noexcept;
const size_t TaskbarProgress() const noexcept; const uint64_t TaskbarProgress() const noexcept;
hstring Title(); hstring Title();
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() noexcept; Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() noexcept;
@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TerminalConnection::ConnectionState ConnectionState() const; TerminalConnection::ConnectionState ConnectionState() const;
int ScrollOffset(); int ScrollOffset() const;
int ViewHeight() const; int ViewHeight() const;
int BufferHeight() const; int BufferHeight() const;
@ -84,7 +84,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
~TermControl(); ~TermControl();
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer(); Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
::Microsoft::Console::Types::IUiaData* GetUiaData() const;
const Windows::UI::Xaml::Thickness GetPadding(); const Windows::UI::Xaml::Thickness GetPadding();
IControlSettings Settings() const; IControlSettings Settings() const;
@ -104,19 +103,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ReadOnly() const noexcept; bool ReadOnly() const noexcept;
void ToggleReadOnly(); void ToggleReadOnly();
static ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState GetPressedMouseButtons(const winrt::Windows::UI::Input::PointerPoint point); static Control::MouseButtonState GetPressedMouseButtons(const winrt::Windows::UI::Input::PointerPoint point);
static unsigned int GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point); static unsigned int GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point);
static Windows::UI::Xaml::Thickness ParseThicknessFromPadding(const hstring padding);
// -------------------------------- WinRT Events --------------------------------- // -------------------------------- WinRT Events ---------------------------------
// clang-format off // clang-format off
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs); WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs, _core, CopyToClipboard); PROJECTED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs, _core, CopyToClipboard);
FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged); PROJECTED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged);
FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged); PROJECTED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged);
FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged); PROJECTED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged);
FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged); PROJECTED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged);
FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard);
PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard);
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs); TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs);
@ -141,11 +142,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown. // IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown.
// (C++ class members are destroyed in reverse order.) // (C++ class members are destroyed in reverse order.)
// Further, the TermControlAutomationPeer must be destructed after _uiaEngine! // Further, the TermControlAutomationPeer must be destructed after _uiaEngine!
winrt::Windows::UI::Xaml::Automation::Peers::AutomationPeer _automationPeer{ nullptr }; Control::TermControlAutomationPeer _automationPeer{ nullptr };
std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine; Control::ControlInteractivity _interactivity{ nullptr };
Control::ControlCore _core{ nullptr };
winrt::com_ptr<ControlCore> _core;
winrt::com_ptr<ControlInteractivity> _interactivity;
winrt::com_ptr<SearchBoxControl> _searchBox; winrt::com_ptr<SearchBoxControl> _searchBox;
IControlSettings _settings; IControlSettings _settings;
@ -240,8 +240,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _TryStopAutoScroll(const uint32_t pointerId); void _TryStopAutoScroll(const uint32_t pointerId);
void _UpdateAutoScroll(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e); void _UpdateAutoScroll(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
static Windows::UI::Xaml::Thickness _ParseThicknessFromPadding(const hstring padding);
void _KeyHandler(Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e, const bool keyDown); void _KeyHandler(Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e, const bool keyDown);
::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() const; ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() const;
bool _TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const; bool _TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const;

View file

@ -30,13 +30,42 @@ namespace XamlAutomation
namespace winrt::Microsoft::Terminal::Control::implementation namespace winrt::Microsoft::Terminal::Control::implementation
{ {
TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::Control::implementation::TermControl* owner) : TermControlAutomationPeer::TermControlAutomationPeer(TermControl* owner,
Control::InteractivityAutomationPeer impl) :
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner), // pass owner to FrameworkElementAutomationPeer TermControlAutomationPeerT<TermControlAutomationPeer>(*owner), // pass owner to FrameworkElementAutomationPeer
_termControl{ owner } _termControl{ owner },
_contentAutomationPeer{ impl }
{ {
THROW_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, _termControl->GetUiaData(), this)); UpdateControlBounds();
// Listen for UIA signalling events from the implementation. We need to
// be the one to actually raise these automation events, so they go
// through the UI tree correctly.
_contentAutomationPeer.SelectionChanged([this](auto&&, auto&&) { SignalSelectionChanged(); });
_contentAutomationPeer.TextChanged([this](auto&&, auto&&) { SignalTextChanged(); });
_contentAutomationPeer.CursorChanged([this](auto&&, auto&&) { SignalCursorChanged(); });
}; };
// Method Description:
// - Inform the interactivity layer about the bounds of the control.
// IControlAccessibilityInfo needs to know this information, but it cannot
// ask us directly.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TermControlAutomationPeer::UpdateControlBounds()
{
// FrameworkElementAutomationPeer has this great GetBoundingRectangle
// method that's seemingly impossible to recreate just from the
// UserControl itself. Weird. But we can use it handily here!
_contentAutomationPeer.SetControlBounds(GetBoundingRectangle());
}
void TermControlAutomationPeer::SetControlPadding(const Core::Padding padding)
{
_contentAutomationPeer.SetControlPadding(padding);
}
// Method Description: // Method Description:
// - Signals the ui automation client that the terminal's selection has changed and should be updated // - Signals the ui automation client that the terminal's selection has changed and should be updated
// Arguments: // Arguments:
@ -167,106 +196,36 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma region ITextProvider #pragma region ITextProvider
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection() com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection()
{ {
SAFEARRAY* pReturnVal; return _contentAutomationPeer.GetSelection();
THROW_IF_FAILED(_uiaProvider->GetSelection(&pReturnVal));
return WrapArrayOfTextRangeProviders(pReturnVal);
} }
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges() com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges()
{ {
SAFEARRAY* pReturnVal; return _contentAutomationPeer.GetVisibleRanges();
THROW_IF_FAILED(_uiaProvider->GetVisibleRanges(&pReturnVal));
return WrapArrayOfTextRangeProviders(pReturnVal);
} }
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement) XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement)
{ {
UIA::ITextRangeProvider* returnVal; return _contentAutomationPeer.RangeFromChild(childElement);
// ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr
THROW_IF_FAILED(_uiaProvider->RangeFromChild(/* IRawElementProviderSimple */ nullptr,
&returnVal));
auto parentProvider = this->ProviderFromPeer(*this);
auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
} }
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation) XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation)
{ {
UIA::ITextRangeProvider* returnVal; return _contentAutomationPeer.RangeFromPoint(screenLocation);
THROW_IF_FAILED(_uiaProvider->RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal));
auto parentProvider = this->ProviderFromPeer(*this);
auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
} }
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::DocumentRange() XamlAutomation::ITextRangeProvider TermControlAutomationPeer::DocumentRange()
{ {
UIA::ITextRangeProvider* returnVal; return _contentAutomationPeer.DocumentRange();
THROW_IF_FAILED(_uiaProvider->get_DocumentRange(&returnVal));
auto parentProvider = this->ProviderFromPeer(*this);
auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
} }
XamlAutomation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection() XamlAutomation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection()
{ {
UIA::SupportedTextSelection returnVal; return _contentAutomationPeer.SupportedTextSelection();
THROW_IF_FAILED(_uiaProvider->get_SupportedTextSelection(&returnVal));
return static_cast<XamlAutomation::SupportedTextSelection>(returnVal);
} }
#pragma endregion #pragma endregion
#pragma region IControlAccessibilityInfo
COORD TermControlAutomationPeer::GetFontSize() const
{
return _termControl->GetFontSize();
}
RECT TermControlAutomationPeer::GetBounds() const
{
auto rect = GetBoundingRectangle();
return {
gsl::narrow_cast<LONG>(rect.X),
gsl::narrow_cast<LONG>(rect.Y),
gsl::narrow_cast<LONG>(rect.X + rect.Width),
gsl::narrow_cast<LONG>(rect.Y + rect.Height)
};
}
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:

View file

@ -20,13 +20,17 @@ Abstract:
Author(s): Author(s):
- Carlos Zamora (CaZamor) 2019 - Carlos Zamora (CaZamor) 2019
Modifications:
- May 2021: Pulled the core logic of ITextProvider implementation into the
InteractivityAutomationPeer, to support tab tear out.
--*/ --*/
#pragma once #pragma once
#include "TermControl.h" #include "TermControl.h"
#include "ControlInteractivity.h"
#include "TermControlAutomationPeer.g.h" #include "TermControlAutomationPeer.g.h"
#include <winrt/Microsoft.Terminal.Control.h>
#include "../types/TermControlUiaProvider.hpp" #include "../types/TermControlUiaProvider.hpp"
#include "../types/IUiaEventDispatcher.h" #include "../types/IUiaEventDispatcher.h"
#include "../types/IControlAccessibilityInfo.h" #include "../types/IControlAccessibilityInfo.h"
@ -35,11 +39,14 @@ namespace winrt::Microsoft::Terminal::Control::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(Microsoft::Terminal::Control::implementation::TermControl* owner); TermControlAutomationPeer(Microsoft::Terminal::Control::implementation::TermControl* owner,
Control::InteractivityAutomationPeer implementation);
void UpdateControlBounds();
void SetControlPadding(const Core::Padding padding);
#pragma region FrameworkElementAutomationPeer #pragma region FrameworkElementAutomationPeer
hstring GetClassNameCore() const; hstring GetClassNameCore() const;
@ -67,21 +74,10 @@ namespace winrt::Microsoft::Terminal::Control::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();
private: private:
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::Control::implementation::TermControl* _termControl; winrt::Microsoft::Terminal::Control::implementation::TermControl* _termControl;
Control::InteractivityAutomationPeer _contentAutomationPeer;
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

@ -9,5 +9,8 @@ namespace Microsoft.Terminal.Control
Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer, Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer,
Windows.UI.Xaml.Automation.Provider.ITextProvider Windows.UI.Xaml.Automation.Provider.ITextProvider
{ {
void UpdateControlBounds();
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
} }
} }

View file

@ -52,6 +52,9 @@
<ClInclude Include="TermControlAutomationPeer.h"> <ClInclude Include="TermControlAutomationPeer.h">
<DependentUpon>TermControlAutomationPeer.idl</DependentUpon> <DependentUpon>TermControlAutomationPeer.idl</DependentUpon>
</ClInclude> </ClInclude>
<ClInclude Include="InteractivityAutomationPeer.h">
<DependentUpon>InteractivityAutomationPeer.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TSFInputControl.h"> <ClInclude Include="TSFInputControl.h">
<DependentUpon>TSFInputControl.xaml</DependentUpon> <DependentUpon>TSFInputControl.xaml</DependentUpon>
</ClInclude> </ClInclude>
@ -91,6 +94,9 @@
<ClCompile Include="TermControlAutomationPeer.cpp"> <ClCompile Include="TermControlAutomationPeer.cpp">
<DependentUpon>TermControlAutomationPeer.idl</DependentUpon> <DependentUpon>TermControlAutomationPeer.idl</DependentUpon>
</ClCompile> </ClCompile>
<ClCompile Include="InteractivityAutomationPeer.cpp">
<DependentUpon>InteractivityAutomationPeer.idl</DependentUpon>
</ClCompile>
<ClCompile Include="XamlUiaTextRange.cpp" /> <ClCompile Include="XamlUiaTextRange.cpp" />
</ItemGroup> </ItemGroup>
<!-- ========================= idl Files ======================== --> <!-- ========================= idl Files ======================== -->
@ -112,6 +118,7 @@
<DependentUpon>TermControl.xaml</DependentUpon> <DependentUpon>TermControl.xaml</DependentUpon>
</Midl> </Midl>
<Midl Include="TermControlAutomationPeer.idl" /> <Midl Include="TermControlAutomationPeer.idl" />
<Midl Include="InteractivityAutomationPeer.idl" />
<Midl Include="IMouseWheelListener.idl" /> <Midl Include="IMouseWheelListener.idl" />
<Midl Include="TSFInputControl.idl"> <Midl Include="TSFInputControl.idl">
<DependentUpon>TSFInputControl.xaml</DependentUpon> <DependentUpon>TSFInputControl.xaml</DependentUpon>

View file

@ -57,6 +57,8 @@
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalControlProvider); TRACELOGGING_DECLARE_PROVIDER(g_hTerminalControlProvider);
#include <telemetry/ProjectTelemetry.h> #include <telemetry/ProjectTelemetry.h>
#include <WinUser.h>
#include "til.h" #include "til.h"
#include "ThrottledFunc.h" #include "ThrottledFunc.h"

View file

@ -82,6 +82,20 @@ public:
} }
#endif #endif
#ifdef WINRT_Microsoft_Terminal_Core_H
constexpr ControlKeyStates(const winrt::Microsoft::Terminal::Core::ControlKeyStates& projKeyStates) :
ControlKeyStates(projKeyStates.Value)
{
}
operator winrt::Microsoft::Terminal::Core::ControlKeyStates() const noexcept
{
winrt::Microsoft::Terminal::Core::ControlKeyStates ret;
ret.Value = _value;
return ret;
}
#endif
constexpr DWORD Value() const noexcept constexpr DWORD Value() const noexcept
{ {
return _value; return _value;

View file

@ -24,11 +24,38 @@ namespace Microsoft.Terminal.Core
UInt8 A; UInt8 A;
}; };
// TerminalCore declares its own Color struct to avoid depending on
// Windows.UI. Windows.Foundation.Point also exists, but it's composed of
// floating-point coordinates, when we almost always need integer coordinates.
// It is supported by til::point for conversions in and out of WinRT land.
struct Point
{
Int32 X;
Int32 Y;
};
// Same thing here, but with padding. Can't use Windows.UI.Thickness, so
// we'll declare our own.
struct Padding {
Double Left;
Double Top;
Double Right;
Double Bottom;
};
// This is a projection of Microsoft::Terminal::Core::ControlKeyStates,
// for conversions in and out of WinRT land.
struct ControlKeyStates
{
UInt32 Value;
};
declare declare
{ {
// Forward declare this parameterized specialization so that it lives // Forward declare this parameterized specialization so that it lives
// in TerminalCore instead of being flung to the winds of all IDL dependents. // in TerminalCore instead of being flung to the winds of all IDL dependents.
interface Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>; interface Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>;
interface Windows.Foundation.IReference<Microsoft.Terminal.Core.Point>;
} }
interface ICoreAppearance interface ICoreAppearance

View file

@ -118,6 +118,7 @@ namespace ControlUnitTests
core->TransparencyChanged(opacityCallback); core->TransparencyChanged(opacityCallback);
const auto modifiers = ControlKeyStates(ControlKeyStates::RightCtrlPressed | ControlKeyStates::ShiftPressed); const auto modifiers = ControlKeyStates(ControlKeyStates::RightCtrlPressed | ControlKeyStates::ShiftPressed);
const Control::MouseButtonState buttonState{};
Log::Comment(L"Scroll in the positive direction, increasing opacity"); Log::Comment(L"Scroll in the positive direction, increasing opacity");
// Scroll more than enough times to get to 1.0 from .5. // Scroll more than enough times to get to 1.0 from .5.
@ -134,7 +135,7 @@ namespace ControlUnitTests
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
30, 30,
til::point{ 0, 0 }, til::point{ 0, 0 },
{ false, false, false }); buttonState);
} }
Log::Comment(L"Scroll in the negative direction, decreasing opacity"); Log::Comment(L"Scroll in the negative direction, decreasing opacity");
@ -152,7 +153,7 @@ namespace ControlUnitTests
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
30, 30,
til::point{ 0, 0 }, til::point{ 0, 0 },
{ false, false, false }); buttonState);
} }
} }
@ -197,6 +198,7 @@ namespace ControlUnitTests
VERIFY_ARE_EQUAL(41, core->BufferHeight()); VERIFY_ARE_EQUAL(41, core->BufferHeight());
Log::Comment(L"Scroll up a line"); Log::Comment(L"Scroll up a line");
const Control::MouseButtonState buttonState{};
const auto modifiers = ControlKeyStates(); const auto modifiers = ControlKeyStates();
expectedBufferHeight = 41; expectedBufferHeight = 41;
expectedTop = 20; expectedTop = 20;
@ -204,7 +206,7 @@ namespace ControlUnitTests
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
WHEEL_DELTA, WHEEL_DELTA,
til::point{ 0, 0 }, til::point{ 0, 0 },
{ false, false, false }); buttonState);
Log::Comment(L"Scroll up 19 more times, to the top"); Log::Comment(L"Scroll up 19 more times, to the top");
for (int i = 0; i < 20; ++i) for (int i = 0; i < 20; ++i)
@ -213,18 +215,18 @@ namespace ControlUnitTests
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
WHEEL_DELTA, WHEEL_DELTA,
til::point{ 0, 0 }, til::point{ 0, 0 },
{ false, false, false }); buttonState);
} }
Log::Comment(L"Scrolling up more should do nothing"); Log::Comment(L"Scrolling up more should do nothing");
expectedTop = 0; expectedTop = 0;
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
WHEEL_DELTA, WHEEL_DELTA,
til::point{ 0, 0 }, til::point{ 0, 0 },
{ false, false, false }); buttonState);
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
WHEEL_DELTA, WHEEL_DELTA,
til::point{ 0, 0 }, til::point{ 0, 0 },
{ false, false, false }); buttonState);
Log::Comment(L"Scroll down 21 more times, to the bottom"); Log::Comment(L"Scroll down 21 more times, to the bottom");
for (int i = 0; i < 21; ++i) for (int i = 0; i < 21; ++i)
@ -234,7 +236,7 @@ namespace ControlUnitTests
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
-WHEEL_DELTA, -WHEEL_DELTA,
til::point{ 0, 0 }, til::point{ 0, 0 },
{ false, false, false }); buttonState);
Log::Comment(NoThrowString().Format(L"internal scrollbar pos:%f", interactivity->_internalScrollbarPosition)); Log::Comment(NoThrowString().Format(L"internal scrollbar pos:%f", interactivity->_internalScrollbarPosition));
} }
Log::Comment(L"Scrolling up more should do nothing"); Log::Comment(L"Scrolling up more should do nothing");
@ -242,11 +244,11 @@ namespace ControlUnitTests
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
-WHEEL_DELTA, -WHEEL_DELTA,
til::point{ 0, 0 }, til::point{ 0, 0 },
{ false, false, false }); buttonState);
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
-WHEEL_DELTA, -WHEEL_DELTA,
til::point{ 0, 0 }, til::point{ 0, 0 },
{ false, false, false }); buttonState);
} }
void ControlInteractivityTests::CreateSubsequentSelectionWithDragging() void ControlInteractivityTests::CreateSubsequentSelectionWithDragging()
@ -260,8 +262,8 @@ namespace ControlUnitTests
// For this test, don't use any modifiers // For this test, don't use any modifiers
const auto modifiers = ControlKeyStates(); const auto modifiers = ControlKeyStates();
const TerminalInput::MouseButtonState leftMouseDown{ true, false, false }; const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const TerminalInput::MouseButtonState noMouseDown{ false, false, false }; const Control::MouseButtonState noMouseDown{};
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };
@ -358,8 +360,8 @@ namespace ControlUnitTests
// For this test, don't use any modifiers // For this test, don't use any modifiers
const auto modifiers = ControlKeyStates(); const auto modifiers = ControlKeyStates();
const TerminalInput::MouseButtonState leftMouseDown{ true, false, false }; const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const TerminalInput::MouseButtonState noMouseDown{ false, false, false }; const Control::MouseButtonState noMouseDown{};
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };
@ -401,7 +403,7 @@ namespace ControlUnitTests
interactivity->MouseWheel(modifiers, interactivity->MouseWheel(modifiers,
WHEEL_DELTA, WHEEL_DELTA,
cursorPosition1, cursorPosition1,
{ true, false, false }); leftMouseDown);
Log::Comment(L"Verify the location of the selection"); Log::Comment(L"Verify the location of the selection");
// The viewport is now on row 20, so the selection will be on: // The viewport is now on row 20, so the selection will be on:
@ -444,7 +446,7 @@ namespace ControlUnitTests
const int delta = WHEEL_DELTA / 5; const int delta = WHEEL_DELTA / 5;
const til::point mousePos{ 0, 0 }; const til::point mousePos{ 0, 0 };
TerminalInput::MouseButtonState state{ false, false, false }; Control::MouseButtonState state{};
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5 interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5
VERIFY_ARE_EQUAL(21, core->ScrollOffset()); VERIFY_ARE_EQUAL(21, core->ScrollOffset());
@ -508,8 +510,8 @@ namespace ControlUnitTests
// For this test, don't use any modifiers // For this test, don't use any modifiers
const auto modifiers = ControlKeyStates(); const auto modifiers = ControlKeyStates();
const TerminalInput::MouseButtonState leftMouseDown{ true, false, false }; const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const TerminalInput::MouseButtonState noMouseDown{ false, false, false }; const Control::MouseButtonState noMouseDown{};
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };

View file

@ -96,6 +96,12 @@ public:
winrt::event_token name(Windows::Foundation::TypedEventHandler<sender, args> const& h) { return handler->handlerName(h); } \ winrt::event_token name(Windows::Foundation::TypedEventHandler<sender, args> const& h) { return handler->handlerName(h); } \
void name(winrt::event_token const& token) noexcept { handler->handlerName(token); } void name(winrt::event_token const& token) noexcept { handler->handlerName(token); }
// Same thing, but handler is a projected type, not an implementation
#define PROJECTED_FORWARDED_TYPED_EVENT(name, sender, args, handler, handlerName) \
public: \
winrt::event_token name(Windows::Foundation::TypedEventHandler<sender, args> const& h) { return handler.handlerName(h); } \
void name(winrt::event_token const& token) noexcept { handler.handlerName(token); }
// Use this macro to quick implement both the getter and setter for a property. // Use this macro to quick implement both the getter and setter for a property.
// This should only be used for simple types where there's no logic in the // This should only be used for simple types where there's no logic in the
// getter/setter beyond just accessing/updating the value. // getter/setter beyond just accessing/updating the value.

View file

@ -338,6 +338,21 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
} }
#endif #endif
#ifdef WINRT_Microsoft_Terminal_Core_H
constexpr point(const winrt::Microsoft::Terminal::Core::Point& corePoint) :
point(corePoint.X, corePoint.Y)
{
}
operator winrt::Microsoft::Terminal::Core::Point() const
{
winrt::Microsoft::Terminal::Core::Point ret;
ret.X = x<int>();
ret.Y = y<int>();
return ret;
}
#endif
std::wstring to_string() const std::wstring to_string() const
{ {
return wil::str_printf<std::wstring>(L"(X:%td, Y:%td)", x(), y()); return wil::str_printf<std::wstring>(L"(X:%td, Y:%td)", x(), y());

View file

@ -183,6 +183,14 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{ {
} }
// This template will convert to rectangle from anything that has a X, Y, Width, and Height field that are floating-point;
// a math type is required.
template<typename TilMath, typename TOther>
constexpr rectangle(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().X)> && std::is_floating_point_v<decltype(std::declval<TOther>().Y)> && std::is_floating_point_v<decltype(std::declval<TOther>().Width)> && std::is_floating_point_v<decltype(std::declval<TOther>().Height)>, int> /*sentinel*/ = 0) :
rectangle(til::point{ TilMath::template cast<ptrdiff_t>(other.X), TilMath::template cast<ptrdiff_t>(other.Y) }, til::size{ TilMath::template cast<ptrdiff_t>(other.Width), TilMath::template cast<ptrdiff_t>(other.Height) })
{
}
// This template will convert to rectangle from anything that has a left, top, right, and bottom field that are floating-point; // This template will convert to rectangle from anything that has a left, top, right, and bottom field that are floating-point;
// a math type is required. // a math type is required.
template<typename TilMath, typename TOther> template<typename TilMath, typename TOther>
@ -875,6 +883,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
} }
#endif #endif
#ifdef WINRT_Microsoft_Terminal_Core_H
operator winrt::Microsoft::Terminal::Core::Padding() const
{
winrt::Microsoft::Terminal::Core::Padding ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret.Left));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret.Top));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(right()).AssignIfValid(&ret.Right));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(bottom()).AssignIfValid(&ret.Bottom));
return ret;
}
constexpr rectangle(const winrt::Microsoft::Terminal::Core::Padding& padding) :
rectangle(til::math::rounding, padding)
{
}
#endif
std::wstring to_string() const std::wstring to_string() const
{ {
return wil::str_printf<std::wstring>(L"(L:%td, T:%td, R:%td, B:%td) [W:%td, H:%td]", left(), top(), right(), bottom(), width(), height()); return wil::str_printf<std::wstring>(L"(L:%td, T:%td, R:%td, B:%td) [W:%td, H:%td]", left(), top(), right(), bottom(), width(), height());