Skip to content

Commit

Permalink
PRE-MERGE #14635 Don't copy before pasting in copyOnSelect mode
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-zamora committed Jan 14, 2023
2 parents 2d177d8 + ca658ba commit e96f6e6
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 3 deletions.
17 changes: 14 additions & 3 deletions src/cascadia/TerminalControl/ControlInteractivity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
{
// Try to copy the text and clear the selection
const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, nullptr);
const auto copyOnSelect{ _core->CopyOnSelect() };
bool successfulCopy = false;

// Don't try to copy if we're in copyOnSelect mode and have already
// copied this selection. GH#14464 demonstrates a scenario where the
// buffer contents might have changed since the selection was made,
// and copying here would cause weirdness.
if (_selectionNeedsToBeCopied || !copyOnSelect)
{
// Try to copy the text and clear the selection
successfulCopy = CopySelectionToClipboard(shiftEnabled, nullptr);
}
_core->ClearSelection();
if (_core->CopyOnSelect() || !successfulCopy)

if (copyOnSelect || !successfulCopy)
{
// CopyOnSelect: right click always pastes!
// Otherwise: no selection --> paste
Expand Down
182 changes: 182 additions & 0 deletions src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ namespace ControlUnitTests
TEST_METHOD(GetMouseEventsInTest);
TEST_METHOD(AltBufferClampMouse);

TEST_METHOD(CopyOnSelectSimple);
TEST_METHOD(CopyOnSelectAltBuffer);

TEST_CLASS_SETUP(ClassSetup)
{
winrt::init_apartment(winrt::apartment_type::single_threaded);
Expand Down Expand Up @@ -966,4 +969,183 @@ namespace ControlUnitTests
cursorPosition1.to_core_point());
VERIFY_ARE_EQUAL(0u, expectedOutput.size(), L"Validate we drained all the expected output");
}

void ControlInteractivityTests::CopyOnSelectSimple()
{
auto [settings, conn] = _createSettingsAndConnection();
settings->CopyOnSelect(true);
auto [core, interactivity] = _createCoreAndInteractivity(*settings, *conn);
_standardInit(core, interactivity);
auto& term{ *core->_terminal };

// Output some text
for (auto i = 0; i < core->ViewHeight() / 2; ++i)
{
conn->WriteInput(winrt::hstring{ fmt::format(L"line {}\r\n", i) });
}

// A callback for checking Copy events. expectedCopyContents=nullopt
// indicates that we're not expecting any copy events.
std::optional<std::wstring> expectedCopyContents{ std::nullopt };
core->CopyToClipboard([&](auto&&, auto& args) {
VERIFY_IS_TRUE(expectedCopyContents.has_value());
const std::wstring expected{ expectedCopyContents->c_str() };
const std::wstring actual{ args.Text().c_str() };
VERIFY_ARE_EQUAL(expected, actual);
});
bool expectedPaste = false;
interactivity->PasteFromClipboard([&](auto&&, auto&&) {
VERIFY_IS_TRUE(expectedPaste);
});

const auto originalViewport{ term.GetViewport() };
VERIFY_ARE_EQUAL(originalViewport.Width(), 30);

// For this test, don't use any modifiers
const auto modifiers = ControlKeyStates();
const auto leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const auto rightMouseDown{ Control::MouseButtonState::IsRightButtonDown };
const Control::MouseButtonState noMouseDown{};
const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 0, 5 };
const til::point terminalPosition1{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
const auto cursorPosition1{ terminalPosition1 * fontSize };

Log::Comment(L" --- Click on the terminal at 0,5 ---");

interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition0.to_core_point());
VERIFY_IS_FALSE(core->HasSelection());

Log::Comment(L" --- Drag to 5,5 ---");

interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true); // pointerPressedInBounds
VERIFY_IS_TRUE(core->HasSelection());
// expectedCopyContents being nullopt here will ensure that we don't send the copy event till the pointer released

Log::Comment(L" --- Release the mouse --- ");
expectedCopyContents = L"line 5";
interactivity->PointerReleased(noMouseDown,
WM_LBUTTONUP, //pointerUpdateKind
modifiers,
cursorPosition1.to_core_point());
VERIFY_IS_TRUE(core->HasSelection());

Log::Comment(L" --- Right-click to paste --- ");
// Note from GH#14464: we don't want to copy _again_ at this point. The
// copy occured when the selection was made, we shouldn't stealth-update
// the clipboard again.
expectedCopyContents = std::nullopt;
expectedPaste = true;
interactivity->PointerPressed(rightMouseDown,
WM_RBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition1.to_core_point());
VERIFY_IS_FALSE(core->HasSelection());
}

void ControlInteractivityTests::CopyOnSelectAltBuffer()
{
// This test was inspired by GH#14464. Ultimately, it's similar to the
// CopyOnSelectSimple, just with an alt buffer, and outputting text
// after the selection was made.
auto [settings, conn] = _createSettingsAndConnection();
settings->CopyOnSelect(true);
auto [core, interactivity] = _createCoreAndInteractivity(*settings, *conn);
_standardInit(core, interactivity);
auto& term{ *core->_terminal };

Log::Comment(L" --- Switch to alt buffer ---");
term.Write(L"\x1b[?1049h");
auto returnToMain = wil::scope_exit([&]() { term.Write(L"\x1b[?1049h"); });

// Output some text
auto i = 0;
for (i = 0; i < core->ViewHeight() - 1; ++i)
{
conn->WriteInput(winrt::hstring{ fmt::format(L"line {}\r\n", i) });
}

// A callback for checking Copy events. expectedCopyContents=nullopt
// indicates that we're not expecting any copy events.
std::optional<std::wstring> expectedCopyContents{ std::nullopt };
core->CopyToClipboard([&](auto&&, auto& args) {
VERIFY_IS_TRUE(expectedCopyContents.has_value());
const std::wstring expected{ expectedCopyContents->c_str() };
const std::wstring actual{ args.Text().c_str() };
VERIFY_ARE_EQUAL(expected, actual);
});
bool expectedPaste = false;
interactivity->PasteFromClipboard([&](auto&&, auto&&) {
VERIFY_IS_TRUE(expectedPaste);
});

const auto originalViewport{ term.GetViewport() };
VERIFY_ARE_EQUAL(originalViewport.Width(), 30);

// For this test, don't use any modifiers
const auto modifiers = ControlKeyStates();
const auto leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const auto rightMouseDown{ Control::MouseButtonState::IsRightButtonDown };
const Control::MouseButtonState noMouseDown{};
const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 0, 5 };
const til::point terminalPosition1{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
const auto cursorPosition1{ terminalPosition1 * fontSize };

Log::Comment(L" --- Click on the terminal at 0,5 ---");

interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition0.to_core_point());
VERIFY_IS_FALSE(core->HasSelection());

Log::Comment(L" --- Drag to 5,5 ---");

interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true); // pointerPressedInBounds
VERIFY_IS_TRUE(core->HasSelection());
// expectedCopyContents being nullopt here will ensure that we don't send the copy event till the pointer released

Log::Comment(L" --- Release the mouse --- ");
expectedCopyContents = L"line 5";
interactivity->PointerReleased(noMouseDown,
WM_LBUTTONUP, //pointerUpdateKind
modifiers,
cursorPosition1.to_core_point());
VERIFY_IS_TRUE(core->HasSelection());

// Output some text
for (; i < 5; ++i)
{
conn->WriteInput(winrt::hstring{ fmt::format(L"line {}\r\n", i) });
}

Log::Comment(L" --- Right-click to paste --- ");
expectedCopyContents = std::nullopt;
expectedPaste = true;
interactivity->PointerPressed(rightMouseDown,
WM_RBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition1.to_core_point());
VERIFY_IS_FALSE(core->HasSelection());
}
}

0 comments on commit e96f6e6

Please sign in to comment.