diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 76ff21745c9..441fbd4deff 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1129,7 +1129,7 @@ namespace winrt::TerminalApp::implementation // - Gets the taskbar state value from the last active control // Return Value: // - The taskbar state of the last active control - size_t AppLogic::GetLastActiveControlTaskbarState() + uint64_t AppLogic::GetLastActiveControlTaskbarState() { if (_root) { @@ -1142,7 +1142,7 @@ namespace winrt::TerminalApp::implementation // - Gets the taskbar progress value from the last active control // Return Value: // - The taskbar progress of the last active control - size_t AppLogic::GetLastActiveControlTaskbarProgress() + uint64_t AppLogic::GetLastActiveControlTaskbarProgress() { if (_root) { diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index dfbe27a4402..3d778203921 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -89,8 +89,8 @@ namespace winrt::TerminalApp::implementation void WindowCloseButtonClicked(); - size_t GetLastActiveControlTaskbarState(); - size_t GetLastActiveControlTaskbarProgress(); + uint64_t GetLastActiveControlTaskbarState(); + uint64_t GetLastActiveControlTaskbarProgress(); winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index a5eb4c5ddf7..cacacb8c3fc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2020,11 +2020,11 @@ namespace winrt::TerminalApp::implementation // - Gets the taskbar state value from the last active control // Return Value: // - The taskbar state of the last active control - size_t TerminalPage::GetLastActiveControlTaskbarState() + uint64_t TerminalPage::GetLastActiveControlTaskbarState() { if (auto control{ _GetActiveControl() }) { - return gsl::narrow_cast(control.TaskbarState()); + return control.TaskbarState(); } return {}; } @@ -2033,11 +2033,11 @@ namespace winrt::TerminalApp::implementation // - Gets the taskbar progress value from the last active control // Return Value: // - The taskbar progress of the last active control - size_t TerminalPage::GetLastActiveControlTaskbarProgress() + uint64_t TerminalPage::GetLastActiveControlTaskbarProgress() { if (auto control{ _GetActiveControl() }) { - return gsl::narrow_cast(control.TaskbarProgress()); + return control.TaskbarProgress(); } return {}; } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 306e44c7335..414c233fdec 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -83,8 +83,8 @@ namespace winrt::TerminalApp::implementation winrt::TerminalApp::IDialogPresenter DialogPresenter() const; void DialogPresenter(winrt::TerminalApp::IDialogPresenter dialogPresenter); - size_t GetLastActiveControlTaskbarState(); - size_t GetLastActiveControlTaskbarProgress(); + uint64_t GetLastActiveControlTaskbarState(); + uint64_t GetLastActiveControlTaskbarProgress(); void ShowKeyboardServiceWarning(); winrt::hstring KeyboardServiceDisabledText(); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 932042a8b1f..27724fa7cc9 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -426,7 +426,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - Updates last hovered cell, renders / removes rendering of hyper-link if required // Arguments: // - terminalPosition: The terminal position of the pointer - void ControlCore::UpdateHoveredCell(const std::optional& terminalPosition) + void ControlCore::SetHoveredCell(Core::Point pos) + { + _updateHoveredCell(std::optional{ pos }); + } + void ControlCore::ClearHoveredCell() + { + _updateHoveredCell(std::nullopt); + } + + void ControlCore::_updateHoveredCell(const std::optional terminalPosition) { if (terminalPosition == _lastHoveredCell) { @@ -477,7 +486,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation 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. if (_lastHoveredCell.has_value()) @@ -487,9 +496,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation return {}; } - std::optional ControlCore::GetHoveredCell() const + Windows::Foundation::IReference ControlCore::HoveredCell() const { - return _lastHoveredCell; + return _lastHoveredCell.has_value() ? Windows::Foundation::IReference{ _lastHoveredCell.value() } : nullptr; } // Method Description: @@ -895,6 +904,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _actualFont; } + winrt::Windows::Foundation::Size ControlCore::FontSize() const noexcept + { + const auto fontSize = GetFont().GetSize(); + return { + ::base::saturated_cast(fontSize.X), + ::base::saturated_cast(fontSize.Y) + }; + } + winrt::hstring ControlCore::FontFaceName() const noexcept + { + return winrt::hstring{ GetFont().GetFaceName() }; + } + + uint16_t ControlCore::FontWeight() const noexcept + { + return static_cast(GetFont().GetWeight()); + } + til::size ControlCore::FontSizeInDips() const { const til::size fontSize{ GetFont().GetSize() }; @@ -1077,10 +1104,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _settings.CopyOnSelect(); } - std::vector ControlCore::SelectedText(bool trimTrailingWhitespace) const + Windows::Foundation::Collections::IVector ControlCore::SelectedText(bool trimTrailingWhitespace) const { // 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(); + + for (const auto& row : internalResult) + { + result.Append(winrt::hstring{ row }); + } + return result; } ::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) { @@ -1176,7 +1211,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - HANDLE ControlCore::GetSwapChainHandle() const + uint64_t ControlCore::SwapChainHandle() const { // This is called by: // * TermControl::RenderEngineSwapChainChanged, who is only registered @@ -1184,7 +1219,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // * TermControl::_InitializeTerminal, after the call to Initialize, for // _AttachDxgiSwapChainToXaml. // In both cases, we'll have a _renderEngine by then. - return _renderEngine->GetSwapChainHandle(); + return reinterpret_cast(_renderEngine->GetSwapChainHandle()); } void ControlCore::_rendererWarning(const HRESULT hr) diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index e4941e00a5e..855aa66d398 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -48,15 +48,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation void UpdateAppearance(const IControlAppearance& newAppearance); void SizeChanged(const double width, const double height); void ScaleChanged(const double scale); - HANDLE GetSwapChainHandle() const; + uint64_t SwapChainHandle() const; void AdjustFontSize(int fontSizeDelta); void ResetFontSize(); FontInfo GetFont() 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; - void SetBackgroundOpacity(const float opacity); + void SetBackgroundOpacity(const double opacity); void SendInput(const winrt::hstring& wstr); void PasteText(const winrt::hstring& hstr); @@ -67,10 +71,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ResumeRendering(); void UpdatePatternLocations(); - void UpdateHoveredCell(const std::optional& terminalPosition); + void SetHoveredCell(Core::Point terminalPosition); + void ClearHoveredCell(); winrt::hstring GetHyperlink(const til::point position) const; - winrt::hstring GetHoveredUriText() const; - std::optional GetHoveredCell() const; + winrt::hstring HoveredUriText() const; + Windows::Foundation::IReference HoveredCell() const; ::Microsoft::Console::Types::IUiaData* GetUiaData() const; @@ -119,7 +124,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool HasSelection() const; bool CopyOnSelect() const; - std::vector SelectedText(bool trimTrailingWhitespace) const; + Windows::Foundation::Collections::IVector SelectedText(bool trimTrailingWhitespace) const; void SetSelectionAnchor(til::point const& position); void SetEndSelectionPoint(til::point const& position); @@ -232,6 +237,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _raiseReadOnlyWarning(); void _updateAntiAliasingMode(::Microsoft::Console::Render::DxEngine* const dxEngine); void _connectionOutputHandler(const hstring& hstr); + void _updateHoveredCell(const std::optional terminalPosition); friend class ControlUnitTests::ControlCoreTests; friend class ControlUnitTests::ControlInteractivityTests; diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index c3074000f75..fa4746ca404 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -8,9 +8,97 @@ import "EventArgs.idl"; 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 { ControlCore(IControlSettings settings, 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 SelectedText(Boolean trimTrailingWhitespace); + + String HoveredUriText { get; }; + Windows.Foundation.IReference HoveredCell { get; }; + + void Close(); + void BlinkCursor(); + Boolean IsInReadOnlyMode { get; }; + Boolean CursorOn; + void EnablePainting(); + + event FontSizeChangedEventArgs FontSizeChanged; + + event Windows.Foundation.TypedEventHandler CopyToClipboard; + event Windows.Foundation.TypedEventHandler TitleChanged; + event Windows.Foundation.TypedEventHandler WarningBell; + event Windows.Foundation.TypedEventHandler TabColorChanged; + event Windows.Foundation.TypedEventHandler BackgroundColorChanged; + event Windows.Foundation.TypedEventHandler ScrollPositionChanged; + event Windows.Foundation.TypedEventHandler CursorPositionChanged; + event Windows.Foundation.TypedEventHandler TaskbarProgressChanged; + event Windows.Foundation.TypedEventHandler ConnectionStateChanged; + event Windows.Foundation.TypedEventHandler HoveredHyperlinkChanged; + event Windows.Foundation.TypedEventHandler RendererEnteredErrorState; + event Windows.Foundation.TypedEventHandler SwapChainChanged; + event Windows.Foundation.TypedEventHandler RendererWarning; + event Windows.Foundation.TypedEventHandler RaiseNotice; + event Windows.Foundation.TypedEventHandler TransparencyChanged; + event Windows.Foundation.TypedEventHandler ReceivedOutput; + }; } diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 433150cabb5..a08640a22fb 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -8,13 +8,15 @@ #include #include #include -#include #include #include "../../types/inc/GlyphWidth.hpp" #include "../../types/inc/Utils.hpp" #include "../../buffer/out/search.h" +#include "InteractivityAutomationPeer.h" + #include "ControlInteractivity.g.cpp" +#include "TermControl.h" using namespace ::Microsoft::Console::Types; using namespace ::Microsoft::Console::VirtualTerminal; @@ -27,6 +29,15 @@ static constexpr unsigned int MAX_CLICK_COUNT = 3; 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, TerminalConnection::ITerminalConnection connection) : _touchAnchor{ std::nullopt }, @@ -37,6 +48,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core = winrt::make_self(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: + // - + // Return Value: + // - void ControlInteractivity::UpdateSettings() { _updateSystemParameterSettings(); @@ -50,9 +69,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation _multiClickTimer = GetDoubleClickTime() * 1000; } - winrt::com_ptr ControlInteractivity::GetCore() + Control::ControlCore ControlInteractivity::Core() { - return _core; + return *_core; } // Method Description: @@ -85,11 +104,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _multiClickCounter; } - void ControlInteractivity::GainFocus() + void ControlInteractivity::GotFocus() { + if (_uiaEngine.get()) + { + THROW_IF_FAILED(_uiaEngine->Enable()); + } + _updateSystemParameterSettings(); } + void ControlInteractivity::LostFocus() + { + if (_uiaEngine.get()) + { + THROW_IF_FAILED(_uiaEngine->Disable()); + } + } + // Method Description // - Updates internal params based on system parameters void ControlInteractivity::_updateSystemParameterSettings() noexcept @@ -156,7 +188,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core->PasteText(winrt::hstring{ wstr }); } - void ControlInteractivity::PointerPressed(TerminalInput::MouseButtonState buttonState, + void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState, const unsigned int pointerUpdateKind, const uint64_t timestamp, 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 auto hyperlink = _core->GetHyperlink(terminalPosition); - if (buttonState.isLeftButtonDown && + if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) && ctrlEnabled && !hyperlink.empty()) { const auto clickCount = _numberOfClicks(pixelPosition, timestamp); @@ -182,9 +214,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation } 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); // This formula enables the number of clicks to cycle properly @@ -219,7 +251,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _singleClickTouchdownPos = std::nullopt; } } - else if (buttonState.isRightButtonDown) + else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown)) { // CopyOnSelect right click always pastes if (_core->CopyOnSelect() || !_core->HasSelection()) @@ -238,7 +270,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _touchAnchor = contactPoint; } - void ControlInteractivity::PointerMoved(TerminalInput::MouseButtonState buttonState, + void ControlInteractivity::PointerMoved(Control::MouseButtonState buttonState, const unsigned int pointerUpdateKind, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool focused, @@ -249,9 +281,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Short-circuit isReadOnly check to avoid warning dialog 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) { @@ -279,7 +311,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation SetEndSelectionPoint(pixelPosition); } - _core->UpdateHoveredCell(terminalPosition); + _core->SetHoveredCell(terminalPosition); } 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 ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const til::point pixelPosition) @@ -328,7 +360,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Short-circuit isReadOnly check to avoid warning dialog if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers)) { - _core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState); + _core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, toInternalMouseState(buttonState)); return; } @@ -366,7 +398,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool ControlInteractivity::MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta, const til::point pixelPosition, - const TerminalInput::MouseButtonState state) + const Control::MouseButtonState buttonState) { const til::point terminalPosition = _getTerminalPosition(pixelPosition); @@ -382,7 +414,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation WM_MOUSEWHEEL, modifiers, ::base::saturated_cast(delta), - state); + toInternalMouseState(buttonState)); } const auto ctrlPressed = modifiers.IsCtrlPressed(); @@ -398,7 +430,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } else { - _mouseScrollHandler(delta, pixelPosition, state.isLeftButtonDown); + _mouseScrollHandler(delta, pixelPosition, WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown)); } return false; } @@ -557,4 +589,36 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Convert the location in pixels to characters within the current viewport. 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(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(); + } + } diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index f684f2dfff3..1c86a52d692 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -23,11 +23,6 @@ #include "ControlCore.h" -namespace Microsoft::Console::VirtualTerminal -{ - struct MouseButtonState; -} - namespace ControlUnitTests { class ControlCoreTests; @@ -42,20 +37,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation ControlInteractivity(IControlSettings settings, TerminalConnection::ITerminalConnection connection); - void GainFocus(); + void GotFocus(); + void LostFocus(); void UpdateSettings(); void Initialize(); - winrt::com_ptr GetCore(); + Control::ControlCore Core(); + + Control::InteractivityAutomationPeer OnCreateAutomationPeer(); + ::Microsoft::Console::Types::IUiaData* GetUiaData() const; #pragma region Input Methods - void PointerPressed(::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState buttonState, + void PointerPressed(Control::MouseButtonState buttonState, const unsigned int pointerUpdateKind, const uint64_t timestamp, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const til::point pixelPosition); void TouchPressed(const til::point contactPoint); - void PointerMoved(::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState buttonState, + void PointerMoved(Control::MouseButtonState buttonState, const unsigned int pointerUpdateKind, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool focused, @@ -63,7 +62,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TouchMoved(const til::point newTouchPoint, const bool focused); - void PointerReleased(::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState buttonState, + void PointerReleased(Control::MouseButtonState buttonState, const unsigned int pointerUpdateKind, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const til::point pixelPosition); @@ -72,7 +71,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta, const til::point pixelPosition, - const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state); + const Control::MouseButtonState state); void UpdateScrollbar(const double newValue); @@ -83,7 +82,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation void RequestPasteTextFromClipboard(); 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: + // 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 _core{ nullptr }; unsigned int _rowsToScroll; double _internalScrollbarPosition{ 0.0 }; @@ -129,10 +141,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _sendPastedTextToConnection(std::wstring_view wstr); 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::ControlInteractivityTests; }; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index 3a9afaa1409..385eefdbd5c 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -5,6 +5,8 @@ import "ICoreState.idl"; import "IControlSettings.idl"; import "ControlCore.idl"; import "EventArgs.idl"; +import "InteractivityAutomationPeer.idl"; + namespace Microsoft.Terminal.Control { @@ -13,5 +15,51 @@ namespace Microsoft.Terminal.Control { ControlInteractivity(IControlSettings settings, 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 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 OpenHyperlink; + event Windows.Foundation.TypedEventHandler ScrollPositionChanged; + event Windows.Foundation.TypedEventHandler PasteFromClipboard; + + }; } diff --git a/src/cascadia/TerminalControl/ICoreState.idl b/src/cascadia/TerminalControl/ICoreState.idl index a21c0747c3e..ad5217bf666 100644 --- a/src/cascadia/TerminalControl/ICoreState.idl +++ b/src/cascadia/TerminalControl/ICoreState.idl @@ -17,6 +17,7 @@ namespace Microsoft.Terminal.Control Int32 ScrollOffset { get; }; Int32 ViewHeight { get; }; + Int32 BufferHeight { get; }; Boolean BracketedPasteEnabled { get; }; diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp b/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp new file mode 100644 index 00000000000..9d028b48444 --- /dev/null +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include +#include +#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: + // - + // Return Value: + // - + 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: + // - + // Return Value: + // - + 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: + // - + // Return Value: + // - + void InteractivityAutomationPeer::SignalCursorChanged() + { + _CursorChangedHandlers(*this, nullptr); + } + +#pragma region ITextProvider + com_array InteractivityAutomationPeer::GetSelection() + { + SAFEARRAY* pReturnVal; + THROW_IF_FAILED(_uiaProvider->GetSelection(&pReturnVal)); + return WrapArrayOfTextRangeProviders(pReturnVal); + } + + com_array 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(returnVal, parentProvider); + return xutr.as(); + } + + 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(returnVal, parentProvider); + return xutr.as(); + } + + 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(returnVal, parentProvider); + return xutr.as(); + } + + XamlAutomation::SupportedTextSelection InteractivityAutomationPeer::SupportedTextSelection() + { + UIA::SupportedTextSelection returnVal; + THROW_IF_FAILED(_uiaProvider->get_SupportedTextSelection(&returnVal)); + return static_cast(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 InteractivityAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges) + { + // transfer ownership of UiaTextRanges to this new vector + auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(textRanges); + int count = gsl::narrow(providers.size()); + + std::vector vec; + vec.reserve(count); + auto parentProvider = this->ProviderFromPeer(*this); + for (int i = 0; i < count; i++) + { + auto xutr = make_self(providers[i].detach(), parentProvider); + vec.emplace_back(xutr.as()); + } + + com_array result{ vec }; + + return result; + } +} diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.h b/src/cascadia/TerminalControl/InteractivityAutomationPeer.h new file mode 100644 index 00000000000..893478e9192 --- /dev/null +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.h @@ -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, + ::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 GetVisibleRanges(); + com_array 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 WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges); + }; +} diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.idl b/src/cascadia/TerminalControl/InteractivityAutomationPeer.idl new file mode 100644 index 00000000000..4c9039c5c82 --- /dev/null +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.idl @@ -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 SelectionChanged; + event Windows.Foundation.TypedEventHandler TextChanged; + event Windows.Foundation.TypedEventHandler CursorChanged; + } +} diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 1894c7b65b9..1b873bf3c06 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include "../../types/inc/GlyphWidth.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::MouseButtonState); + namespace winrt::Microsoft::Terminal::Control::implementation { TermControl::TermControl(IControlSettings settings, @@ -60,33 +61,33 @@ namespace winrt::Microsoft::Terminal::Control::implementation { InitializeComponent(); - _interactivity = winrt::make_self(settings, connection); - _core = _interactivity->GetCore(); + _interactivity = winrt::make(settings, connection); + _core = _interactivity.Core(); // Use a manual revoker on the output event, so we can immediately stop // 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 // should be drained and closed before we complete destruction. So these // are safe. - _core->ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); - _core->WarningBell({ this, &TermControl::_coreWarningBell }); - _core->CursorPositionChanged({ this, &TermControl::_CursorPositionChanged }); + _core.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); + _core.WarningBell({ this, &TermControl::_coreWarningBell }); + _core.CursorPositionChanged({ this, &TermControl::_CursorPositionChanged }); // 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 // they don't need weak refs - they can't be triggered unless we're // alive. - _core->BackgroundColorChanged({ this, &TermControl::_BackgroundColorChangedHandler }); - _core->FontSizeChanged({ this, &TermControl::_coreFontSizeChanged }); - _core->TransparencyChanged({ this, &TermControl::_coreTransparencyChanged }); - _core->RaiseNotice({ this, &TermControl::_coreRaisedNotice }); - _core->HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged }); - _interactivity->OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); - _interactivity->ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); + _core.BackgroundColorChanged({ this, &TermControl::_BackgroundColorChangedHandler }); + _core.FontSizeChanged({ this, &TermControl::_coreFontSizeChanged }); + _core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged }); + _core.RaiseNotice({ this, &TermControl::_coreRaisedNotice }); + _core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged }); + _interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); + _interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); // 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 @@ -128,7 +129,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation [weakThis = get_weak()]() { 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 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. // Empirically, multi-line selection works as well on sample scenarios, // but since code paths differ, extra work is required to ensure correctness. - auto bufferText = _core->SelectedText(true); - if (bufferText.size() == 1) + auto bufferText = _core.SelectedText(true); + if (bufferText.Size() == 1) { - const auto selectedLine{ til::at(bufferText, 0) }; - _searchBox->PopulateTextbox(selectedLine.data()); + const auto selectedLine{ bufferText.GetAt(0) }; + _searchBox->PopulateTextbox(selectedLine); } } @@ -217,7 +218,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } 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 caseSensitive) { - _core->Search(text, goForward, caseSensitive); + _core.Search(text, goForward, caseSensitive); } // Method Description: @@ -303,7 +304,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - _core->UpdateSettings(_settings); + _core.UpdateSettings(_settings); // Update our control settings _ApplyUISettings(_settings); @@ -362,7 +363,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation foregroundBrush.Color(static_cast(newAppearance.DefaultForeground())); TSFInputControl().Foreground(foregroundBrush); - _core->UpdateAppearance(newAppearance); + _core.UpdateAppearance(newAppearance); } // Method Description: @@ -373,12 +374,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void TermControl::SendInput(const winrt::hstring& wstr) { - _core->SendInput(wstr); + _core.SendInput(wstr); } void TermControl::ToggleShaderEffects() { - _core->ToggleShaderEffects(); + _core.ToggleShaderEffects(); } // Method Description: @@ -401,7 +402,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _changeBackgroundColor(bg); // Apply padding as swapChainPanel's margin - auto newMargin = _ParseThicknessFromPadding(newSettings.Padding()); + auto newMargin = ParseThicknessFromPadding(newSettings.Padding()); SwapChainPanel().Margin(newMargin); TSFInputControl().Margin(newMargin); @@ -422,7 +423,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation ScrollBar().Visibility(Visibility::Visible); } - _interactivity->UpdateSettings(); + _interactivity.UpdateSettings(); + if (_automationPeer) + { + _automationPeer.SetControlPadding(Core::Padding{ newMargin.Left, + newMargin.Top, + newMargin.Right, + newMargin.Bottom }); + } } // 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. - _core->SetBackgroundOpacity(::base::saturated_cast(_settings.TintOpacity())); + _core.SetBackgroundOpacity(::base::saturated_cast(_settings.TintOpacity())); } else { @@ -479,7 +487,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation RootGrid().Background(solidColor); // 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*/, const IInspectable& /*args*/) { - til::color newBgColor{ _core->BackgroundColor() }; + til::color newBgColor{ _core.BackgroundColor() }; _changeBackgroundColor(newBgColor); } @@ -527,37 +535,25 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Return Value: // - The automation peer for our control 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 { // create a custom automation peer with this code pattern: // (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers) - auto autoPeer = winrt::make_self(this); - - _uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get()); - _core->AttachUiaEngine(_uiaEngine.get()); - _automationPeer = *autoPeer; - return _automationPeer; + if (const auto& interactivityAutoPeer = _interactivity.OnCreateAutomationPeer()) + { + _automationPeer = winrt::make(this, interactivityAutoPeer); + return _automationPeer; + } } 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 // clever way around asking the core for this. 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() @@ -567,7 +563,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TerminalConnection::ConnectionState TermControl::ConnectionState() const { - return _core->ConnectionState(); + return _core.ConnectionState(); } 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() }) { - const auto chainHandle = _core->GetSwapChainHandle(); + const HANDLE chainHandle = reinterpret_cast(control->_core.SwapChainHandle()); _AttachDxgiSwapChainToXaml(chainHandle); } } @@ -658,23 +654,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation // after Enable, then it'll be possible to paint the frame once // _before_ the warning handler is set up, and then warnings from // the first paint will be ignored! - _core->RendererWarning({ get_weak(), &TermControl::_RendererWarning }); + _core.RendererWarning({ get_weak(), &TermControl::_RendererWarning }); - const auto coreInitialized = _core->Initialize(panelWidth, - panelHeight, - panelScaleX); + const auto coreInitialized = _core.Initialize(panelWidth, + panelHeight, + panelScaleX); if (!coreInitialized) { return false; } - _interactivity->Initialize(); + _interactivity.Initialize(); - _AttachDxgiSwapChainToXaml(_core->GetSwapChainHandle()); + _AttachDxgiSwapChainToXaml(reinterpret_cast(_core.SwapChainHandle())); // 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 // callbacks (and locking problems) - _core->SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged }); + _core.SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged }); // !! LOAD BEARING !! // 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 // actually attached the swapchain to anything, and the DxEngine is not // prepared to handle that. - _core->EnablePainting(); + _core.EnablePainting(); - auto bufferHeight = _core->BufferHeight(); + auto bufferHeight = _core.BufferHeight(); ScrollBar().Maximum(bufferHeight - bufferHeight); ScrollBar().Minimum(0); @@ -763,7 +759,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { modifiers |= ControlKeyStates::EnhancedKey; } - const bool handled = _core->SendCharEvent(ch, scanCode, modifiers); + const bool handled = _core.SendCharEvent(ch, scanCode, modifiers); 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) { // Short-circuit isReadOnly check to avoid warning dialog - if (_core->IsInReadOnlyMode()) + if (_core.IsInReadOnlyMode()) { return false; } @@ -861,7 +857,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto scanCode = gsl::narrow_cast(e.KeyStatus().ScanCode); // Short-circuit isReadOnly check to avoid warning dialog - if (_core->IsInReadOnlyMode()) + if (_core.IsInReadOnlyMode()) { e.Handled(!keyDown || _TryHandleKeyBinding(vkey, scanCode, modifiers)); return; @@ -993,17 +989,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation // This will prevent the system from trying to get the character out // of it and sending us a CharacterReceived event. const auto handled = vkey ? - _core->TrySendKeyEvent(vkey, - scanCode, - modifiers, - keyDown) : + _core.TrySendKeyEvent(vkey, + scanCode, + modifiers, + keyDown) : true; if (_cursorTimer) { // Manually show the cursor when a key is pressed. Restarting // the timer prevents flickering. - _core->CursorOn(true); + _core.CursorOn(true); _cursorTimer->Start(); } @@ -1057,16 +1053,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation { const auto contactRect = point.Properties().ContactRect(); auto anchor = til::point{ til::math::rounding, contactRect.X, contactRect.Y }; - _interactivity->TouchPressed(anchor); + _interactivity.TouchPressed(anchor); } else { const auto cursorPosition = point.Position(); - _interactivity->PointerPressed(TermControl::GetPressedMouseButtons(point), - TermControl::GetPointerUpdateKind(point), - point.Timestamp(), - ControlKeyStates{ args.KeyModifiers() }, - _toTerminalOrigin(cursorPosition)); + _interactivity.PointerPressed(TermControl::GetPressedMouseButtons(point), + TermControl::GetPointerUpdateKind(point), + point.Timestamp(), + ControlKeyStates{ args.KeyModifiers() }, + _toTerminalOrigin(cursorPosition)); } args.Handled(true); @@ -1101,11 +1097,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (type == Windows::Devices::Input::PointerDeviceType::Mouse || type == Windows::Devices::Input::PointerDeviceType::Pen) { - _interactivity->PointerMoved(TermControl::GetPressedMouseButtons(point), - TermControl::GetPointerUpdateKind(point), - ControlKeyStates(args.KeyModifiers()), - _focused, - pixelPosition); + _interactivity.PointerMoved(TermControl::GetPressedMouseButtons(point), + TermControl::GetPointerUpdateKind(point), + ControlKeyStates(args.KeyModifiers()), + _focused, + pixelPosition); if (_focused && point.Properties().IsLeftButtonPressed()) { @@ -1138,7 +1134,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto contactRect = point.Properties().ContactRect(); til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y }; - _interactivity->TouchMoved(newTouchPoint, _focused); + _interactivity.TouchMoved(newTouchPoint, _focused); } args.Handled(true); @@ -1169,14 +1165,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (type == Windows::Devices::Input::PointerDeviceType::Mouse || type == Windows::Devices::Input::PointerDeviceType::Pen) { - _interactivity->PointerReleased(TermControl::GetPressedMouseButtons(point), - TermControl::GetPointerUpdateKind(point), - ControlKeyStates(args.KeyModifiers()), - pixelPosition); + _interactivity.PointerReleased(TermControl::GetPressedMouseButtons(point), + TermControl::GetPointerUpdateKind(point), + ControlKeyStates(args.KeyModifiers()), + pixelPosition); } else if (type == Windows::Devices::Input::PointerDeviceType::Touch) { - _interactivity->TouchReleased(); + _interactivity.TouchReleased(); } _TryStopAutoScroll(ptr.PointerId()); @@ -1204,10 +1200,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto point = args.GetCurrentPoint(*this); - auto result = _interactivity->MouseWheel(ControlKeyStates{ args.KeyModifiers() }, - point.Properties().MouseWheelDelta(), - _toTerminalOrigin(point.Position()), - TermControl::GetPressedMouseButtons(point)); + auto result = _interactivity.MouseWheel(ControlKeyStates{ args.KeyModifiers() }, + point.Properties().MouseWheelDelta(), + _toTerminalOrigin(point.Position()), + TermControl::GetPressedMouseButtons(point)); if (result) { args.Handled(true); @@ -1231,10 +1227,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation const bool rightButtonDown) { const auto modifiers = _GetPressedModifierKeys(); - TerminalInput::MouseButtonState state{ leftButtonDown, - midButtonDown, - rightButtonDown }; - return _interactivity->MouseWheel(modifiers, delta, _toTerminalOrigin(location), state); + + Control::MouseButtonState state{}; + WI_SetFlagIf(state, Control::MouseButtonState::IsLeftButtonDown, leftButtonDown); + WI_SetFlagIf(state, Control::MouseButtonState::IsMiddleButtonDown, midButtonDown); + WI_SetFlagIf(state, Control::MouseButtonState::IsRightButtonDown, rightButtonDown); + + return _interactivity.MouseWheel(modifiers, delta, _toTerminalOrigin(location), state); } // Method Description: @@ -1282,7 +1281,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - none void TermControl::ResetFontSize() { - _core->ResetFontSize(); + _core.ResetFontSize(); } // Method Description: @@ -1291,7 +1290,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - fontSizeDelta: The amount to increase or decrease the font size by. void TermControl::AdjustFontSize(int fontSizeDelta) { - _core->AdjustFontSize(fontSizeDelta); + _core.AdjustFontSize(fontSizeDelta); } void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& /*sender*/, @@ -1306,7 +1305,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } const auto newValue = args.NewValue(); - _interactivity->UpdateScrollbar(newValue); + _interactivity.UpdateScrollbar(newValue); // User input takes priority over terminal events so cancel // 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 // That way, new selections are notified to automation clients. - if (_uiaEngine.get()) - { - THROW_IF_FAILED(_uiaEngine->Enable()); - } + // The _uiaEngine lives in _interactivity, so call into there to enable it. + _interactivity.GotFocus(); // 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 @@ -1473,7 +1470,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (_cursorTimer) { // When the terminal focuses, show the cursor immediately - _core->CursorOn(true); + _core.CursorOn(true); _cursorTimer->Start(); } @@ -1482,8 +1479,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _blinkTimer->Start(); } - _interactivity->GainFocus(); - // Only update the appearance here if an unfocused config exists - // 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 @@ -1509,10 +1504,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation _focused = false; - if (_uiaEngine.get()) - { - THROW_IF_FAILED(_uiaEngine->Disable()); - } + // This will disable the accessibility notifications, because the + // UiaEngine lives in ControlInteractivity + _interactivity.LostFocus(); if (TSFInputControl() != nullptr) { @@ -1522,7 +1516,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (_cursorTimer) { _cursorTimer->Stop(); - _core->CursorOn(false); + _core.CursorOn(false); } if (_blinkTimer) @@ -1552,7 +1546,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation } const auto newSize = e.NewSize(); - _core->SizeChanged(newSize.Width, newSize.Height); + _core.SizeChanged(newSize.Width, newSize.Height); + + if (_automationPeer) + { + _automationPeer.UpdateControlBounds(); + } } // Method Description: @@ -1587,7 +1586,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { const auto scaleX = sender.CompositionScaleX(); - _core->ScaleChanged(scaleX); + _core.ScaleChanged(scaleX); } // Method Description: @@ -1600,7 +1599,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (!_IsClosing()) { - _core->BlinkCursor(); + _core.BlinkCursor(); } } @@ -1614,7 +1613,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { 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 void TermControl::_SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition) { - _interactivity->SetEndSelectionPoint(_toTerminalOrigin(cursorPosition)); + _interactivity.SetEndSelectionPoint(_toTerminalOrigin(cursorPosition)); } // Method Description: @@ -1664,7 +1663,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring TermControl::Title() { - return _core->Title(); + return _core.Title(); } hstring TermControl::GetProfileName() const @@ -1674,12 +1673,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring TermControl::WorkingDirectory() const { - return _core->WorkingDirectory(); + return _core.WorkingDirectory(); } bool TermControl::BracketedPasteEnabled() const noexcept { - return _core->BracketedPasteEnabled(); + return _core.BracketedPasteEnabled(); } // Method Description: @@ -1697,14 +1696,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation return false; } - return _interactivity->CopySelectionToClipboard(singleLine, formats); + return _interactivity.CopySelectionToClipboard(singleLine, formats); } // Method Description: // - Initiate a paste operation. void TermControl::PasteTextFromClipboard() { - _interactivity->RequestPasteTextFromClipboard(); + _interactivity.RequestPasteTextFromClipboard(); } void TermControl::Close() @@ -1713,13 +1712,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _closing = true; - _core->ReceivedOutput(_coreOutputEventToken); + _core.ReceivedOutput(_coreOutputEventToken); _RestorePointerCursorHandlers(*this, nullptr); // Disconnect the TSF input control so it doesn't receive EditContext events. TSFInputControl().Close(); _autoScrollTimer.Stop(); - _core->Close(); + _core.Close(); } } @@ -1732,9 +1731,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation ScrollBar().Value(viewTop); } - int TermControl::ScrollOffset() + int TermControl::ScrollOffset() const { - return _core->ScrollOffset(); + return _core.ScrollOffset(); } // Function Description: @@ -1743,7 +1742,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - The height of the terminal in lines of text int TermControl::ViewHeight() const { - return _core->ViewHeight(); + return _core.ViewHeight(); + } + + int TermControl::BufferHeight() const + { + return _core.BufferHeight(); } // Function Description: @@ -1844,7 +1848,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } 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 width += scale * (thickness.Left + thickness.Right); 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 winrt::Windows::Foundation::Size TermControl::CharacterDimensions() const { - const auto fontSize = _core->GetFont().GetSize(); - return { gsl::narrow_cast(fontSize.X), gsl::narrow_cast(fontSize.Y) }; + return _core.FontSize(); } // Method Description: @@ -1879,9 +1882,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (_initializedTerminal) { - const auto fontSize = _core->GetFont().GetSize(); - double width = fontSize.X; - double height = fontSize.Y; + const auto fontSize = _core.FontSize(); + double width = fontSize.Width; + double height = fontSize.Height; // Reserve additional space if scrollbar is intended to be 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. float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension) { - const auto fontSize = _core->GetFont().GetSize(); - const auto fontDimension = widthOrHeight ? fontSize.X : fontSize.Y; + const auto fontSize = _core.FontSize(); + const auto fontDimension = widthOrHeight ? fontSize.Width : fontSize.Height; const auto padding = GetPadding(); auto nonTerminalArea = gsl::narrow_cast(widthOrHeight ? @@ -1952,7 +1955,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Four Double values provide independent padding for 4 sides of the bounding rectangle // Return Value: // - 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','; std::wstringstream tokenStream(padding.c_str()); @@ -2085,7 +2088,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - _core->SendInput(text); + _core.SendInput(text); } // Method Description: @@ -2105,7 +2108,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - const til::point cursorPos = _core->CursorPosition(); + const til::point cursorPos = _core.CursorPosition(); Windows::Foundation::Point p = { ::base::ClampedNumeric(cursorPos.x()), ::base::ClampedNumeric(cursorPos.y()) }; eventArgs.CurrentPosition(p); @@ -2121,11 +2124,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::_FontInfoHandler(const IInspectable& /*sender*/, const FontInfoEventArgs& eventArgs) { - const auto fontInfo = _core->GetFont(); eventArgs.FontSize(CharacterDimensions()); - eventArgs.FontFace(fontInfo.GetFaceName()); + eventArgs.FontFace(_core.FontFaceName()); ::winrt::Windows::UI::Text::FontWeight weight; - weight.Weight = static_cast(fontInfo.GetWeight()); + weight.Weight = _core.FontWeight(); eventArgs.FontWeight(weight); } @@ -2167,7 +2169,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation try { Windows::Foundation::Uri link{ co_await e.DataView().GetApplicationLinkAsync() }; - _core->PasteText(link.AbsoluteUri()); + _core.PasteText(link.AbsoluteUri()); } CATCH_LOG(); } @@ -2176,7 +2178,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation try { Windows::Foundation::Uri link{ co_await e.DataView().GetWebLinkAsync() }; - _core->PasteText(link.AbsoluteUri()); + _core.PasteText(link.AbsoluteUri()); } CATCH_LOG(); } @@ -2185,7 +2187,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation try { auto text{ co_await e.DataView().GetTextAsync() }; - _core->PasteText(text); + _core.PasteText(text); } CATCH_LOG(); } @@ -2226,7 +2228,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation 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. RendererFailedNotice().Visibility(Visibility::Collapsed); - _core->ResumeRendering(); + _core.ResumeRendering(); } 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 // color with VT sequences like they're currently allowed to with the // title. - return _core->TabColor(); + return _core.TabColor(); } // Method Description: // - Gets the internal taskbar state value // Return Value: // - 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: // - Gets the internal taskbar progress value // Return Value: // - 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() @@ -2418,15 +2420,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - True if the mode is read-only bool TermControl::ReadOnly() const noexcept { - return _core->IsInReadOnlyMode(); + return _core.IsInReadOnlyMode(); } // Method Description: // - Toggles the read-only flag, raises event describing the value change void TermControl::ToggleReadOnly() { - _core->ToggleReadOnlyMode(); - _ReadOnlyChangedHandlers(*this, winrt::box_value(_core->IsInReadOnlyMode())); + _core.ToggleReadOnlyMode(); + _ReadOnlyChangedHandlers(*this, winrt::box_value(_core.IsInReadOnlyMode())); } // Method Description: @@ -2435,9 +2437,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Arguments: // - sender: not used // - 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, @@ -2447,10 +2450,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation co_await resume_foreground(Dispatcher()); if (auto self{ weakThis.get() }) { - auto lastHoveredCell = _core->GetHoveredCell(); + auto lastHoveredCell = _core.HoveredCell(); if (lastHoveredCell) { - const auto uriText = _core->GetHoveredUriText(); + const auto uriText = _core.HoveredUriText(); if (!uriText.empty()) { // 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 const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top }; - const til::point startPos{ *lastHoveredCell }; - const til::size fontSize{ _core->GetFont().GetSize() }; + const til::point startPos{ lastHoveredCell.Value() }; + const til::size fontSize{ til::math::rounding, _core.FontSize() }; const til::point posInPixels{ startPos * fontSize }; const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() }; const til::point locationInDIPs{ posInDIPs + marginsInDips }; @@ -2502,11 +2505,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation _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(), - point.Properties().IsMiddleButtonPressed(), - point.Properties().IsRightButtonPressed() }; + Control::MouseButtonState state{}; + WI_SetFlagIf(state, Control::MouseButtonState::IsLeftButtonDown, point.Properties().IsLeftButtonPressed()); + 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) diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 9cdb67e9767..a074e020eff 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -40,8 +40,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation float SnapDimensionToGrid(const bool widthOrHeight, const float dimension); #pragma region ICoreState - const size_t TaskbarState() const noexcept; - const size_t TaskbarProgress() const noexcept; + const uint64_t TaskbarState() const noexcept; + const uint64_t TaskbarProgress() const noexcept; hstring Title(); Windows::Foundation::IReference TabColor() noexcept; @@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TerminalConnection::ConnectionState ConnectionState() const; - int ScrollOffset(); + int ScrollOffset() const; int ViewHeight() const; int BufferHeight() const; @@ -84,7 +84,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation ~TermControl(); Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer(); - ::Microsoft::Console::Types::IUiaData* GetUiaData() const; const Windows::UI::Xaml::Thickness GetPadding(); IControlSettings Settings() const; @@ -104,19 +103,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool ReadOnly() const noexcept; 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 Windows::UI::Xaml::Thickness ParseThicknessFromPadding(const hstring padding); // -------------------------------- WinRT Events --------------------------------- // clang-format off WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs); - FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs, _core, CopyToClipboard); - FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged); - FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged); - FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged); - FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged); - FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard); + PROJECTED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs, _core, CopyToClipboard); + PROJECTED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged); + PROJECTED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged); + PROJECTED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged); + PROJECTED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged); + + PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs); @@ -141,11 +142,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown. // (C++ class members are destroyed in reverse order.) // Further, the TermControlAutomationPeer must be destructed after _uiaEngine! - winrt::Windows::UI::Xaml::Automation::Peers::AutomationPeer _automationPeer{ nullptr }; - std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine; + Control::TermControlAutomationPeer _automationPeer{ nullptr }; + Control::ControlInteractivity _interactivity{ nullptr }; + Control::ControlCore _core{ nullptr }; - winrt::com_ptr _core; - winrt::com_ptr _interactivity; winrt::com_ptr _searchBox; IControlSettings _settings; @@ -240,8 +240,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _TryStopAutoScroll(const uint32_t pointerId); 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); ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() const; bool _TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const; diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp index 69af0750f9a..7300644769d 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp @@ -30,13 +30,42 @@ namespace XamlAutomation namespace winrt::Microsoft::Terminal::Control::implementation { - TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::Control::implementation::TermControl* owner) : + TermControlAutomationPeer::TermControlAutomationPeer(TermControl* owner, + Control::InteractivityAutomationPeer impl) : TermControlAutomationPeerT(*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: + // - + // Return Value: + // - + 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: // - Signals the ui automation client that the terminal's selection has changed and should be updated // Arguments: @@ -167,106 +196,36 @@ namespace winrt::Microsoft::Terminal::Control::implementation #pragma region ITextProvider com_array TermControlAutomationPeer::GetSelection() { - SAFEARRAY* pReturnVal; - THROW_IF_FAILED(_uiaProvider->GetSelection(&pReturnVal)); - return WrapArrayOfTextRangeProviders(pReturnVal); + return _contentAutomationPeer.GetSelection(); } com_array TermControlAutomationPeer::GetVisibleRanges() { - SAFEARRAY* pReturnVal; - THROW_IF_FAILED(_uiaProvider->GetVisibleRanges(&pReturnVal)); - return WrapArrayOfTextRangeProviders(pReturnVal); + return _contentAutomationPeer.GetVisibleRanges(); } XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement) { - UIA::ITextRangeProvider* returnVal; - // ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr - THROW_IF_FAILED(_uiaProvider->RangeFromChild(/* IRawElementProviderSimple */ nullptr, - &returnVal)); - - auto parentProvider = this->ProviderFromPeer(*this); - auto xutr = winrt::make_self(returnVal, parentProvider); - return xutr.as(); + return _contentAutomationPeer.RangeFromChild(childElement); } XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation) { - UIA::ITextRangeProvider* returnVal; - THROW_IF_FAILED(_uiaProvider->RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal)); - - auto parentProvider = this->ProviderFromPeer(*this); - auto xutr = winrt::make_self(returnVal, parentProvider); - return xutr.as(); + return _contentAutomationPeer.RangeFromPoint(screenLocation); } XamlAutomation::ITextRangeProvider TermControlAutomationPeer::DocumentRange() { - UIA::ITextRangeProvider* returnVal; - THROW_IF_FAILED(_uiaProvider->get_DocumentRange(&returnVal)); - - auto parentProvider = this->ProviderFromPeer(*this); - auto xutr = winrt::make_self(returnVal, parentProvider); - return xutr.as(); + return _contentAutomationPeer.DocumentRange(); } XamlAutomation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection() { - UIA::SupportedTextSelection returnVal; - THROW_IF_FAILED(_uiaProvider->get_SupportedTextSelection(&returnVal)); - return static_cast(returnVal); + return _contentAutomationPeer.SupportedTextSelection(); } #pragma endregion -#pragma region IControlAccessibilityInfo - COORD TermControlAutomationPeer::GetFontSize() const - { - return _termControl->GetFontSize(); - } - - RECT TermControlAutomationPeer::GetBounds() const - { - auto rect = GetBoundingRectangle(); - return { - gsl::narrow_cast(rect.X), - gsl::narrow_cast(rect.Y), - gsl::narrow_cast(rect.X + rect.Width), - gsl::narrow_cast(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(padding.Left), - gsl::narrow_cast(padding.Top), - gsl::narrow_cast(padding.Right), - gsl::narrow_cast(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: // - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders // Arguments: diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.h b/src/cascadia/TerminalControl/TermControlAutomationPeer.h index 46a90073644..0b81ebafec5 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.h +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.h @@ -20,13 +20,17 @@ Module Name: Author(s): - Carlos Zamora (CaZamor) 2019 + +Modifications: +- May 2021: Pulled the core logic of ITextProvider implementation into the + InteractivityAutomationPeer, to support tab tear out. --*/ #pragma once #include "TermControl.h" +#include "ControlInteractivity.h" #include "TermControlAutomationPeer.g.h" -#include #include "../types/TermControlUiaProvider.hpp" #include "../types/IUiaEventDispatcher.h" #include "../types/IControlAccessibilityInfo.h" @@ -35,11 +39,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation { struct TermControlAutomationPeer : public TermControlAutomationPeerT, - ::Microsoft::Console::Types::IUiaEventDispatcher, - ::Microsoft::Console::Types::IControlAccessibilityInfo + ::Microsoft::Console::Types::IUiaEventDispatcher { 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 hstring GetClassNameCore() const; @@ -67,21 +74,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation 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 - - RECT GetBoundingRectWrapped(); - private: - ::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider; winrt::Microsoft::Terminal::Control::implementation::TermControl* _termControl; + Control::InteractivityAutomationPeer _contentAutomationPeer; + winrt::com_array WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges); }; } diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.idl b/src/cascadia/TerminalControl/TermControlAutomationPeer.idl index 0d777b09a51..4ca83f0d9c1 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.idl +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.idl @@ -9,5 +9,8 @@ namespace Microsoft.Terminal.Control Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer, Windows.UI.Xaml.Automation.Provider.ITextProvider { + + void UpdateControlBounds(); + void SetControlPadding(Microsoft.Terminal.Core.Padding padding); } } diff --git a/src/cascadia/TerminalControl/TerminalControlLib.vcxproj b/src/cascadia/TerminalControl/TerminalControlLib.vcxproj index 945dde88900..3ce1d8a5292 100644 --- a/src/cascadia/TerminalControl/TerminalControlLib.vcxproj +++ b/src/cascadia/TerminalControl/TerminalControlLib.vcxproj @@ -52,6 +52,9 @@ TermControlAutomationPeer.idl + + InteractivityAutomationPeer.idl + TSFInputControl.xaml @@ -91,6 +94,9 @@ TermControlAutomationPeer.idl + + InteractivityAutomationPeer.idl + @@ -112,6 +118,7 @@ TermControl.xaml + TSFInputControl.xaml diff --git a/src/cascadia/TerminalControl/pch.h b/src/cascadia/TerminalControl/pch.h index 730f71f5823..bac6c29cabf 100644 --- a/src/cascadia/TerminalControl/pch.h +++ b/src/cascadia/TerminalControl/pch.h @@ -57,6 +57,8 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalControlProvider); #include +#include + #include "til.h" #include "ThrottledFunc.h" diff --git a/src/cascadia/TerminalCore/ControlKeyStates.hpp b/src/cascadia/TerminalCore/ControlKeyStates.hpp index 9b88c71a3eb..9d09bb6e567 100644 --- a/src/cascadia/TerminalCore/ControlKeyStates.hpp +++ b/src/cascadia/TerminalCore/ControlKeyStates.hpp @@ -82,6 +82,20 @@ class Microsoft::Terminal::Core::ControlKeyStates } #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 { return _value; diff --git a/src/cascadia/TerminalCore/ICoreAppearance.idl b/src/cascadia/TerminalCore/ICoreAppearance.idl index 30c91833ceb..99148c2657d 100644 --- a/src/cascadia/TerminalCore/ICoreAppearance.idl +++ b/src/cascadia/TerminalCore/ICoreAppearance.idl @@ -24,11 +24,38 @@ namespace Microsoft.Terminal.Core 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 { // Forward declare this parameterized specialization so that it lives // in TerminalCore instead of being flung to the winds of all IDL dependents. interface Windows.Foundation.IReference; + interface Windows.Foundation.IReference; } interface ICoreAppearance diff --git a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp index 3839d86e8bc..f4c89311797 100644 --- a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp @@ -118,6 +118,7 @@ namespace ControlUnitTests core->TransparencyChanged(opacityCallback); const auto modifiers = ControlKeyStates(ControlKeyStates::RightCtrlPressed | ControlKeyStates::ShiftPressed); + const Control::MouseButtonState buttonState{}; Log::Comment(L"Scroll in the positive direction, increasing opacity"); // Scroll more than enough times to get to 1.0 from .5. @@ -134,7 +135,7 @@ namespace ControlUnitTests interactivity->MouseWheel(modifiers, 30, til::point{ 0, 0 }, - { false, false, false }); + buttonState); } Log::Comment(L"Scroll in the negative direction, decreasing opacity"); @@ -152,7 +153,7 @@ namespace ControlUnitTests interactivity->MouseWheel(modifiers, 30, til::point{ 0, 0 }, - { false, false, false }); + buttonState); } } @@ -197,6 +198,7 @@ namespace ControlUnitTests VERIFY_ARE_EQUAL(41, core->BufferHeight()); Log::Comment(L"Scroll up a line"); + const Control::MouseButtonState buttonState{}; const auto modifiers = ControlKeyStates(); expectedBufferHeight = 41; expectedTop = 20; @@ -204,7 +206,7 @@ namespace ControlUnitTests interactivity->MouseWheel(modifiers, WHEEL_DELTA, til::point{ 0, 0 }, - { false, false, false }); + buttonState); Log::Comment(L"Scroll up 19 more times, to the top"); for (int i = 0; i < 20; ++i) @@ -213,18 +215,18 @@ namespace ControlUnitTests interactivity->MouseWheel(modifiers, WHEEL_DELTA, til::point{ 0, 0 }, - { false, false, false }); + buttonState); } Log::Comment(L"Scrolling up more should do nothing"); expectedTop = 0; interactivity->MouseWheel(modifiers, WHEEL_DELTA, til::point{ 0, 0 }, - { false, false, false }); + buttonState); interactivity->MouseWheel(modifiers, WHEEL_DELTA, til::point{ 0, 0 }, - { false, false, false }); + buttonState); Log::Comment(L"Scroll down 21 more times, to the bottom"); for (int i = 0; i < 21; ++i) @@ -234,7 +236,7 @@ namespace ControlUnitTests interactivity->MouseWheel(modifiers, -WHEEL_DELTA, til::point{ 0, 0 }, - { false, false, false }); + buttonState); Log::Comment(NoThrowString().Format(L"internal scrollbar pos:%f", interactivity->_internalScrollbarPosition)); } Log::Comment(L"Scrolling up more should do nothing"); @@ -242,11 +244,11 @@ namespace ControlUnitTests interactivity->MouseWheel(modifiers, -WHEEL_DELTA, til::point{ 0, 0 }, - { false, false, false }); + buttonState); interactivity->MouseWheel(modifiers, -WHEEL_DELTA, til::point{ 0, 0 }, - { false, false, false }); + buttonState); } void ControlInteractivityTests::CreateSubsequentSelectionWithDragging() @@ -260,8 +262,8 @@ namespace ControlUnitTests // For this test, don't use any modifiers const auto modifiers = ControlKeyStates(); - const TerminalInput::MouseButtonState leftMouseDown{ true, false, false }; - const TerminalInput::MouseButtonState noMouseDown{ false, false, false }; + const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown }; + const Control::MouseButtonState noMouseDown{}; const til::size fontSize{ 9, 21 }; @@ -358,8 +360,8 @@ namespace ControlUnitTests // For this test, don't use any modifiers const auto modifiers = ControlKeyStates(); - const TerminalInput::MouseButtonState leftMouseDown{ true, false, false }; - const TerminalInput::MouseButtonState noMouseDown{ false, false, false }; + const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown }; + const Control::MouseButtonState noMouseDown{}; const til::size fontSize{ 9, 21 }; @@ -401,7 +403,7 @@ namespace ControlUnitTests interactivity->MouseWheel(modifiers, WHEEL_DELTA, cursorPosition1, - { true, false, false }); + leftMouseDown); Log::Comment(L"Verify the location of the selection"); // 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 til::point mousePos{ 0, 0 }; - TerminalInput::MouseButtonState state{ false, false, false }; + Control::MouseButtonState state{}; interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5 VERIFY_ARE_EQUAL(21, core->ScrollOffset()); @@ -508,8 +510,8 @@ namespace ControlUnitTests // For this test, don't use any modifiers const auto modifiers = ControlKeyStates(); - const TerminalInput::MouseButtonState leftMouseDown{ true, false, false }; - const TerminalInput::MouseButtonState noMouseDown{ false, false, false }; + const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown }; + const Control::MouseButtonState noMouseDown{}; const til::size fontSize{ 9, 21 }; diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h index 586f26e6cc4..5c183a87582 100644 --- a/src/cascadia/inc/cppwinrt_utils.h +++ b/src/cascadia/inc/cppwinrt_utils.h @@ -96,6 +96,12 @@ public: winrt::event_token name(Windows::Foundation::TypedEventHandler const& h) { return handler->handlerName(h); } \ 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 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. // This should only be used for simple types where there's no logic in the // getter/setter beyond just accessing/updating the value. diff --git a/src/inc/til/point.h b/src/inc/til/point.h index 366ba18077b..5d47588255e 100644 --- a/src/inc/til/point.h +++ b/src/inc/til/point.h @@ -338,6 +338,21 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" } #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(); + ret.Y = y(); + return ret; + } +#endif + std::wstring to_string() const { return wil::str_printf(L"(X:%td, Y:%td)", x(), y()); diff --git a/src/inc/til/rectangle.h b/src/inc/til/rectangle.h index 83bb0b3f11a..43bae773f0b 100644 --- a/src/inc/til/rectangle.h +++ b/src/inc/til/rectangle.h @@ -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 + constexpr rectangle(TilMath, const TOther& other, std::enable_if_t().X)> && std::is_floating_point_v().Y)> && std::is_floating_point_v().Width)> && std::is_floating_point_v().Height)>, int> /*sentinel*/ = 0) : + rectangle(til::point{ TilMath::template cast(other.X), TilMath::template cast(other.Y) }, til::size{ TilMath::template cast(other.Width), TilMath::template cast(other.Height) }) + { + } + // 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. template @@ -875,6 +883,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" } #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 { return wil::str_printf(L"(L:%td, T:%td, R:%td, B:%td) [W:%td, H:%td]", left(), top(), right(), bottom(), width(), height());