From 0f386592e4e17cc7813cb3296894bf5c28a39353 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Thu, 19 Mar 2020 20:23:03 -0700 Subject: [PATCH] Switch to the I-beam cursor when hovering over the terminal This commit makes us use the I-beam cursor when the user hovers over the terminal, *unless* mouse mode is enabled. I've also plumbed up a bunch of events so that: * If mouse mode is _toggled_ while hovering, the cursor will switch to the arrow if it's on or the I-beam if it's off. * If you hold down shift to suppress mouse mode, the cursor will switch back to the I-beam. Fixes #1441. --- src/cascadia/TerminalControl/TermControl.cpp | 83 ++++++++++++++++++- src/cascadia/TerminalControl/TermControl.h | 8 ++ src/cascadia/TerminalControl/TermControl.xaml | 5 +- src/cascadia/TerminalCore/Terminal.cpp | 4 + src/cascadia/TerminalCore/Terminal.hpp | 1 + src/terminal/input/mouseInputState.cpp | 24 ++++++ src/terminal/input/terminalInput.hpp | 3 + 7 files changed, 126 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 0d955716ca0..3387229ba50 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -71,7 +71,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _lastMouseClickPos{}, _searchBox{ nullptr }, _focusRaisedClickPos{ std::nullopt }, - _clickDrag{ false } + _clickDrag{ false }, + _textCursor{ Windows::UI::Core::CoreCursorType::IBeam, 0 }, + _pointerCursor{ Windows::UI::Core::CoreCursorType::Arrow, 0 } { _EnsureStaticInitialization(); InitializeComponent(); @@ -589,6 +591,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation auto inputFn = std::bind(&TermControl::_SendInputToConnection, this, std::placeholders::_1); _terminal->SetWriteInputCallback(inputFn); + _terminal->SetMouseModeChangedCallback([weakThis = get_weak()]() { + if (auto strongThis{ weakThis.get() }) + { + strongThis->_TerminalMouseModeChanged(); + } + }); _SwapChainRoutine(); @@ -715,6 +723,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation e.OriginalKey() == VirtualKey::RightWindows) { + if (!_closing && e.OriginalKey() == VirtualKey::Shift) + { + // If the user presses or releases shift, check whether we're in mouse mode and the cursor needs updating + _TerminalMouseModeChanged(); + } e.Handled(true); return; } @@ -766,6 +779,23 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation e.Handled(handled); } + void TermControl::_KeyUpHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, + Input::KeyRoutedEventArgs const& e) + { + // If the current focused element is a child element of searchbox, + // we do not send this event up to terminal + if (_searchBox && _searchBox->ContainsFocus()) + { + return; + } + + if (!_closing && e.OriginalKey() == VirtualKey::Shift) + { + // If the user presses or releases shift, check whether we're in mouse mode and the cursor needs updating + _TerminalMouseModeChanged(); + } + } + // Method Description: // - Send this particular key event to the terminal. // See Terminal::SendKeyEvent for more information. @@ -889,6 +919,18 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation return _terminal->IsTrackingMouseInput(); } + // Method Description: + // - Handles changes in mouse mode state + winrt::fire_and_forget TermControl::_TerminalMouseModeChanged() + { + co_await Dispatcher(); + if (_oldCursor) // if we have an active cursor transition + { + auto coreWindow = Window::Current().CoreWindow(); + coreWindow.PointerCursor(_CanSendVTMouseInput() ? _pointerCursor : _textCursor); + } + } + // Method Description: // - handle a mouse click event. Begin selection process. // Arguments: @@ -1150,6 +1192,45 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation args.Handled(true); } + // Method Description: + // - Event handler for the PointerEntered event. We use this for cursor manipulation. + // Arguments: + // - sender: the XAML element responding to the pointer input + // - args: event data + void TermControl::_PointerEnteredHandler(Windows::Foundation::IInspectable const& /*sender*/, + Input::PointerRoutedEventArgs const& /*args*/) + { + if (_closing) + { + return; + } + + auto coreWindow = Window::Current().CoreWindow(); + _oldCursor = coreWindow.PointerCursor(); + + if (_terminal->IsTrackingMouseInput()) + { + return; + } + + coreWindow.PointerCursor(_textCursor); + } + + // Method Description: + // - Event handler for the PointerExited event. We use this for cursor manipulation. + // Arguments: + // - sender: the XAML element responding to the pointer input + // - args: event data + void TermControl::_PointerExitedHandler(Windows::Foundation::IInspectable const& /*sender*/, + Input::PointerRoutedEventArgs const& /*args*/) + { + if (auto oldCursor{ std::exchange(_oldCursor, std::nullopt) }) + { + auto coreWindow = Window::Current().CoreWindow(); + coreWindow.PointerCursor(*oldCursor); + } + } + // Method Description: // - Event handler for the PointerWheelChanged event. This is raised in // response to mouse wheel changes. Depending upon what modifier keys are diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 57606506bee..19b73b596c9 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -164,6 +164,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker; + std::optional _oldCursor; // when we toggle the cursor, we have to save it here to restore it + winrt::Windows::UI::Core::CoreCursor _textCursor; + winrt::Windows::UI::Core::CoreCursor _pointerCursor; + void _ApplyUISettings(); void _InitializeBackgroundBrush(); winrt::fire_and_forget _BackgroundColorChanged(const uint32_t color); @@ -172,10 +176,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void _SetFontSize(int fontSize); void _TappedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs const& e); void _KeyDownHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e); + void _KeyUpHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e); void _CharacterHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::CharacterReceivedRoutedEventArgs const& e); void _PointerPressedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); void _PointerMovedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); void _PointerReleasedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + void _PointerEnteredHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + void _PointerExitedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); void _MouseWheelHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); void _ScrollbarChangeHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e); void _GotFocusHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); @@ -213,6 +220,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation bool _TrySendKeyEvent(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers); bool _TrySendMouseEvent(Windows::UI::Input::PointerPoint const& point); bool _CanSendVTMouseInput(); + winrt::fire_and_forget _TerminalMouseModeChanged(); const COORD _GetTerminalPosition(winrt::Windows::Foundation::Point cursorPosition); const unsigned int _NumberOfClicks(winrt::Windows::Foundation::Point clickPos, Timestamp clickTime); diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index a6a24e628b2..55331b3ea5f 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -19,6 +19,7 @@ Tapped="_TappedHandler" PointerWheelChanged="_MouseWheelHandler" PreviewKeyDown="_KeyDownHandler" + KeyUp="_KeyUpHandler" CharacterReceived="_CharacterHandler" GotFocus="_GotFocusHandler" LostFocus="_LostFocusHandler"> @@ -43,7 +44,9 @@ CompositionScaleChanged="_SwapChainScaleChanged" PointerPressed="_PointerPressedHandler" PointerMoved="_PointerMovedHandler" - PointerReleased="_PointerReleasedHandler" /> + PointerReleased="_PointerReleasedHandler" + PointerEntered="_PointerEnteredHandler" + PointerExited="_PointerExitedHandler" /> diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 89998b8e45e..7317b3c4bfd 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -788,3 +788,7 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept const auto& cursor = _buffer->GetCursor(); return cursor.IsBlinkingAllowed(); } + +void Terminal::SetMouseModeChangedCallback(std::function mouseModeChangedCallback) { + _terminalInput->SetMouseModeChangedCallback(std::move(mouseModeChangedCallback)); +} diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 8498efa1aff..43042311d4b 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -172,6 +172,7 @@ class Microsoft::Terminal::Core::Terminal final : void SetCursorOn(const bool isOn) noexcept; bool IsCursorBlinkingAllowed() const noexcept; + void SetMouseModeChangedCallback(std::function mouseModeChangedCallback); #pragma region TextSelection // These methods are defined in TerminalSelection.cpp diff --git a/src/terminal/input/mouseInputState.cpp b/src/terminal/input/mouseInputState.cpp index b1c2509de74..d135923686c 100644 --- a/src/terminal/input/mouseInputState.cpp +++ b/src/terminal/input/mouseInputState.cpp @@ -47,6 +47,11 @@ void TerminalInput::EnableDefaultTracking(const bool enable) noexcept _mouseInputState.trackingMode = enable ? TrackingMode::Default : TrackingMode::None; _mouseInputState.lastPos = { -1, -1 }; // Clear out the last saved mouse position & button. _mouseInputState.lastButton = 0; + + if (_mouseModeChangedCallback) + { + _mouseModeChangedCallback(); + } } // Routine Description: @@ -63,6 +68,11 @@ void TerminalInput::EnableButtonEventTracking(const bool enable) noexcept _mouseInputState.trackingMode = enable ? TrackingMode::ButtonEvent : TrackingMode::None; _mouseInputState.lastPos = { -1, -1 }; // Clear out the last saved mouse position & button. _mouseInputState.lastButton = 0; + + if (_mouseModeChangedCallback) + { + _mouseModeChangedCallback(); + } } // Routine Description: @@ -79,6 +89,11 @@ void TerminalInput::EnableAnyEventTracking(const bool enable) noexcept _mouseInputState.trackingMode = enable ? TrackingMode::AnyEvent : TrackingMode::None; _mouseInputState.lastPos = { -1, -1 }; // Clear out the last saved mouse position & button. _mouseInputState.lastButton = 0; + + if (_mouseModeChangedCallback) + { + _mouseModeChangedCallback(); + } } // Routine Description: @@ -113,3 +128,12 @@ void TerminalInput::UseMainScreenBuffer() noexcept { _mouseInputState.inAlternateBuffer = false; } + +// Routine Description: +// - Sets up the callback for mouse input mode changes +// Parameters: +// - mouseModeChangedCallback: the callback +void TerminalInput::SetMouseModeChangedCallback(std::function mouseModeChangedCallback) +{ + _mouseModeChangedCallback = std::move(mouseModeChangedCallback); +} diff --git a/src/terminal/input/terminalInput.hpp b/src/terminal/input/terminalInput.hpp index 064cfd91684..d2ecad9818b 100644 --- a/src/terminal/input/terminalInput.hpp +++ b/src/terminal/input/terminalInput.hpp @@ -60,10 +60,13 @@ namespace Microsoft::Console::VirtualTerminal void EnableAlternateScroll(const bool enable) noexcept; void UseAlternateScreenBuffer() noexcept; void UseMainScreenBuffer() noexcept; + + void SetMouseModeChangedCallback(std::function mouseModeChangedCallback); #pragma endregion private: std::function>&)> _pfnWriteEvents; + std::function _mouseModeChangedCallback; // storage location for the leading surrogate of a utf-16 surrogate pair std::optional _leadingSurrogate;