Introduce `switchSelectionEndpoint` action (#13370)

## Summary of the Pull Request
Introduces the `switchSelectionEndpoint` action which switches whichever selection endpoint is targeted when a selection is present. For example, if you are targeting "start", `switchSelectionEndpoint` makes it so that now you are targeting "end". This also updates the selection markers appropriately.

## References
Spec - #5804 
#13358
Closes #3663

## Detailed Description of the Pull Request / Additional comments
Most of the code is just standard code of adding a new action. Other than that, we have...
- if there is no selection, the action fails and the keybinding is passed through (similar to `copy()`)
- when we update the selection endpoint, we need to also update the "pivot". This ensures that future calls of `UpdateSelection()` respect this swap.
- [Corner Case] if the cursor is being moved, we make it so that you basically "anchored" an endpoint and you don't have to hold shift anymore.
This commit is contained in:
Carlos Zamora 2022-06-30 18:44:39 -07:00 коммит произвёл GitHub
Родитель d6a9e9ffb6
Коммит 7b8a53c030
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 77 добавлений и 3 удалений

Просмотреть файл

@ -354,6 +354,7 @@
"toggleFocusMode",
"selectAll",
"setFocusMode",
"switchSelectionEndpoint",
"toggleFullscreen",
"setFullScreen",
"setMaximized",

Просмотреть файл

@ -1112,4 +1112,14 @@ namespace winrt::TerminalApp::implementation
args.Handled(handled);
}
}
void TerminalPage::_HandleSwitchSelectionEndpoint(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& control{ _GetActiveControl() })
{
const auto handled = control.SwitchSelectionEndpoint();
args.Handled(handled);
}
}
}

Просмотреть файл

@ -1089,6 +1089,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return static_cast<Control::SelectionInteractionMode>(_terminal->SelectionMode());
}
bool ControlCore::SwitchSelectionEndpoint()
{
if (_terminal->IsSelectionActive())
{
_terminal->SwitchSelectionEndpoint();
return true;
}
return false;
}
// Method Description:
// - Pre-process text pasted (presumably from the clipboard)
// before sending it over the terminal's connection.

Просмотреть файл

@ -86,6 +86,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ToggleBlockSelection();
void ToggleMarkMode();
Control::SelectionInteractionMode SelectionMode() const;
bool SwitchSelectionEndpoint();
void GotFocus();
void LostFocus();

Просмотреть файл

@ -92,6 +92,7 @@ namespace Microsoft.Terminal.Control
void ClearSelection();
Boolean ToggleBlockSelection();
void ToggleMarkMode();
Boolean SwitchSelectionEndpoint();
void ClearBuffer(ClearBufferType clearType);
void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);

Просмотреть файл

@ -1929,6 +1929,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core.ToggleMarkMode();
}
bool TermControl::SwitchSelectionEndpoint()
{
return _core.SwitchSelectionEndpoint();
}
void TermControl::Close()
{
if (!_IsClosing())

Просмотреть файл

@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SelectAll();
bool ToggleBlockSelection();
void ToggleMarkMode();
bool SwitchSelectionEndpoint();
void Close();
Windows::Foundation::Size CharacterDimensions() const;
Windows::Foundation::Size MinimumSize();

Просмотреть файл

@ -53,6 +53,7 @@ namespace Microsoft.Terminal.Control
void SelectAll();
Boolean ToggleBlockSelection();
void ToggleMarkMode();
Boolean SwitchSelectionEndpoint();
void ClearBuffer(ClearBufferType clearType);
void Close();
Windows.Foundation.Size CharacterDimensions { get; };

Просмотреть файл

@ -48,6 +48,7 @@ Terminal::Terminal() :
_selectionMode{ SelectionInteractionMode::None },
_selection{ std::nullopt },
_selectionEndpoint{ static_cast<SelectionEndpoint>(0) },
_anchorInactiveSelectionEndpoint{ false },
_taskbarState{ 0 },
_taskbarProgress{ 0 },
_trimBlockSelection{ false },

Просмотреть файл

@ -271,6 +271,7 @@ public:
void UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods);
void SelectAll();
SelectionInteractionMode SelectionMode() const noexcept;
void SwitchSelectionEndpoint();
void ToggleMarkMode();
using UpdateSelectionParams = std::optional<std::pair<SelectionDirection, SelectionExpansion>>;
@ -349,6 +350,7 @@ private:
SelectionExpansion _multiClickSelectionMode;
SelectionInteractionMode _selectionMode;
SelectionEndpoint _selectionEndpoint;
bool _anchorInactiveSelectionEndpoint;
#pragma endregion
std::unique_ptr<TextBuffer> _mainBuffer;

Просмотреть файл

@ -309,6 +309,33 @@ void Terminal::ToggleMarkMode()
}
}
// Method Description:
// - switch the targeted selection endpoint with the other one (i.e. start <--> end)
void Terminal::SwitchSelectionEndpoint()
{
if (IsSelectionActive())
{
if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start) && WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::End))
{
// moving cursor --> anchor start, move end
_selectionEndpoint = SelectionEndpoint::End;
_anchorInactiveSelectionEndpoint = true;
}
else if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::End))
{
// moving end --> now we're moving start
_selectionEndpoint = SelectionEndpoint::Start;
_selection->pivot = _selection->end;
}
else if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start))
{
// moving start --> now we're moving end
_selectionEndpoint = SelectionEndpoint::End;
_selection->pivot = _selection->start;
}
}
}
Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const
{
if ((_selectionMode == SelectionInteractionMode::Mark || mods.IsShiftPressed()) && !mods.IsAltPressed())
@ -365,6 +392,12 @@ Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams
// - mods: the key modifiers pressed when performing this update
void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods)
{
// This is a special variable used to track if we should move the cursor when in mark mode.
// We have special functionality where if you use the "switchSelectionEndpoint" action
// when in mark mode (moving the cursor), we anchor an endpoint and you can use the
// plain arrow keys to move the endpoint. This way, you don't have to hold shift anymore!
const bool shouldMoveBothEndpoints = _selectionMode == SelectionInteractionMode::Mark && !_anchorInactiveSelectionEndpoint && !mods.IsShiftPressed();
// 1. Figure out which endpoint to update
// [Mark Mode]
// - shift pressed --> only move "end" (or "start" if "pivot" == "end")
@ -372,7 +405,7 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
// [Quick Edit]
// - just move "end" (or "start" if "pivot" == "end")
_selectionEndpoint = static_cast<SelectionEndpoint>(0);
if (_selectionMode == SelectionInteractionMode::Mark && !mods.IsShiftPressed())
if (shouldMoveBothEndpoints)
{
WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End);
}
@ -405,7 +438,7 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
// 3. Actually modify the selection state
_selectionMode = std::max(_selectionMode, SelectionInteractionMode::Keyboard);
if (_selectionMode == SelectionInteractionMode::Mark && !mods.IsShiftPressed())
if (shouldMoveBothEndpoints)
{
// [Mark Mode] + shift unpressed --> move all three (i.e. just use arrow keys)
_selection->start = targetPos;
@ -598,6 +631,7 @@ void Terminal::ClearSelection()
_selection = std::nullopt;
_selectionMode = SelectionInteractionMode::None;
_selectionEndpoint = static_cast<SelectionEndpoint>(0);
_anchorInactiveSelectionEndpoint = false;
}
// Method Description:

Просмотреть файл

@ -82,6 +82,7 @@ static constexpr std::string_view RestoreLastClosedKey{ "restoreLastClosed" };
static constexpr std::string_view SelectAllKey{ "selectAll" };
static constexpr std::string_view MarkModeKey{ "markMode" };
static constexpr std::string_view ToggleBlockSelectionKey{ "toggleBlockSelection" };
static constexpr std::string_view SwitchSelectionEndpointKey{ "switchSelectionEndpoint" };
static constexpr std::string_view ActionKey{ "action" };
@ -400,6 +401,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::SelectAll, RS_(L"SelectAllCommandKey") },
{ ShortcutAction::MarkMode, RS_(L"MarkModeCommandKey") },
{ ShortcutAction::ToggleBlockSelection, RS_(L"ToggleBlockSelectionCommandKey") },
{ ShortcutAction::SwitchSelectionEndpoint, RS_(L"SwitchSelectionEndpointCommandKey") },
};
}();

Просмотреть файл

@ -95,7 +95,8 @@
ON_ALL_ACTIONS(RestoreLastClosed) \
ON_ALL_ACTIONS(SelectAll) \
ON_ALL_ACTIONS(MarkMode) \
ON_ALL_ACTIONS(ToggleBlockSelection)
ON_ALL_ACTIONS(ToggleBlockSelection) \
ON_ALL_ACTIONS(SwitchSelectionEndpoint)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \

Просмотреть файл

@ -564,4 +564,7 @@
<data name="ToggleBlockSelectionCommandKey" xml:space="preserve">
<value>Toggle block selection</value>
</data>
<data name="SwitchSelectionEndpointCommandKey" xml:space="preserve">
<value>Switch selection endpoint</value>
</data>
</root>

Просмотреть файл

@ -388,6 +388,7 @@
{ "command": "selectAll", "keys": "ctrl+shift+a" },
{ "command": "markMode", "keys": "ctrl+shift+m" },
{ "command": "toggleBlockSelection" },
{ "command": "switchSelectionEndpoint" },
// Scrollback
{ "command": "scrollDown", "keys": "ctrl+shift+down" },