From 7836da07dd4738153db4659f9796f81956c981f4 Mon Sep 17 00:00:00 2001 From: Leon Liang <57155886+leonMSFT@users.noreply.github.com> Date: Wed, 12 Feb 2020 16:32:50 -0800 Subject: [PATCH] Fix click-drag selection on an unfocused Terminal (#4506) ## Summary of the Pull Request This PR tries to address some of the weird interactions with pointer pressed events when the Terminal isn't in focus. Here's the four things that have changed as part of this PR; 1. This PR will allow the user to be able to make a selection with a click-drag without having to first perform a single click on a tab/pane to bring it to focus. 2. Another weird bug that's fixed in this PR is where trying to make a selection on an unfocused tab when it already has a selection active will simply extend the existing selection instead of making a new one. 3. Not related to the issue that his PR closes: a right click will now focus the tab/pane. I've made sure that we still have the existing functionality where a single click on an unfocused tab/pane does not make a single-cell selection and just focuses the tab/pane. ## PR Checklist * [x] Closes #4282 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed ## Validation Steps Performed Played around with all sorts of selection when in-focus and out of focus with multiple panes and tabs. Unit tests still pass as well. --- src/cascadia/TerminalControl/TermControl.cpp | 42 +++++++++++++++----- src/cascadia/TerminalControl/TermControl.h | 1 + 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 14d95dc1471..629860f3946 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -886,17 +886,20 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto ptr = args.Pointer(); const auto point = args.GetCurrentPoint(_root); - if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen) + if (!_focused) { - // Ignore mouse events while the terminal does not have focus. - // This prevents the user from selecting and copying text if they - // click inside the current tab to refocus the terminal window. - if (!_focused) - { - args.Handled(true); - return; - } + Focus(FocusState::Pointer); + // Save the click position here when the terminal does not have focus + // because they might be performing a click-drag selection. Since we + // only want to start the selection when the user moves the pointer with + // the left mouse button held down, the PointerMovedHandler will use + // this saved position to set the SelectionAnchor. + _clickDragStartPos = point.Position(); + } + + if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen) + { const auto modifiers = static_cast(args.KeyModifiers()); // static_cast to a uint32_t because we can't use the WI_IsFlagSet // macro directly with a VirtualKeyModifiers @@ -905,6 +908,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation if (point.Properties().IsLeftButtonPressed()) { + // _clickDragStartPos having a value signifies to us that + // the user clicked on an unfocused terminal. We don't want + // a single left click from out of focus to start a selection, + // so we return fast here. + if (_clickDragStartPos) + { + args.Handled(true); + return; + } + const auto cursorPosition = point.Position(); const auto terminalPosition = _GetTerminalPosition(cursorPosition); @@ -981,6 +994,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { if (point.Properties().IsLeftButtonPressed()) { + // If this does not have a value, it means that PointerPressedHandler already + // set the SelectionAnchor. If it does have a value, that means the user is + // performing a click-drag selection on an unfocused terminal, so + // a SelectionAnchor isn't set yet. We'll have to set it here. + if (_clickDragStartPos) + { + _terminal->SetSelectionAnchor(_GetTerminalPosition(*_clickDragStartPos)); + } + const auto cursorPosition = point.Position(); _SetEndSelectionPointAtCursor(cursorPosition); @@ -1053,6 +1075,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto ptr = args.Pointer(); + _clickDragStartPos = std::nullopt; + if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen) { const auto modifiers = static_cast(args.KeyModifiers()); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index c566c440f35..a0290bccce8 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -162,6 +162,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation Timestamp _lastMouseClick; unsigned int _multiClickCounter; std::optional _lastMouseClickPos; + std::optional _clickDragStartPos{ std::nullopt }; // Event revokers -- we need to deregister ourselves before we die, // lest we get callbacks afterwards.