From 7abed9f6fb100f77c31eee4bbcb88ee9b01cf4e5 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 12 Jul 2022 12:51:37 -0700 Subject: [PATCH] Add keyboard navigation to hyperlinks (#13405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of the Pull Request Adds support to navigate to clickable hyperlinks using only the keyboard. When in mark mode, the user can press [shift+]tab to go the previous/next hyperlink in the text buffer. Once a hyperlink is selected, the user can press Ctrl+Enter to open the hyperlink. ## References #4993 ## PR Checklist * [x] Closes #6649 * [x] Documentation updated at https://github.com/MicrosoftDocs/terminal/pull/558 ## Detailed Description of the Pull Request / Additional comments - Main change - The `OpenHyperlink` event needs to be piped down to `ControlCore` now so that we can open a hyperlink at that layer. - `SelectHyperlink(dir)` searches the buffer in a given direction and finds the first hyperlink, then selects it. - "finding the hyperlink" is the tough part because the pattern tree takes in buffer coordinates, searches through the buffer in that given space, then stores everything relative to the defined space. Normally, the given buffer coordinates would align with the viewport's start and end. However, we're now trying to search outside of the viewport (sometimes), so we need to manage two coordinate systems at the same time. - `convertToSearchArea()` lambda was used to convert a given coordinate into the search area coordinate system. So if the search area is the visible viewport, we spit out a viewport position. If the search area is the _next_ viewport, we spit out a position relative to that. - `extractResultFromList()` lambda takes the list of patterns from the pattern tree and spits out the hyperlink we want. If we're searching forwards, we get the next one. Otherwise, we get the previous one. We explicitly ignore the one we're already on. If we don't find any, we return `nullopt`. - Now that we have all these cool tools, we use them to progressively search through the buffer to find the next/previous hyperlink. Start by searching the visible viewport _after_ (or _before_) the current selection. If we can't find anything, go to the next "page" (viewport scrolled up/down). Repeat this process until something comes up. - If we can't find anything, nothing happens. We don't wrap around. - Other relevant changes - the `copy` action is no longer bound to `Enter`. Instead, we manually handle it in `ControlCore.cpp`. This also lets us handle Shift+Enter appropriately without having to take another key binding. - `_ScrollToPoint` was added. It's a simple function that just scrolls the viewport such that the provided buffer position is in view. This was used to de-duplicate code. - `_ScrollToPoint` was added into the `ToggleMarkMode()` function. Turns out, we don't scroll to the new selection when we enter mark mode (whoops!). We _should_ do that and we should backport this part of the change too. I'll handle that. - add some clarity when some functions are using the viewport position vs the buffer position. This is important because the pattern interval tree works in the viewport space. ## Validation Steps Performed - case: all hyperlinks are in the view - ✅ get next link - ✅ get prev link - ✅ case: need to scroll down for next link - ✅ case: need to scroll up for next link --- src/cascadia/TerminalControl/ControlCore.cpp | 52 ++++- src/cascadia/TerminalControl/ControlCore.h | 1 + src/cascadia/TerminalControl/ControlCore.idl | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 1 + src/cascadia/TerminalCore/Terminal.cpp | 28 ++- src/cascadia/TerminalCore/Terminal.hpp | 17 +- .../TerminalCore/TerminalSelection.cpp | 197 ++++++++++++++++-- .../TerminalSettingsModel/defaults.json | 1 - 8 files changed, 257 insertions(+), 42 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index f71eef421f7..c2506f2616e 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -417,13 +417,43 @@ namespace winrt::Microsoft::Terminal::Control::implementation vkey != VK_SNAPSHOT && keyDown) { - const auto isInMarkMode = _terminal->SelectionMode() == ::Microsoft::Terminal::Core::Terminal::SelectionInteractionMode::Mark; - if (isInMarkMode && modifiers.IsCtrlPressed() && vkey == 'A') + const auto isInMarkMode = _terminal->SelectionMode() == ::Terminal::SelectionInteractionMode::Mark; + if (isInMarkMode) { - auto lock = _terminal->LockForWriting(); - _terminal->SelectAll(); - _updateSelectionUI(); - return true; + if (modifiers.IsCtrlPressed() && vkey == 'A') + { + // Ctrl + A --> Select all + auto lock = _terminal->LockForWriting(); + _terminal->SelectAll(); + _updateSelectionUI(); + return true; + } + else if (vkey == VK_TAB && _settings->DetectURLs()) + { + // [Shift +] Tab --> next/previous hyperlink + auto lock = _terminal->LockForWriting(); + const auto direction = modifiers.IsShiftPressed() ? ::Terminal::SearchDirection::Backward : ::Terminal::SearchDirection::Forward; + _terminal->SelectHyperlink(direction); + _updateSelectionUI(); + return true; + } + else if (vkey == VK_RETURN && modifiers.IsCtrlPressed() && _terminal->IsTargetingUrl()) + { + // Ctrl + Enter --> Open URL + auto lock = _terminal->LockForReading(); + const auto uri = _terminal->GetHyperlinkAtBufferPosition(_terminal->GetSelectionAnchor()); + _OpenHyperlinkHandlers(*this, winrt::make(winrt::hstring{ uri })); + return true; + } + else if (vkey == VK_RETURN) + { + // [Shift +] Enter --> copy text + // Don't lock here! CopySelectionToClipboard already locks for you! + CopySelectionToClipboard(modifiers.IsShiftPressed(), nullptr); + _terminal->ClearSelection(); + _updateSelectionUI(); + return true; + } } // try to update the selection @@ -591,12 +621,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation _lastHoveredCell = terminalPosition; uint16_t newId{ 0u }; // we can't use auto here because we're pre-declaring newInterval. - decltype(_terminal->GetHyperlinkIntervalFromPosition({})) newInterval{ std::nullopt }; + decltype(_terminal->GetHyperlinkIntervalFromViewportPosition({})) newInterval{ std::nullopt }; if (terminalPosition.has_value()) { auto lock = _terminal->LockForReading(); // Lock for the duration of our reads. - newId = _terminal->GetHyperlinkIdAtPosition(*terminalPosition); - newInterval = _terminal->GetHyperlinkIntervalFromPosition(*terminalPosition); + newId = _terminal->GetHyperlinkIdAtViewportPosition(*terminalPosition); + newInterval = _terminal->GetHyperlinkIntervalFromViewportPosition(*terminalPosition); } // If the hyperlink ID changed or the interval changed, trigger a redraw all @@ -626,7 +656,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // Lock for the duration of our reads. auto lock = _terminal->LockForReading(); - return winrt::hstring{ _terminal->GetHyperlinkAtPosition(til::point{ pos }) }; + return winrt::hstring{ _terminal->GetHyperlinkAtViewportPosition(til::point{ pos }) }; } winrt::hstring ControlCore::HoveredUriText() const @@ -634,7 +664,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto lock = _terminal->LockForReading(); // Lock for the duration of our reads. if (_lastHoveredCell.has_value()) { - return winrt::hstring{ _terminal->GetHyperlinkAtPosition(*_lastHoveredCell) }; + return winrt::hstring{ _terminal->GetHyperlinkAtViewportPosition(*_lastHoveredCell) }; } return {}; } diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 948a591bfc4..c8ba9b88d7d 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -218,6 +218,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(FoundMatch, IInspectable, Control::FoundResultsArgs); TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs); TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs); + TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); // clang-format on private: diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 1d7801bdad4..ec8230fa180 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -153,6 +153,6 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler FoundMatch; event Windows.Foundation.TypedEventHandler ShowWindowChanged; event Windows.Foundation.TypedEventHandler UpdateSelectionMarkers; - + event Windows.Foundation.TypedEventHandler OpenHyperlink; }; } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 821f8885e24..943190cfaf2 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -84,6 +84,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged }); _core.FoundMatch({ this, &TermControl::_coreFoundMatch }); _core.UpdateSelectionMarkers({ this, &TermControl::_updateSelectionMarkers }); + _core.OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); _interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); _interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index d41e4f53b65..f8dce231a3a 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -46,6 +46,7 @@ Terminal::Terminal() : _altGrAliasing{ true }, _blockSelection{ false }, _selectionMode{ SelectionInteractionMode::None }, + _isTargetingUrl{ false }, _selection{ std::nullopt }, _selectionEndpoint{ static_cast(0) }, _anchorInactiveSelectionEndpoint{ false }, @@ -563,17 +564,24 @@ bool Terminal::ShouldSendAlternateScroll(const unsigned int uiButton, // Method Description: // - Given a coord, get the URI at that location // Arguments: -// - The position -std::wstring Terminal::GetHyperlinkAtPosition(const til::point position) +// - The position relative to the viewport +std::wstring Terminal::GetHyperlinkAtViewportPosition(const til::point viewportPos) { - auto attr = _activeBuffer().GetCellDataAt(_ConvertToBufferCell(position))->TextAttr(); + return GetHyperlinkAtBufferPosition(_ConvertToBufferCell(viewportPos)); +} + +std::wstring Terminal::GetHyperlinkAtBufferPosition(const til::point bufferPos) +{ + auto attr = _activeBuffer().GetCellDataAt(bufferPos)->TextAttr(); if (attr.IsHyperlink()) { auto uri = _activeBuffer().GetHyperlinkUriFromId(attr.GetHyperlinkId()); return uri; } // also look through our known pattern locations in our pattern interval tree - const auto result = GetHyperlinkIntervalFromPosition(position); + auto viewportPos = bufferPos; + _GetVisibleViewport().ConvertToOrigin(&viewportPos); + const auto result = GetHyperlinkIntervalFromViewportPosition(viewportPos); if (result.has_value() && result->value == _hyperlinkPatternId) { const auto start = result->start; @@ -594,23 +602,23 @@ std::wstring Terminal::GetHyperlinkAtPosition(const til::point position) // Method Description: // - Gets the hyperlink ID of the text at the given terminal position // Arguments: -// - The position of the text +// - The position of the text relative to the viewport // Return value: // - The hyperlink ID -uint16_t Terminal::GetHyperlinkIdAtPosition(const til::point position) +uint16_t Terminal::GetHyperlinkIdAtViewportPosition(const til::point viewportPos) { - return _activeBuffer().GetCellDataAt(_ConvertToBufferCell(position))->TextAttr().GetHyperlinkId(); + return _activeBuffer().GetCellDataAt(_ConvertToBufferCell(viewportPos))->TextAttr().GetHyperlinkId(); } // Method description: // - Given a position in a URI pattern, gets the start and end coordinates of the URI // Arguments: -// - The position +// - The position relative to the viewport // Return value: // - The interval representing the start and end coordinates -std::optional Terminal::GetHyperlinkIntervalFromPosition(const til::point position) +std::optional Terminal::GetHyperlinkIntervalFromViewportPosition(const til::point viewportPos) { - const auto results = _patternIntervalTree.findOverlapping({ position.X + 1, position.Y }, position); + const auto results = _patternIntervalTree.findOverlapping({ viewportPos.X + 1, viewportPos.Y }, viewportPos); if (results.size() > 0) { for (const auto& result : results) diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index c63eb887b65..5dec2e85124 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -162,9 +162,10 @@ class Microsoft::Terminal::Core::Terminal final : void FocusChanged(const bool focused) noexcept override; - std::wstring GetHyperlinkAtPosition(const til::point position); - uint16_t GetHyperlinkIdAtPosition(const til::point position); - std::optional::interval> GetHyperlinkIntervalFromPosition(const til::point position); + std::wstring GetHyperlinkAtViewportPosition(const til::point viewportPos); + std::wstring GetHyperlinkAtBufferPosition(const til::point bufferPos); + uint16_t GetHyperlinkIdAtViewportPosition(const til::point viewportPos); + std::optional::interval> GetHyperlinkIntervalFromViewportPosition(const til::point viewportPos); #pragma endregion #pragma region IBaseData(base to IRenderData and IUiaData) @@ -249,6 +250,12 @@ class Microsoft::Terminal::Core::Terminal final : Down }; + enum class SearchDirection + { + Forward, + Backward + }; + enum class SelectionExpansion { Char, @@ -273,6 +280,8 @@ class Microsoft::Terminal::Core::Terminal final : SelectionInteractionMode SelectionMode() const noexcept; void SwitchSelectionEndpoint(); void ToggleMarkMode(); + void SelectHyperlink(const SearchDirection dir); + bool IsTargetingUrl() const noexcept; using UpdateSelectionParams = std::optional>; UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const; @@ -349,6 +358,7 @@ class Microsoft::Terminal::Core::Terminal final : std::wstring _wordDelimiters; SelectionExpansion _multiClickSelectionMode; SelectionInteractionMode _selectionMode; + bool _isTargetingUrl; SelectionEndpoint _selectionEndpoint; bool _anchorInactiveSelectionEndpoint; #pragma endregion @@ -424,6 +434,7 @@ class Microsoft::Terminal::Core::Terminal final : std::pair _PivotSelection(const til::point targetPos, bool& targetStart) const; std::pair _ExpandSelectionAnchors(std::pair anchors) const; til::point _ConvertToBufferCell(const til::point viewportPos) const; + void _ScrollToPoint(const til::point pos); void _MoveByChar(SelectionDirection direction, til::point& pos); void _MoveByWord(SelectionDirection direction, til::point& pos); void _MoveByViewport(SelectionDirection direction, til::point& pos); diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index 007a26b0c3f..dd156b463c4 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -6,6 +6,7 @@ #include "unicode.hpp" using namespace Microsoft::Terminal::Core; +using namespace Microsoft::Console::Types; DEFINE_ENUM_FLAG_OPERATORS(Terminal::SelectionEndpoint); @@ -303,8 +304,10 @@ void Terminal::ToggleMarkMode() _selection->start = cursorPos; _selection->end = cursorPos; _selection->pivot = cursorPos; + _ScrollToPoint(cursorPos); _selectionMode = SelectionInteractionMode::Mark; _blockSelection = false; + _isTargetingUrl = false; WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End); } } @@ -336,6 +339,157 @@ void Terminal::SwitchSelectionEndpoint() } } +// Method Description: +// - selects the next/previous hyperlink, if one is available +// Arguments: +// - dir: the direction we're scanning the buffer in to find the hyperlink of interest +// Return Value: +// - true if we found a hyperlink to select (and selected it). False otherwise. +void Terminal::SelectHyperlink(const SearchDirection dir) +{ + if (_selectionMode != SelectionInteractionMode::Mark) + { + // This feature only works in mark mode + _isTargetingUrl = false; + return; + } + + // 0. Useful tools/vars + const auto bufferSize = _activeBuffer().GetSize(); + const auto viewportHeight = _GetMutableViewport().Height(); + + // The patterns are stored relative to the "search area". Initially, this search area will be the viewport, + // but as we progressively search through more of the buffer, this will change. + // Keep track of the search area here, and use the lambda below to convert points to the search area coordinate space. + auto searchArea = _GetVisibleViewport(); + auto convertToSearchArea = [&searchArea](const til::point pt) { + auto copy = pt; + searchArea.ConvertToOrigin(©); + return copy; + }; + + // extracts the next/previous hyperlink from the list of hyperlink ranges provided + auto extractResultFromList = [&](std::vector>& list) { + const auto selectionStartInSearchArea = convertToSearchArea(_selection->start); + + std::optional> resultFromList; + if (!list.empty()) + { + if (dir == SearchDirection::Forward) + { + // pattern tree includes the currently selected range when going forward, + // so we need to check if we're pointing to that one before returning it. + auto range = list.front(); + if (_isTargetingUrl && range.start == selectionStartInSearchArea) + { + if (list.size() > 1) + { + // if we're pointing to the currently selected URL, + // pick the next one. + range = til::at(list, 1); + resultFromList = { range.start, range.stop }; + } + else + { + // LOAD-BEARING: the only range here is the one that's currently selected. + // Make sure this is set to nullopt so that we keep searching through the buffer. + resultFromList = std::nullopt; + } + } + else + { + // not on currently selected range, return the first one + resultFromList = { range.start, range.stop }; + } + } + else if (dir == SearchDirection::Backward) + { + // moving backwards excludes the currently selected range, + // simply return the last one in the list as it's ordered + const auto range = list.back(); + resultFromList = { range.start, range.stop }; + } + } + + // pattern tree stores everything as viewport coords, + // so we need to convert them on the way out + if (resultFromList) + { + searchArea.ConvertFromOrigin(&resultFromList->first); + searchArea.ConvertFromOrigin(&resultFromList->second); + } + return resultFromList; + }; + + // 1. Look for the hyperlink + til::point searchStart = dir == SearchDirection::Forward ? _selection->start : til::point{ bufferSize.Left(), _VisibleStartIndex() }; + til::point searchEnd = dir == SearchDirection::Forward ? til::point{ bufferSize.RightInclusive(), _VisibleEndIndex() } : _selection->start; + + // 1.A) Try searching the current viewport (no scrolling required) + auto resultList = _patternIntervalTree.findContained(convertToSearchArea(searchStart), convertToSearchArea(searchEnd)); + std::optional> result = extractResultFromList(resultList); + if (!result) + { + // 1.B) Incrementally search through more of the space + if (dir == SearchDirection::Forward) + { + searchStart = { bufferSize.Left(), searchEnd.y + 1 }; + searchEnd = { bufferSize.RightInclusive(), std::min(searchStart.y + viewportHeight, ViewEndIndex()) }; + } + else + { + searchEnd = { bufferSize.RightInclusive(), searchStart.y - 1 }; + searchStart = { bufferSize.Left(), std::max(searchStart.y - viewportHeight, bufferSize.Top()) }; + } + searchArea = Viewport::FromDimensions(searchStart, searchEnd.x + 1, searchEnd.y + 1); + + const til::point bufferStart{ bufferSize.Origin() }; + const til::point bufferEnd{ bufferSize.RightInclusive(), ViewEndIndex() }; + while (!result && bufferSize.IsInBounds(searchStart) && bufferSize.IsInBounds(searchEnd) && searchStart <= searchEnd && bufferStart <= searchStart && searchEnd <= bufferEnd) + { + auto patterns = _activeBuffer().GetPatterns(searchStart.y, searchEnd.y); + resultList = patterns.findContained(convertToSearchArea(searchStart), convertToSearchArea(searchEnd)); + result = extractResultFromList(resultList); + if (!result) + { + if (dir == SearchDirection::Forward) + { + searchStart.y += 1; + searchEnd.y = std::min(searchStart.y + viewportHeight, ViewEndIndex()); + } + else + { + searchEnd.y -= 1; + searchStart.y = std::max(searchEnd.y - viewportHeight, bufferSize.Top()); + } + searchArea = Viewport::FromDimensions(searchStart, searchEnd.x + 1, searchEnd.y + 1); + } + } + + // 1.C) Nothing was found. Bail! + if (!result.has_value()) + { + return; + } + } + + // 2. Select the hyperlink + _selection->start = result->first; + _selection->pivot = result->first; + _selection->end = result->second; + bufferSize.DecrementInBounds(_selection->end); + _isTargetingUrl = true; + _selectionEndpoint = SelectionEndpoint::End; + + // 3. Scroll to the selected area (if necessary) + _ScrollToPoint(_selection->end); +} + +bool Terminal::IsTargetingUrl() const noexcept +{ + return _isTargetingUrl; +} + Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const { if ((_selectionMode == SelectionInteractionMode::Mark || mods.IsShiftPressed()) && !mods.IsAltPressed()) @@ -437,6 +591,7 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion } // 3. Actually modify the selection state + _isTargetingUrl = false; _selectionMode = std::max(_selectionMode, SelectionInteractionMode::Keyboard); if (shouldMoveBothEndpoints) { @@ -459,22 +614,7 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion } // 4. Scroll (if necessary) - if (const auto visibleViewport = _GetVisibleViewport(); !visibleViewport.IsInBounds(targetPos)) - { - if (const auto amtAboveView = visibleViewport.Top() - targetPos.Y; amtAboveView > 0) - { - // anchor is above visible viewport, scroll by that amount - _scrollOffset += amtAboveView; - } - else - { - // anchor is below visible viewport, scroll by that amount - const auto amtBelowView = targetPos.Y - visibleViewport.BottomInclusive(); - _scrollOffset -= amtBelowView; - } - _NotifyScrollEvent(); - _activeBuffer().TriggerScroll(); - } + _ScrollToPoint(targetPos); } void Terminal::SelectAll() @@ -630,6 +770,7 @@ void Terminal::ClearSelection() { _selection = std::nullopt; _selectionMode = SelectionInteractionMode::None; + _isTargetingUrl = false; _selectionEndpoint = static_cast(0); _anchorInactiveSelectionEndpoint = false; } @@ -674,6 +815,30 @@ til::point Terminal::_ConvertToBufferCell(const til::point viewportPos) const return bufferPos; } +// Method Description: +// - if necessary, scroll the viewport such that the given point is visible +// Arguments: +// - pos: a coordinate relative to the buffer (not viewport) +void Terminal::_ScrollToPoint(const til::point pos) +{ + if (const auto visibleViewport = _GetVisibleViewport(); !visibleViewport.IsInBounds(pos)) + { + if (const auto amtAboveView = visibleViewport.Top() - pos.Y; amtAboveView > 0) + { + // anchor is above visible viewport, scroll by that amount + _scrollOffset += amtAboveView; + } + else + { + // anchor is below visible viewport, scroll by that amount + const auto amtBelowView = pos.Y - visibleViewport.BottomInclusive(); + _scrollOffset -= amtBelowView; + } + _NotifyScrollEvent(); + _activeBuffer().TriggerScroll(); + } +} + // Method Description: // - This method won't be used. We just throw and do nothing. For now we // need this method to implement UiaData interface diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 530a28b6ad3..7a672723c33 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -402,7 +402,6 @@ // Clipboard Integration { "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+shift+c" }, { "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+insert" }, - { "command": { "action": "copy", "singleLine": false }, "keys": "enter" }, { "command": "paste", "keys": "ctrl+shift+v" }, { "command": "paste", "keys": "shift+insert" }, { "command": "selectAll", "keys": "ctrl+shift+a" },