Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@
"moveFocus",
"movePane",
"swapPane",
"markMode",
"moveTab",
"multipleActions",
"newTab",
Expand Down
10 changes: 10 additions & 0 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1043,4 +1043,14 @@ namespace winrt::TerminalApp::implementation
args.Handled(true);
}
}

void TerminalPage::_HandleMarkMode(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& control{ _GetActiveControl() })
{
control.ToggleMarkMode();
args.Handled(true);
}
}
}
24 changes: 22 additions & 2 deletions src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,11 +392,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
vkey != VK_SNAPSHOT &&
keyDown)
{
if (_terminal->IsInMarkMode() && modifiers.IsCtrlPressed() && vkey == 'A')
{
auto lock = _terminal->LockForWriting();
_terminal->SelectAll();
_renderer->TriggerSelection();
Comment on lines +397 to +399

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised we don't have a helper for this!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I think at one point I made (or witnessed) the decision that triggering the renderer and (un)locking would be handled at the control core layer consistently to ensure we don't deadlock on accident. It's a little annoying, but we could swing the pendulum the other way hahaha

return true;
}

// try to update the selection
if (const auto updateSlnParams{ ::Terminal::ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
{
auto lock = _terminal->LockForWriting();
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second);
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers);
_renderer->TriggerSelection();
return true;
}
Expand Down Expand Up @@ -1001,6 +1009,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_renderer->TriggerSelection();
}

void ControlCore::ToggleMarkMode()
{
auto lock = _terminal->LockForWriting();
_terminal->ToggleMarkMode();
_renderer->TriggerSelection();
}

bool ControlCore::IsInMarkMode() const
{
return _terminal->IsInMarkMode();
}

// Method Description:
// - Pre-process text pasted (presumably from the clipboard)
// before sending it over the terminal's connection.
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/ControlCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void PasteText(const winrt::hstring& hstr);
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
void SelectAll();
void ToggleMarkMode();
bool IsInMarkMode() const;

void GotFocus();
void LostFocus();
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/ControlCore.idl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ namespace Microsoft.Terminal.Control
void SendInput(String text);
void PasteText(String text);
void SelectAll();
void ToggleMarkMode();
void ClearBuffer(ClearBufferType clearType);
Boolean IsInMarkMode();

void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
void ClearHoveredCell();
Expand Down
9 changes: 7 additions & 2 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
// Manually show the cursor when a key is pressed. Restarting
// the timer prevents flickering.
_core.CursorOn(true);
_core.CursorOn(!_core.IsInMarkMode());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this right? On is literally the on/off blink state of the cursor (e.g. when it's drawn, it's on, then it blinks to off and is not drawn, then blinks back to on)

So we don't want the cursor to be shown at all when in mark mode?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's intentional (but definitely up for discussion on the design, actually). I'll just rapid fire random thoughts in my head for why I feel this is the desired behavior...

  • the cursor is a bit distracting when it's blinking if you're in mark mode
  • preventing the cursor from blinking makes it feel more clear that you're in a different mode (though we're looking to add the markers in Add selection marker overlays for keyboard selection #10865 )
  • when in mark mode, the "cursor" should really be the selection marker (just like in a text editor). So there's no reason to show the actual cursor.
    • [Accessibility] we do need to track the cursor movements for some things. Maybe we should actually be moving the cursor along with the selection? Idk. I'll have to test this out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right I forgot about the overlays. Good point.

_cursorTimer->Start();
}

Expand Down Expand Up @@ -1594,7 +1594,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (_cursorTimer)
{
// When the terminal focuses, show the cursor immediately
_core.CursorOn(true);
_core.CursorOn(!_core.IsInMarkMode());
_cursorTimer->Start();
}

Expand Down Expand Up @@ -1845,6 +1845,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core.SelectAll();
}

void TermControl::ToggleMarkMode()
{
_core.ToggleMarkMode();
}

void TermControl::Close()
{
if (!_IsClosing())
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/TermControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
void PasteTextFromClipboard();
void SelectAll();
void ToggleMarkMode();
void Close();
Windows::Foundation::Size CharacterDimensions() const;
Windows::Foundation::Size MinimumSize();
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/TermControl.idl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ namespace Microsoft.Terminal.Control
Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference<CopyFormat> formats);
void PasteTextFromClipboard();
void SelectAll();
void ToggleMarkMode();
void ClearBuffer(ClearBufferType clearType);
void Close();
Windows.Foundation.Size CharacterDimensions { get; };
Expand Down
3 changes: 2 additions & 1 deletion src/cascadia/TerminalCore/Terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Terminal::Terminal() :
_snapOnInput{ true },
_altGrAliasing{ true },
_blockSelection{ false },
_markMode{ false },
_selection{ std::nullopt },
_taskbarState{ 0 },
_taskbarProgress{ 0 },
Expand Down Expand Up @@ -1328,7 +1329,7 @@ void Terminal::SetCursorOn(const bool isOn)
bool Terminal::IsCursorBlinkingAllowed() const noexcept
{
const auto& cursor = _activeBuffer().GetCursor();
return cursor.IsBlinkingAllowed();
return !_markMode && cursor.IsBlinkingAllowed();
}

// Method Description:
Expand Down
7 changes: 5 additions & 2 deletions src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,13 @@ class Microsoft::Terminal::Core::Terminal final :
void SetSelectionAnchor(const COORD position);
void SetSelectionEnd(const COORD position, std::optional<SelectionExpansion> newExpansionMode = std::nullopt);
void SetBlockSelection(const bool isEnabled) noexcept;
void UpdateSelection(SelectionDirection direction, SelectionExpansion mode);
void UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods);
void SelectAll();
bool IsInMarkMode() const;
void ToggleMarkMode();

using UpdateSelectionParams = std::optional<std::pair<SelectionDirection, SelectionExpansion>>;
static UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey);
UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const;

const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace);
#pragma endregion
Expand Down Expand Up @@ -313,6 +315,7 @@ class Microsoft::Terminal::Core::Terminal final :
bool _blockSelection;
std::wstring _wordDelimiters;
SelectionExpansion _multiClickSelectionMode;
bool _markMode;
#pragma endregion

std::unique_ptr<TextBuffer> _mainBuffer;
Expand Down
66 changes: 59 additions & 7 deletions src/cascadia/TerminalCore/TerminalSelection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,40 @@ void Terminal::SetBlockSelection(const bool isEnabled) noexcept
_blockSelection = isEnabled;
}

Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey)
bool Terminal::IsInMarkMode() const
{
if (mods.IsShiftPressed() && !mods.IsAltPressed())
return _markMode;
}

void Terminal::ToggleMarkMode()
{
if (_markMode)
{
// Exit Mark Mode
ClearSelection();
}
else
{
// Enter Mark Mode
// NOTE: directly set cursor state. We already should have locked before calling this function.
_activeBuffer().GetCursor().SetIsOn(false);
const auto cursorPos{ _activeBuffer().GetCursor().GetPosition() };
_selection = SelectionAnchors{};
_selection->start = cursorPos;
_selection->end = cursorPos;
_selection->pivot = cursorPos;
_markMode = true;
}
}

Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const
{
if ((_markMode || mods.IsShiftPressed()) && !mods.IsAltPressed())
{
if (mods.IsCtrlPressed())
{
// Ctrl + Shift + _
// (Mark Mode) Ctrl + _
switch (vkey)
{
case VK_LEFT:
Expand All @@ -257,6 +284,7 @@ Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams
else
{
// Shift + _
// (Mark Mode) Just the vkeys
switch (vkey)
{
case VK_HOME:
Expand All @@ -281,11 +309,19 @@ Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams
return std::nullopt;
}

void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion mode)
// Method Description:
// - updates the selection endpoints based on a direction and expansion mode. Primarily used for keyboard selection.
// Arguments:
// - direction: the direction to move the selection endpoint in
// - mode: the type of movement to be performed (i.e. move by word)
// - mods: the key modifiers pressed when performing this update
void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods)
{
// 1. Figure out which endpoint to update
// One of the endpoints is the pivot, signifying that the other endpoint is the one we want to move.
const auto movingEnd{ _selection->start == _selection->pivot };
// If we're in mark mode, shift dictates whether you are moving the end or not.
// Otherwise, we're updating an existing selection, so one of the endpoints is the pivot,
// signifying that the other endpoint is the one we want to move.
const auto movingEnd{ _markMode ? mods.IsShiftPressed() : _selection->start == _selection->pivot };
auto targetPos{ movingEnd ? _selection->end : _selection->start };

// 2. Perform the movement
Expand All @@ -307,8 +343,23 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion

// 3. Actually modify the selection
// NOTE: targetStart doesn't matter here
auto targetStart = false;
std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);
if (_markMode)
{
// [Mark Mode]
// - moveSelectionEnd --> just move end (i.e. shift + arrow keys)
// - !moveSelectionEnd --> move all three (i.e. just use arrow keys)
_selection->end = targetPos;
if (!movingEnd)
{
_selection->start = targetPos;
_selection->pivot = targetPos;
}
}
else
{
auto targetStart = false;
std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);
}

// 4. Scroll (if necessary)
if (const auto viewport = _GetVisibleViewport(); !viewport.IsInBounds(targetPos))
Expand Down Expand Up @@ -473,6 +524,7 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, COORD& pos)
void Terminal::ClearSelection()
{
_selection = std::nullopt;
_markMode = false;
}

// Method Description:
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ static constexpr std::string_view QuitKey{ "quit" };
static constexpr std::string_view AdjustOpacityKey{ "adjustOpacity" };
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 ActionKey{ "action" };

Expand Down Expand Up @@ -388,6 +389,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::AdjustOpacity, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::RestoreLastClosed, RS_(L"RestoreLastClosedCommandKey") },
{ ShortcutAction::SelectAll, RS_(L"SelectAllCommandKey") },
{ ShortcutAction::MarkMode, RS_(L"MarkModeCommandKey") },
};
}();

Expand Down
3 changes: 2 additions & 1 deletion src/cascadia/TerminalSettingsModel/AllShortcutActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@
ON_ALL_ACTIONS(Quit) \
ON_ALL_ACTIONS(AdjustOpacity) \
ON_ALL_ACTIONS(RestoreLastClosed) \
ON_ALL_ACTIONS(SelectAll)
ON_ALL_ACTIONS(SelectAll) \
ON_ALL_ACTIONS(MarkMode)

#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,4 +525,8 @@
<data name="SelectAllCommandKey" xml:space="preserve">
<value>Select all text</value>
</data>
<data name="MarkModeCommandKey" xml:space="preserve">
<value>Toggle mark mode</value>
<comment>A command that will toggle "mark mode". This is a mode in the application where the user can mark the text by selecting portions of the text.</comment>
</data>
</root>
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@
{ "command": "paste", "keys": "ctrl+shift+v" },
{ "command": "paste", "keys": "shift+insert" },
{ "command": "selectAll", "keys": "ctrl+shift+a" },
{ "command": "markMode", "keys": "ctrl+shift+m" },

// Scrollback
{ "command": "scrollDown", "keys": "ctrl+shift+down" },
Expand Down