Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added CopyOnSelect as a Global Setting #2152

Merged
13 commits merged into from
Aug 20, 2019
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;
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
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