Skip to content

Commit

Permalink
Added CopyOnSelect as a Global Setting (microsoft#2152)
Browse files Browse the repository at this point in the history
* Added CopyOnSelect as a ControlSetting

* Updated doc

* Updated doc

* CopyOnSelect feature changes (like, overall)

* Made CopyOnSelect a CoreSetting
CopyOnSelect value accessible through Terminal's IsCopyOnSelectActive

* Refactor a bit.

* CopyOnSelect Tests

* PR nits
  • Loading branch information
carlos-zamora authored and msftbot[bot] committed Aug 20, 2019
1 parent 98f7781 commit ff87190
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 28 deletions.
1 change: 1 addition & 0 deletions doc/cascadia/SettingsSchema.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Properties listed below affect the entire window, regardless of the profile sett
| Property | Necessity | Type | Default | Description |
| -------- | --------- | ---- | ------- | ----------- |
| `alwaysShowTabs` | _Required_ | Boolean | `true` | When set to `true`, tabs are always displayed. When set to `false` and `showTabsInTitlebar` is set to `false`, tabs only appear after typing <kbd>Ctrl</kbd> + <kbd>T</kbd>. |
| `copyOnSelect` | Optional | Boolean | `false` | When set to `true`, a selection is immediately copied to your clipboard upon creation. When set to `false`, the selection persists and awaits further action. |
| `defaultProfile` | _Required_ | String | PowerShell guid | Sets the default profile. Opens by typing <kbd>Ctrl</kbd> + <kbd>T</kbd> or by clicking the '+' icon. The guid of the desired default profile is used as the value. |
| `initialCols` | _Required_ | Integer | `120` | The number of columns displayed in the window upon first load. |
| `initialRows` | _Required_ | Integer | `30` | The number of rows displayed in the window upon first load. |
Expand Down
21 changes: 20 additions & 1 deletion src/cascadia/TerminalApp/GlobalAppSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTi
static constexpr std::string_view RequestedThemeKey{ "requestedTheme" };
static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" };
static constexpr std::string_view WordDelimitersKey{ "wordDelimiters" };
static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" };

static constexpr std::wstring_view LightThemeValue{ L"light" };
static constexpr std::wstring_view DarkThemeValue{ L"dark" };
Expand All @@ -39,7 +40,8 @@ GlobalAppSettings::GlobalAppSettings() :
_showTitleInTitlebar{ true },
_showTabsInTitlebar{ true },
_requestedTheme{ ElementTheme::Default },
_wordDelimiters{ DEFAULT_WORD_DELIMITERS }
_wordDelimiters{ DEFAULT_WORD_DELIMITERS },
_copyOnSelect{ false }
{
}

Expand Down Expand Up @@ -117,6 +119,16 @@ void GlobalAppSettings::SetWordDelimiters(const std::wstring wordDelimiters) noe
_wordDelimiters = wordDelimiters;
}

bool GlobalAppSettings::GetCopyOnSelect() const noexcept
{
return _copyOnSelect;
}

void GlobalAppSettings::SetCopyOnSelect(const bool copyOnSelect) noexcept
{
_copyOnSelect = copyOnSelect;
}

#pragma region ExperimentalSettings
bool GlobalAppSettings::GetShowTabsInTitlebar() const noexcept
{
Expand All @@ -141,6 +153,7 @@ void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexce
settings.InitialRows(_initialRows);
settings.InitialCols(_initialCols);
settings.WordDelimiters(_wordDelimiters);
settings.CopyOnSelect(_copyOnSelect);
}

// Method Description:
Expand All @@ -160,6 +173,7 @@ Json::Value GlobalAppSettings::ToJson() const
jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar;
jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar;
jsonObject[JsonKey(WordDelimitersKey)] = winrt::to_string(_wordDelimiters);
jsonObject[JsonKey(CopyOnSelectKey)] = _copyOnSelect;
jsonObject[JsonKey(RequestedThemeKey)] = winrt::to_string(_SerializeTheme(_requestedTheme));
jsonObject[JsonKey(KeybindingsKey)] = AppKeyBindingsSerialization::ToJson(_keybindings);

Expand Down Expand Up @@ -210,6 +224,11 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json)
result._wordDelimiters = GetWstringFromJson(wordDelimiters);
}

if (auto copyOnSelect{ json[JsonKey(CopyOnSelectKey)] })
{
result._copyOnSelect = copyOnSelect.asBool();
}

if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] })
{
result._requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme));
Expand Down
4 changes: 4 additions & 0 deletions src/cascadia/TerminalApp/GlobalAppSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class TerminalApp::GlobalAppSettings final
std::wstring GetWordDelimiters() const noexcept;
void SetWordDelimiters(const std::wstring wordDelimiters) noexcept;

bool GetCopyOnSelect() const noexcept;
void SetCopyOnSelect(const bool copyOnSelect) noexcept;

winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept;

Json::Value ToJson() const;
Expand All @@ -72,6 +75,7 @@ class TerminalApp::GlobalAppSettings final

bool _showTabsInTitlebar;
std::wstring _wordDelimiters;
bool _copyOnSelect;
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;

static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept;
Expand Down
67 changes: 42 additions & 25 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -773,15 +773,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
else if (point.Properties().IsRightButtonPressed())
{
// copy selection, if one exists
if (_terminal->IsSelectionActive())
// copyOnSelect causes right-click to always paste
if (_terminal->IsCopyOnSelectActive() || !_terminal->IsSelectionActive())
{
CopySelectionToClipboard(!shiftEnabled);
PasteTextFromClipboard();
}
// paste selection, otherwise
else
{
PasteTextFromClipboard();
CopySelectionToClipboard(!shiftEnabled);
}
}
}
Expand All @@ -808,7 +807,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation

if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
{
if (_terminal->IsSelectionActive() && point.Properties().IsLeftButtonPressed())
if (point.Properties().IsLeftButtonPressed())
{
const auto cursorPosition = point.Position();
_SetEndSelectionPointAtCursor(cursorPosition);
Expand Down Expand Up @@ -885,7 +884,19 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation

const auto ptr = args.Pointer();

if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
{
const auto modifiers = static_cast<uint32_t>(args.KeyModifiers());
// static_cast to a uint32_t because we can't use the WI_IsFlagSet
// macro directly with a VirtualKeyModifiers
const auto shiftEnabled = WI_IsFlagSet(modifiers, static_cast<uint32_t>(VirtualKeyModifiers::Shift));

if (_terminal->IsCopyOnSelectActive())
{
CopySelectionToClipboard(!shiftEnabled);
}
}
else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
{
_touchAnchor = std::nullopt;
}
Expand Down Expand Up @@ -1387,35 +1398,41 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}

// Method Description:
// - get text from buffer and send it to the Windows Clipboard (CascadiaWin32:main.cpp). Also removes rendering of selection.
// - Given a copy-able selection, get the selected text from the buffer and send it to the
// Windows Clipboard (CascadiaWin32:main.cpp).
// - CopyOnSelect does NOT clear the selection
// Arguments:
// - trimTrailingWhitespace: enable removing any whitespace from copied selection
// and get text to appear on separate lines.
bool TermControl::CopySelectionToClipboard(bool trimTrailingWhitespace)
{
if (_terminal != nullptr && _terminal->IsSelectionActive())
// no selection --> nothing to copy
if (_terminal == nullptr || !_terminal->IsSelectionActive())
{
// extract text from buffer
const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace);
return false;
}
// extract text from buffer
const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace);

// convert text: vector<string> --> string
std::wstring textData;
for (const auto& text : bufferData.text)
{
textData += text;
}
// convert text: vector<string> --> string
std::wstring textData;
for (const auto& text : bufferData.text)
{
textData += text;
}

// convert text to HTML format
const auto htmlData = TextBuffer::GenHTML(bufferData, _actualFont.GetUnscaledSize().Y, _actualFont.GetFaceName(), "Windows Terminal");
// convert text to HTML format
const auto htmlData = TextBuffer::GenHTML(bufferData, _actualFont.GetUnscaledSize().Y, _actualFont.GetFaceName(), "Windows Terminal");

if (!_terminal->IsCopyOnSelectActive())
{
_terminal->ClearSelection();

// send data up for clipboard
auto copyArgs = winrt::make_self<CopyToClipboardEventArgs>(winrt::hstring(textData.data(), textData.size()), winrt::to_hstring(htmlData));
_clipboardCopyHandlers(*this, *copyArgs);
return true;
}
return false;

// send data up for clipboard
auto copyArgs = winrt::make_self<CopyToClipboardEventArgs>(winrt::hstring(textData.data(), textData.size()), winrt::to_hstring(htmlData));
_clipboardCopyHandlers(*this, *copyArgs);
return true;
}

// Method Description:
Expand Down
4 changes: 4 additions & 0 deletions src/cascadia/TerminalCore/Terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Terminal::Terminal() :
_snapOnInput{ true },
_boxSelection{ false },
_selectionActive{ false },
_allowSingleCharSelection{ false },
_copyOnSelect{ false },
_selectionAnchor{ 0, 0 },
_endSelectionPosition{ 0, 0 }
{
Expand Down Expand Up @@ -135,6 +137,8 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting

_wordDelimiters = settings.WordDelimiters();

_copyOnSelect = settings.CopyOnSelect();

// TODO:MSFT:21327402 - if HistorySize has changed, resize the buffer so we
// have a smaller scrollback. We should do this carefully - if the new buffer
// size is smaller than where the mutable viewport currently is, we'll want
Expand Down
4 changes: 4 additions & 0 deletions src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class Microsoft::Terminal::Core::Terminal final :

#pragma region TextSelection
// These methods are defined in TerminalSelection.cpp
const bool IsCopyOnSelectActive() const noexcept;
void DoubleClickSelection(const COORD position);
void TripleClickSelection(const COORD position);
void SetSelectionAnchor(const COORD position);
Expand Down Expand Up @@ -183,6 +184,8 @@ class Microsoft::Terminal::Core::Terminal final :
COORD _endSelectionPosition;
bool _boxSelection;
bool _selectionActive;
bool _allowSingleCharSelection;
bool _copyOnSelect;
SHORT _selectionAnchor_YOffset;
SHORT _endSelectionPosition_YOffset;
std::wstring _wordDelimiters;
Expand Down Expand Up @@ -234,5 +237,6 @@ class Microsoft::Terminal::Core::Terminal final :
COORD _ExpandDoubleClickSelectionRight(const COORD position) const;
const bool _isWordDelimiter(std::wstring_view cellChar) const;
const COORD _ConvertToBufferCell(const COORD viewportPos) const;
const bool _isSingleCellSelection() const noexcept;
#pragma endregion
};
36 changes: 34 additions & 2 deletions src/cascadia/TerminalCore/TerminalSelection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ std::vector<SMALL_RECT> Terminal::_GetSelectionRects() const
{
std::vector<SMALL_RECT> selectionArea;

if (!_selectionActive)
if (!IsSelectionActive())
{
return selectionArea;
}
Expand Down Expand Up @@ -67,7 +67,7 @@ std::vector<SMALL_RECT> Terminal::_GetSelectionRects() const
if (_multiClickSelectionMode == SelectionExpansionMode::Word)
{
const auto cellChar = _buffer->GetCellDataAt(selectionAnchorWithOffset)->Chars();
if (_selectionAnchor == _endSelectionPosition && _isWordDelimiter(cellChar))
if (_isSingleCellSelection() && _isWordDelimiter(cellChar))
{
// only highlight the cell if you double click a delimiter
}
Expand Down Expand Up @@ -142,15 +142,39 @@ const SHORT Terminal::_ExpandWideGlyphSelectionRight(const SHORT xPos, const SHO
return position.X;
}

// Method Description:
// - Checks if selection is on a single cell
// Return Value:
// - bool representing if selection is only a single cell. Used for copyOnSelect
const bool Terminal::_isSingleCellSelection() const noexcept
{
return (_selectionAnchor == _endSelectionPosition);
}

// Method Description:
// - Checks if selection is active
// Return Value:
// - bool representing if selection is active. Used to decide copy/paste on right click
const bool Terminal::IsSelectionActive() const noexcept
{
// A single cell selection is not considered an active selection,
// if it's not allowed
if (!_allowSingleCharSelection && _isSingleCellSelection())
{
return false;
}
return _selectionActive;
}

// Method Description:
// - Checks if the CopyOnSelect setting is active
// Return Value:
// - true if feature is active, false otherwise.
const bool Terminal::IsCopyOnSelectActive() const noexcept
{
return _copyOnSelect;
}

// Method Description:
// - Select the sequence between delimiters defined in Settings
// Arguments:
Expand Down Expand Up @@ -211,6 +235,8 @@ void Terminal::SetSelectionAnchor(const COORD position)
_selectionAnchor_YOffset = gsl::narrow<SHORT>(_ViewStartIndex());

_selectionActive = true;
_allowSingleCharSelection = (_copyOnSelect) ? false : true;

SetEndSelectionPosition(position);

_multiClickSelectionMode = SelectionExpansionMode::Cell;
Expand All @@ -230,6 +256,11 @@ void Terminal::SetEndSelectionPosition(const COORD position)
// copy value of ViewStartIndex to support scrolling
// and update on new buffer output (used in _GetSelectionRects())
_endSelectionPosition_YOffset = gsl::narrow<SHORT>(_ViewStartIndex());

if (_copyOnSelect && !_isSingleCellSelection())
{
_allowSingleCharSelection = true;
}
}

// Method Description:
Expand All @@ -246,6 +277,7 @@ void Terminal::SetBoxSelection(const bool isEnabled) noexcept
void Terminal::ClearSelection()
{
_selectionActive = false;
_allowSingleCharSelection = false;
_selectionAnchor = { 0, 0 };
_endSelectionPosition = { 0, 0 };
_selectionAnchor_YOffset = 0;
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettings/ICoreSettings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace Microsoft.Terminal.Settings
CursorStyle CursorShape;
UInt32 CursorHeight;
String WordDelimiters;
Boolean CopyOnSelect;
};

}
11 changes: 11 additions & 0 deletions src/cascadia/TerminalSettings/TerminalSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_cursorShape{ CursorStyle::Vintage },
_cursorHeight{ DEFAULT_CURSOR_HEIGHT },
_wordDelimiters{ DEFAULT_WORD_DELIMITERS },
_copyOnSelect{ false },
_useAcrylic{ false },
_closeOnExit{ true },
_tintOpacity{ 0.5 },
Expand Down Expand Up @@ -149,6 +150,16 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_wordDelimiters = value;
}

bool TerminalSettings::CopyOnSelect()
{
return _copyOnSelect;
}

void TerminalSettings::CopyOnSelect(bool value)
{
_copyOnSelect = value;
}

bool TerminalSettings::UseAcrylic()
{
return _useAcrylic;
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalSettings/terminalsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
void CursorHeight(uint32_t value);
hstring WordDelimiters();
void WordDelimiters(hstring const& value);
bool CopyOnSelect();
void CopyOnSelect(bool value);
// ------------------------ End of Core Settings -----------------------

bool UseAcrylic();
Expand Down Expand Up @@ -116,6 +118,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
winrt::Windows::UI::Xaml::Media::Stretch _backgroundImageStretchMode;
winrt::Windows::UI::Xaml::HorizontalAlignment _backgroundImageHorizontalAlignment;
winrt::Windows::UI::Xaml::VerticalAlignment _backgroundImageVerticalAlignment;
bool _copyOnSelect;
hstring _commandline;
hstring _startingDir;
hstring _startingTitle;
Expand Down
Loading

0 comments on commit ff87190

Please sign in to comment.