Compare commits

...

4 Commits

Author SHA1 Message Date
Dustin L. Howett
9eb8537aa8 Move autoscrolling down to Interactivity, make it a win32 TP timer 2023-09-20 18:37:56 -05:00
Dustin L. Howett
51fe036223 Trust ControlInteractivity to handle its own focus and bounds check
This removes "focused" and "pointerPressedInBounds" from the
ControlInteractivity API.
2023-09-20 18:37:56 -05:00
Dustin L. Howett
32d20b6287 Add Pointer IDs to the Interactivity layer 2023-09-20 18:37:56 -05:00
Dustin L. Howett
e8555da23d Move the Cursor and VT Blink timer down to ControlCore
It has access to its own dispatcher, so it can do this on its own.
We'll be able to remove the timer from the WPF control in the future as
well.

TODO:
- [ ] Fix "allow cursor while blurred"
- [ ] Fix a crash I observed trying to blink a nonexistent text buffer
  cursor ??? while splitting a bunch of panes all at once
- [ ] Test more
2023-09-20 18:37:56 -05:00
9 changed files with 400 additions and 380 deletions

View File

@@ -212,6 +212,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
core->_ScrollPositionChangedHandlers(*core, update);
}
});
_cursorTimer = _dispatcher.CreateTimer();
_blinkTimer = _dispatcher.CreateTimer();
_cursorTimer.IsRepeating(true);
_blinkTimer.IsRepeating(true);
_cursorTimer.Tick({ get_weak(), &ControlCore::_cursorTimerTick });
_blinkTimer.Tick({ get_weak(), &ControlCore::_blinkTimerTick });
}
ControlCore::~ControlCore()
@@ -237,6 +246,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
shared->tsfTryRedrawCanvas.reset();
shared->updatePatternLocations.reset();
shared->updateScrollBar.reset();
if (_cursorTimer)
{
_cursorTimer.Stop();
_cursorTimer = nullptr;
}
if (_blinkTimer)
{
_blinkTimer.Stop();
_blinkTimer = nullptr;
}
}
void ControlCore::AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings)
@@ -410,6 +429,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_initializedTerminal.store(true, std::memory_order_relaxed);
} // scope for TerminalLock
// Set the original intervals and start the timers
_updateTimers();
// Start the connection outside of lock, because it could
// start writing output immediately.
_connection.Start();
@@ -634,6 +656,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
// Force the cursor to be visible
if (_cursorTimer)
{
CursorOn(SelectionMode() != SelectionInteractionMode::Mark);
_cursorTimer.Start();
}
// If the terminal translated the key, mark the event as handled.
// This will prevent the system from trying to get the character out
// of it and sending us a CharacterReceived event.
@@ -1438,6 +1467,46 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _terminal->GetBufferHeight();
}
void ControlCore::_updateTimers()
{
if (_cursorTimer)
{
_cursorTimer.Interval(_cursorBlinkTime);
if (_cursorBlinkTime == std::chrono::milliseconds(0))
{
_cursorTimer.Stop();
}
else
{
_cursorTimer.Start();
}
}
if (_blinkTimer)
{
_blinkTimer.Interval(_cursorBlinkTime);
if (_cursorBlinkTime == std::chrono::milliseconds(0) || !_blinkAnimationEnabled)
{
_blinkTimer.Stop();
}
else
{
_blinkTimer.Start();
}
}
}
void ControlCore::CursorBlinkTime(Windows::Foundation::TimeSpan t)
{
_cursorBlinkTime = t;
_updateTimers();
}
void ControlCore::VtBlinkEnabled(bool b)
{
_blinkAnimationEnabled = b;
_updateTimers();
}
void ControlCore::_terminalWarningBell()
{
// Since this can only ever be triggered by output from the connection,
@@ -1648,6 +1717,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_closing = true;
_cursorTimer.Stop();
_blinkTimer.Stop();
// Ensure Close() doesn't hang, waiting for MidiAudio to finish playing an hour long song.
_midiAudio.BeginSkip();
@@ -1703,7 +1775,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_TabColorChangedHandlers(*this, nullptr);
}
void ControlCore::BlinkAttributeTick()
void ControlCore::_blinkTimerTick(const IInspectable&, const IInspectable&)
{
const auto lock = _terminal->LockForWriting();
@@ -1711,7 +1783,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
renderSettings.ToggleBlinkRendition(*_renderer);
}
void ControlCore::BlinkCursor()
void ControlCore::_cursorTimerTick(const IInspectable&, const IInspectable&)
{
const auto lock = _terminal->LockForWriting();
_terminal->BlinkCursor();
@@ -2214,12 +2286,30 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::GotFocus()
{
_focusChanged(true);
if (_cursorTimer)
{
CursorOn(SelectionMode() != SelectionInteractionMode::Mark);
_cursorTimer.Start();
}
if (_blinkTimer)
{
_blinkTimer.Start();
}
}
// See GotFocus.
void ControlCore::LostFocus()
{
_focusChanged(false);
if (_cursorTimer && !false /*TODO DH*/)
{
_cursorTimer.Stop();
CursorOn(false);
}
if (_blinkTimer)
{
_blinkTimer.Stop();
}
}
void ControlCore::_focusChanged(bool focused)

View File

@@ -192,8 +192,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma endregion
void BlinkAttributeTick();
void BlinkCursor();
bool CursorOn() const;
void CursorOn(const bool isCursorOn);
@@ -245,6 +243,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ShouldShowSelectCommand();
bool ShouldShowSelectOutput();
Windows::Foundation::TimeSpan CursorBlinkTime() { return _cursorBlinkTime; }
bool VtBlinkEnabled() { return _blinkAnimationEnabled; }
void CursorBlinkTime(Windows::Foundation::TimeSpan v);
void VtBlinkEnabled(bool v);
RUNTIME_SETTING(double, Opacity, _settings->Opacity());
RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic());
@@ -337,6 +340,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
uint64_t _owningHwnd{ 0 };
winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr };
winrt::Windows::System::DispatcherQueueTimer _cursorTimer{ nullptr };
winrt::Windows::System::DispatcherQueueTimer _blinkTimer{ nullptr };
til::shared_mutex<SharedState> _shared;
til::point _contextMenuBufferPosition{ 0, 0 };
@@ -375,6 +380,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
MidiAudio _midiAudio;
winrt::Windows::System::DispatcherQueueTimer _midiAudioSkipTimer{ nullptr };
winrt::Windows::Foundation::TimeSpan _cursorBlinkTime{ std::chrono::milliseconds(500) };
bool _blinkAnimationEnabled{ true };
#pragma region RendererCallbacks
void _rendererWarning(const HRESULT hr);
winrt::fire_and_forget _renderEngineSwapChainChanged(const HANDLE handle);
@@ -400,6 +408,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _clickedOnMark(const til::point& pos, bool (*filter)(const ::ScrollMark&));
void _updateTimers();
void _cursorTimerTick(const IInspectable&, const IInspectable&);
void _blinkTimerTick(const IInspectable&, const IInspectable&);
inline bool _IsClosing() const noexcept
{
#ifndef NDEBUG

View File

@@ -126,7 +126,6 @@ namespace Microsoft.Terminal.Control
Microsoft.Terminal.Core.Point CursorPosition { get; };
void ResumeRendering();
void BlinkAttributeTick();
void Search(String text, Boolean goForward, Boolean caseSensitive);
void ClearSearch();
@@ -141,7 +140,6 @@ namespace Microsoft.Terminal.Control
String HoveredUriText { get; };
Windows.Foundation.IReference<Microsoft.Terminal.Core.Point> HoveredCell { get; };
void BlinkCursor();
Boolean IsInReadOnlyMode { get; };
Boolean CursorOn;
void EnablePainting();
@@ -159,6 +157,9 @@ namespace Microsoft.Terminal.Control
Boolean ShouldShowSelectCommand();
Boolean ShouldShowSelectOutput();
Windows.Foundation.TimeSpan CursorBlinkTime { get; set; };
Boolean VtBlinkEnabled { get; set; };
// These events are called from some background thread
event Windows.Foundation.TypedEventHandler<Object, CopyToClipboardEventArgs> CopyToClipboard;
event Windows.Foundation.TypedEventHandler<Object, TitleChangedEventArgs> TitleChanged;

View File

@@ -56,6 +56,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
self->_AttachedHandlers(*self, nullptr);
}
});
_createInteractivityTimers();
}
uint64_t ControlInteractivity::Id()
@@ -88,6 +90,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings)
{
_core->AttachToNewControl(keyBindings);
_createInteractivityTimers();
}
// Method Description:
@@ -119,12 +122,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::Close()
{
_ClosedHandlers(*this, nullptr);
_autoScrollTimer.reset(); // Cancel and wait for completion
if (_core)
{
_core->Close();
}
}
void ControlInteractivity::_createInteractivityTimers()
{
_autoScrollTimer.reset(CreateThreadpoolTimer(&ControlInteractivity::_autoScrollTimerCallback, this, nullptr));
}
// Method Description:
// - Returns the number of clicks that occurred (double and triple click support).
// Every call to this function registers a click.
@@ -157,6 +166,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::GotFocus()
{
_focused = true;
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Enable());
@@ -169,6 +180,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::LostFocus()
{
_focused = false;
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Disable());
@@ -242,7 +255,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core->PasteText(winrt::hstring{ wstr });
}
void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState,
void ControlInteractivity::PointerPressed(const uint32_t /*pointerId*/,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
@@ -254,6 +268,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto shiftEnabled = modifiers.IsShiftPressed();
const auto ctrlEnabled = modifiers.IsCtrlPressed();
// Mark that this pointer event actually started within our bounds.
// We'll need this later, for PointerMoved events.
_pointerPressedInBounds = true;
// GH#9396: we prioritize hyper-link over VT mouse events
auto hyperlink = _core->GetHyperlink(terminalPosition.to_core_point());
if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) &&
@@ -338,17 +356,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_touchAnchor = contactPoint;
}
void ControlInteractivity::PointerMoved(Control::MouseButtonState buttonState,
void ControlInteractivity::PointerMoved(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused,
const Core::Point pixelPosition,
const bool pointerPressedInBounds)
const Core::Point pixelPosition)
{
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition });
// Short-circuit isReadOnly check to avoid warning dialog
if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
if (_focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
{
_sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
}
@@ -356,7 +373,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// actually start _in_ the control bounds. Case in point - someone drags
// a file into the bounds of the control. That shouldn't send the
// selection into space.
else if (focused && pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
else if (_focused && _pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
{
if (_singleClickTouchdownPos)
{
@@ -384,15 +401,48 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
SetEndSelectionPoint(pixelPosition);
// GH#9109 - Only start an auto-scroll when the drag actually
// started within our bounds. Otherwise, someone could start a drag
// outside the terminal control, drag into the padding, and trick us
// into starting to scroll.
{
// We want to find the distance relative to the bounds of the
// SwapChainPanel, not the entire control. If they drag out of
// the bounds of the text, into the padding, we still what that
// to auto-scroll
const auto height = _core->ViewHeight() * _core->FontSize().Height;
const auto cursorBelowBottomDist = pixelPosition.Y - height;
const auto cursorAboveTopDist = -1 * pixelPosition.Y;
constexpr auto MinAutoScrollDist = 2.0; // Arbitrary value
auto newAutoScrollVelocity = 0.0;
if (cursorBelowBottomDist > MinAutoScrollDist)
{
newAutoScrollVelocity = _getAutoScrollSpeed(cursorBelowBottomDist);
}
else if (cursorAboveTopDist > MinAutoScrollDist)
{
newAutoScrollVelocity = -1.0 * _getAutoScrollSpeed(cursorAboveTopDist);
}
if (newAutoScrollVelocity != 0)
{
_tryStartAutoScroll(pointerId, pixelPosition, newAutoScrollVelocity);
}
else
{
_tryStopAutoScroll(pointerId);
}
}
}
_core->SetHoveredCell(terminalPosition.to_core_point());
}
void ControlInteractivity::TouchMoved(const Core::Point newTouchPoint,
const bool focused)
void ControlInteractivity::TouchMoved(const Core::Point newTouchPoint)
{
if (focused &&
if (_focused &&
_touchAnchor)
{
const auto anchor = _touchAnchor.value();
@@ -426,11 +476,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void ControlInteractivity::PointerReleased(Control::MouseButtonState buttonState,
void ControlInteractivity::PointerReleased(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition)
{
_pointerPressedInBounds = false;
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition });
// Short-circuit isReadOnly check to avoid warning dialog
if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
@@ -454,6 +507,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
_singleClickTouchdownPos = std::nullopt;
_tryStopAutoScroll(pointerId);
}
void ControlInteractivity::TouchReleased()
@@ -745,4 +799,110 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
return _core->Settings().ProfileSource() == L"Windows.Terminal.Wsl";
}
// Method Description:
// - Calculates speed of single axis of auto scrolling. It has to allow for both
// fast and precise selection.
// Arguments:
// - cursorDistanceFromBorder: distance from viewport border to cursor, in pixels. Must be non-negative.
// Return Value:
// - positive speed in characters / sec
double ControlInteractivity::_getAutoScrollSpeed(double cursorDistanceFromBorder) const
{
// The numbers below just feel well, feel free to change.
// TODO: Maybe account for space beyond border that user has available
return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
}
// Method Description:
// - Starts new pointer related auto scroll behavior, or continues existing one.
// Does nothing when there is already auto scroll associated with another pointer.
// Arguments:
// - pointerId, point: info about pointer that causes auto scroll. Pointer's position
// is later used to update selection.
// - scrollVelocity: target velocity of scrolling in characters / sec
void ControlInteractivity::_tryStartAutoScroll(const uint32_t pointerId, const Core::Point& point, const double scrollVelocity)
{
// Allow only one pointer at the time
if (!_autoScrollingPointerId ||
_autoScrollingPointerId == pointerId)
{
_autoScrollingPointerId = pointerId;
_autoScrollingPointerPoint = point;
_autoScrollVelocity = scrollVelocity;
// If this is first time the auto scroll update is about to be called,
// kick-start it by initializing its time delta as if it started now
if (!_lastAutoScrollUpdateTime)
{
_lastAutoScrollUpdateTime = std::chrono::high_resolution_clock::now();
}
if (!IsThreadpoolTimerSet(_autoScrollTimer.get()))
{
static constexpr auto AutoScrollUpdateInterval = std::chrono::milliseconds(33); // 1/30th of a second
FILETIME updateIntervalDeadline{
.dwLowDateTime = -1 * static_cast<DWORD>(AutoScrollUpdateInterval.count() * 10000 /* convert ms to 100-ns */),
.dwHighDateTime = 0xFFFFFFFF
};
SetThreadpoolTimer(
_autoScrollTimer.get(),
&updateIntervalDeadline,
static_cast<DWORD>(AutoScrollUpdateInterval.count()),
0);
}
}
}
// Method Description:
// - Stops auto scroll if it's active and is associated with supplied pointer id.
// Arguments:
// - pointerId: id of pointer for which to stop auto scroll
void ControlInteractivity::_tryStopAutoScroll(const uint32_t pointerId)
{
if (_autoScrollingPointerId &&
pointerId == _autoScrollingPointerId)
{
_autoScrollingPointerId = std::nullopt;
_autoScrollingPointerPoint = std::nullopt;
_autoScrollVelocity = 0;
_lastAutoScrollUpdateTime = std::nullopt;
SetThreadpoolTimer(_autoScrollTimer.get(), nullptr, 0, 0);
}
}
void CALLBACK ControlInteractivity::_autoScrollTimerCallback(PTP_CALLBACK_INSTANCE, void* context, PTP_TIMER)
{
// **INVARIANT**
// This timer only fires while ControlInteractivity is alive.
auto pThis{ static_cast<ControlInteractivity*>(context) };
pThis->_updateAutoScroll();
}
// Method Description:
// - Called continuously to gradually scroll viewport when user is mouse
// selecting outside it (to 'follow' the cursor).
void ControlInteractivity::_updateAutoScroll()
{
if (_autoScrollVelocity != 0)
{
const auto timeNow = std::chrono::high_resolution_clock::now();
if (_lastAutoScrollUpdateTime)
{
static constexpr auto microSecPerSec = 1000000.0;
const auto deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(timeNow - *_lastAutoScrollUpdateTime).count() / microSecPerSec;
UpdateScrollbar(_core->ScrollOffset() + _autoScrollVelocity * deltaTime);
if (_autoScrollingPointerPoint)
{
SetEndSelectionPoint(*_autoScrollingPointerPoint);
}
}
_lastAutoScrollUpdateTime = timeNow;
}
}
}

View File

@@ -51,23 +51,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
::Microsoft::Console::Render::IRenderData* GetRenderData() const;
#pragma region Input Methods
void PointerPressed(Control::MouseButtonState buttonState,
void PointerPressed(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition);
void TouchPressed(const Core::Point contactPoint);
void PointerMoved(Control::MouseButtonState buttonState,
void PointerMoved(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused,
const Core::Point pixelPosition,
const bool pointerPressedInBounds);
void TouchMoved(const Core::Point newTouchPoint,
const bool focused);
const Core::Point pixelPosition);
void TouchMoved(const Core::Point newTouchPoint);
void PointerReleased(Control::MouseButtonState buttonState,
void PointerReleased(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition);
@@ -142,6 +142,25 @@ namespace winrt::Microsoft::Terminal::Control::implementation
uint64_t _id;
static std::atomic<uint64_t> _nextId;
bool _focused{ false };
// Auto scroll occurs when user, while selecting, drags cursor outside
// viewport. View is then scrolled to 'follow' the cursor.
double _autoScrollVelocity;
std::optional<uint32_t> _autoScrollingPointerId;
std::optional<Core::Point> _autoScrollingPointerPoint;
wil::unique_threadpool_timer _autoScrollTimer;
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;
bool _pointerPressedInBounds{ false };
void _tryStartAutoScroll(const uint32_t id, const Core::Point& point, const double scrollVelocity);
void _tryStopAutoScroll(const uint32_t pointerId);
static void CALLBACK _autoScrollTimerCallback(PTP_CALLBACK_INSTANCE instance, void* context, PTP_TIMER timer);
void _updateAutoScroll();
double _getAutoScrollSpeed(double cursorDistanceFromBorder) const;
void _createInteractivityTimers();
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
void _updateSystemParameterSettings() noexcept;

View File

@@ -36,24 +36,24 @@ namespace Microsoft.Terminal.Control
void RequestPasteTextFromClipboard();
void SetEndSelectionPoint(Microsoft.Terminal.Core.Point point);
void PointerPressed(MouseButtonState buttonState,
void PointerPressed(UInt32 pointerId,
MouseButtonState buttonState,
UInt32 pointerUpdateKind,
UInt64 timestamp,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition);
void TouchPressed(Microsoft.Terminal.Core.Point contactPoint);
void PointerMoved(MouseButtonState buttonState,
void PointerMoved(UInt32 pointerId,
MouseButtonState buttonState,
UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Boolean focused,
Microsoft.Terminal.Core.Point pixelPosition,
Boolean pointerPressedInBounds);
Microsoft.Terminal.Core.Point pixelPosition);
void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint,
Boolean focused);
void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint);
void PointerReleased(MouseButtonState buttonState,
void PointerReleased(UInt32 pointerId,
MouseButtonState buttonState,
UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition);

View File

@@ -57,12 +57,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TermControl::TermControl(Control::ControlInteractivity content) :
_interactivity{ content },
_isInternalScrollBarUpdate{ false },
_autoScrollVelocity{ 0 },
_autoScrollingPointerPoint{ std::nullopt },
_autoScrollTimer{},
_lastAutoScrollUpdateTime{ std::nullopt },
_cursorTimer{},
_blinkTimer{},
_searchBox{ nullptr }
{
InitializeComponent();
@@ -161,10 +155,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_revokers.WarningBell = _core.WarningBell(winrt::auto_revoke, { get_weak(), &TermControl::_coreWarningBell });
_revokers.CursorPositionChanged = _core.CursorPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_CursorPositionChanged });
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000));
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
_autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll });
_ApplyUISettings();
_originalPrimaryElements = winrt::single_threaded_observable_vector<Controls::ICommandBarElement>();
@@ -1080,53 +1070,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Set up blinking cursor
int blinkTime = GetCaretBlinkTime();
if (blinkTime != INFINITE)
{
// Create a timer
DispatcherTimer cursorTimer;
cursorTimer.Interval(std::chrono::milliseconds(blinkTime));
cursorTimer.Tick({ get_weak(), &TermControl::_CursorTimerTick });
_cursorTimer.emplace(std::move(cursorTimer));
// As of GH#6586, don't start the cursor timer immediately, and
// don't show the cursor initially. We'll show the cursor and start
// the timer when the control is first focused.
//
// As of GH#11411, turn on the cursor if we've already been marked
// as focused. We suspect that it's possible for the Focused event
// to fire before the LayoutUpdated. In that case, the
// _GotFocusHandler would mark us _focused, but find that a
// _cursorTimer doesn't exist, and it would never turn on the
// cursor. To mitigate, we'll initialize the cursor's 'on' state
// with `_focused` here.
_core.CursorOn(_focused || _displayCursorWhileBlurred());
if (_displayCursorWhileBlurred())
{
_cursorTimer->Start();
}
}
else
{
// The user has disabled cursor blinking
_cursorTimer = std::nullopt;
}
// Set up blinking attributes
auto animationsEnabled = TRUE;
SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0);
if (animationsEnabled && blinkTime != INFINITE)
{
// Create a timer
DispatcherTimer blinkTimer;
blinkTimer.Interval(std::chrono::milliseconds(blinkTime));
blinkTimer.Tick({ get_weak(), &TermControl::_BlinkTimerTick });
blinkTimer.Start();
_blinkTimer.emplace(std::move(blinkTimer));
}
else
{
// The user has disabled blinking
_blinkTimer = std::nullopt;
}
_core.CursorBlinkTime(std::chrono::milliseconds(blinkTime == INFINITE ? 0 : blinkTime));
_core.VtBlinkEnabled(animationsEnabled);
// Now that the renderer is set up, update the appearance for initialization
_UpdateAppearanceFromUIThread(_core.FocusedAppearance());
@@ -1489,14 +1436,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
get_self<TermControlAutomationPeer>(_automationPeer)->RecordKeyEvent(vkey);
}
if (_cursorTimer)
{
// Manually show the cursor when a key is pressed. Restarting
// the timer prevents flickering.
_core.CursorOn(_core.SelectionMode() != SelectionInteractionMode::Mark);
_cursorTimer->Start();
}
return handled;
}
@@ -1543,10 +1482,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Focus(FocusState::Pointer);
}
// Mark that this pointer event actually started within our bounds.
// We'll need this later, for PointerMoved events.
_pointerPressedInBounds = true;
if (type == Windows::Devices::Input::PointerDeviceType::Touch)
{
const auto contactRect = point.Properties().ContactRect();
@@ -1556,7 +1491,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
else
{
const auto cursorPosition = point.Position();
_interactivity.PointerPressed(TermControl::GetPressedMouseButtons(point),
_interactivity.PointerPressed(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
point.Timestamp(),
ControlKeyStates{ args.KeyModifiers() },
@@ -1595,53 +1531,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen)
{
_interactivity.PointerMoved(TermControl::GetPressedMouseButtons(point),
_interactivity.PointerMoved(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()),
_focused,
pixelPosition.to_core_point(),
_pointerPressedInBounds);
// GH#9109 - Only start an auto-scroll when the drag actually
// started within our bounds. Otherwise, someone could start a drag
// outside the terminal control, drag into the padding, and trick us
// into starting to scroll.
if (_focused && _pointerPressedInBounds && point.Properties().IsLeftButtonPressed())
{
// We want to find the distance relative to the bounds of the
// SwapChainPanel, not the entire control. If they drag out of
// the bounds of the text, into the padding, we still what that
// to auto-scroll
const auto cursorBelowBottomDist = cursorPosition.Y - SwapChainPanel().Margin().Top - SwapChainPanel().ActualHeight();
const auto cursorAboveTopDist = -1 * cursorPosition.Y + SwapChainPanel().Margin().Top;
constexpr auto MinAutoScrollDist = 2.0; // Arbitrary value
auto newAutoScrollVelocity = 0.0;
if (cursorBelowBottomDist > MinAutoScrollDist)
{
newAutoScrollVelocity = _GetAutoScrollSpeed(cursorBelowBottomDist);
}
else if (cursorAboveTopDist > MinAutoScrollDist)
{
newAutoScrollVelocity = -1.0 * _GetAutoScrollSpeed(cursorAboveTopDist);
}
if (newAutoScrollVelocity != 0)
{
_TryStartAutoScroll(point, newAutoScrollVelocity);
}
else
{
_TryStopAutoScroll(ptr.PointerId());
}
}
pixelPosition.to_core_point());
}
else if (type == Windows::Devices::Input::PointerDeviceType::Touch)
{
const auto contactRect = point.Properties().ContactRect();
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity.TouchMoved(newTouchPoint.to_core_point(), _focused);
_interactivity.TouchMoved(newTouchPoint.to_core_point());
}
args.Handled(true);
@@ -1661,8 +1562,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
_pointerPressedInBounds = false;
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(*this);
const auto cursorPosition = point.Position();
@@ -1674,7 +1573,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen)
{
_interactivity.PointerReleased(TermControl::GetPressedMouseButtons(point),
_interactivity.PointerReleased(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()),
pixelPosition.to_core_point());
@@ -1684,8 +1584,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_interactivity.TouchReleased();
}
_TryStopAutoScroll(ptr.PointerId());
args.Handled(true);
}
@@ -1846,86 +1744,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return false;
}
// Method Description:
// - Starts new pointer related auto scroll behavior, or continues existing one.
// Does nothing when there is already auto scroll associated with another pointer.
// Arguments:
// - pointerPoint: info about pointer that causes auto scroll. Pointer's position
// is later used to update selection.
// - scrollVelocity: target velocity of scrolling in characters / sec
void TermControl::_TryStartAutoScroll(const Windows::UI::Input::PointerPoint& pointerPoint, const double scrollVelocity)
{
// Allow only one pointer at the time
if (!_autoScrollingPointerPoint ||
_autoScrollingPointerPoint->PointerId() == pointerPoint.PointerId())
{
_autoScrollingPointerPoint = pointerPoint;
_autoScrollVelocity = scrollVelocity;
// If this is first time the auto scroll update is about to be called,
// kick-start it by initializing its time delta as if it started now
if (!_lastAutoScrollUpdateTime)
{
_lastAutoScrollUpdateTime = std::chrono::high_resolution_clock::now();
}
// Apparently this check is not necessary but greatly improves performance
if (!_autoScrollTimer.IsEnabled())
{
_autoScrollTimer.Start();
}
}
}
// Method Description:
// - Stops auto scroll if it's active and is associated with supplied pointer id.
// Arguments:
// - pointerId: id of pointer for which to stop auto scroll
void TermControl::_TryStopAutoScroll(const uint32_t pointerId)
{
if (_autoScrollingPointerPoint &&
pointerId == _autoScrollingPointerPoint->PointerId())
{
_autoScrollingPointerPoint = std::nullopt;
_autoScrollVelocity = 0;
_lastAutoScrollUpdateTime = std::nullopt;
// Apparently this check is not necessary but greatly improves performance
if (_autoScrollTimer.IsEnabled())
{
_autoScrollTimer.Stop();
}
}
}
// Method Description:
// - Called continuously to gradually scroll viewport when user is mouse
// selecting outside it (to 'follow' the cursor).
// Arguments:
// - none
void TermControl::_UpdateAutoScroll(const Windows::Foundation::IInspectable& /* sender */,
const Windows::Foundation::IInspectable& /* e */)
{
if (_autoScrollVelocity != 0)
{
const auto timeNow = std::chrono::high_resolution_clock::now();
if (_lastAutoScrollUpdateTime)
{
static constexpr auto microSecPerSec = 1000000.0;
const auto deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(timeNow - *_lastAutoScrollUpdateTime).count() / microSecPerSec;
ScrollBar().Value(ScrollBar().Value() + _autoScrollVelocity * deltaTime);
if (_autoScrollingPointerPoint)
{
_SetEndSelectionPointAtCursor(_autoScrollingPointerPoint->Position());
}
}
_lastAutoScrollUpdateTime = timeNow;
}
}
// Method Description:
// - Event handler for the GotFocus event. This is used to...
// - enable accessibility notifications for this TermControl
@@ -1965,18 +1783,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TSFInputControl().NotifyFocusEnter();
}
if (_cursorTimer)
{
// When the terminal focuses, show the cursor immediately
_core.CursorOn(_core.SelectionMode() != SelectionInteractionMode::Mark);
_cursorTimer->Start();
}
if (_blinkTimer)
{
_blinkTimer->Start();
}
// Only update the appearance here if an unfocused config exists - if an
// unfocused config does not exist then we never would have switched
// appearances anyway so there's no need to switch back upon gaining
@@ -2015,17 +1821,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TSFInputControl().NotifyFocusLeave();
}
if (_cursorTimer && !_displayCursorWhileBlurred())
{
_cursorTimer->Stop();
_core.CursorOn(false);
}
if (_blinkTimer)
{
_blinkTimer->Stop();
}
// Check if there is an unfocused config we should set the appearance to
// upon losing focus
if (_core.HasUnfocusedAppearance())
@@ -2091,43 +1886,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core.ScaleChanged(scaleX);
}
// Method Description:
// - Toggle the cursor on and off when called by the cursor blink timer.
// Arguments:
// - sender: not used
// - e: not used
void TermControl::_CursorTimerTick(const Windows::Foundation::IInspectable& /* sender */,
const Windows::Foundation::IInspectable& /* e */)
{
if (!_IsClosing())
{
_core.BlinkCursor();
}
}
// Method Description:
// - Toggle the blinking rendition state when called by the blink timer.
// Arguments:
// - sender: not used
// - e: not used
void TermControl::_BlinkTimerTick(const Windows::Foundation::IInspectable& /* sender */,
const Windows::Foundation::IInspectable& /* e */)
{
if (!_IsClosing())
{
_core.BlinkAttributeTick();
}
}
// Method Description:
// - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
// Arguments:
// - cursorPosition: in pixels, relative to the origin of the control
void TermControl::_SetEndSelectionPointAtCursor(const Windows::Foundation::Point& cursorPosition)
{
_interactivity.SetEndSelectionPoint(_toTerminalOrigin(cursorPosition).to_core_point());
}
// Method Description:
// - Update the position and size of the scrollbar to match the given
// viewport top, viewport height, and buffer size.
@@ -2274,7 +2032,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Disconnect the TSF input control so it doesn't receive EditContext events.
TSFInputControl().Close();
_autoScrollTimer.Stop();
if (!_detached)
{
@@ -2767,20 +2524,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
eventArgs.FontWeight(weight);
}
// Method Description:
// - Calculates speed of single axis of auto scrolling. It has to allow for both
// fast and precise selection.
// Arguments:
// - cursorDistanceFromBorder: distance from viewport border to cursor, in pixels. Must be non-negative.
// Return Value:
// - positive speed in characters / sec
double TermControl::_GetAutoScrollSpeed(double cursorDistanceFromBorder) const
{
// The numbers below just feel well, feel free to change.
// TODO: Maybe account for space beyond border that user has available
return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
}
// Method Description:
// - Async handler for the "Drop" event. If a file was dropped onto our
// root, we'll try to get the path of the file dropped onto us, and write

View File

@@ -232,21 +232,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _isInternalScrollBarUpdate;
// Auto scroll occurs when user, while selecting, drags cursor outside
// viewport. View is then scrolled to 'follow' the cursor.
double _autoScrollVelocity;
std::optional<Windows::UI::Input::PointerPoint> _autoScrollingPointerPoint;
Windows::UI::Xaml::DispatcherTimer _autoScrollTimer;
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;
bool _pointerPressedInBounds{ false };
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellLightAnimation{ nullptr };
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellDarkAnimation{ nullptr };
Windows::UI::Xaml::DispatcherTimer _bellLightTimer{ nullptr };
std::optional<Windows::UI::Xaml::DispatcherTimer> _cursorTimer;
std::optional<Windows::UI::Xaml::DispatcherTimer> _blinkTimer;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
bool _showMarksInScrollbar{ false };
@@ -313,12 +302,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::fire_and_forget _HyperlinkHandler(Windows::Foundation::IInspectable sender, Control::OpenHyperlinkEventArgs e);
void _CursorTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _BlinkTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _BellLightOff(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _SetEndSelectionPointAtCursor(const Windows::Foundation::Point& cursorPosition);
void _SwapChainSizeChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::SizeChangedEventArgs& e);
void _SwapChainScaleChanged(const Windows::UI::Xaml::Controls::SwapChainPanel& sender, const Windows::Foundation::IInspectable& args);
@@ -330,10 +315,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _CapturePointer(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
bool _ReleasePointerCapture(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _TryStartAutoScroll(const Windows::UI::Input::PointerPoint& pointerPoint, const double scrollVelocity);
void _TryStopAutoScroll(const uint32_t pointerId);
void _UpdateAutoScroll(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _KeyHandler(const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e, const bool keyDown);
static ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() noexcept;
bool _TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const;
@@ -343,8 +324,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::point _toControlOrigin(const til::point terminalPosition);
const til::point _toTerminalOrigin(winrt::Windows::Foundation::Point cursorPosition);
double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const;
void _Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive);
void _SearchChanged(const winrt::hstring& text, const bool goForward, const bool caseSensitive);

View File

@@ -309,10 +309,13 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 0, 0 };
const auto cursorPosition0 = terminalPosition0 * fontSize;
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -325,12 +328,11 @@ namespace ControlUnitTests
// move not quite a whole cell, but enough to start a selection
const til::point terminalPosition1{ 0, 0 };
const til::point cursorPosition1{ 6, 0 };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
@@ -338,18 +340,18 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse down a whole row");
const til::point terminalPosition2{ 1, 1 };
const auto cursorPosition2 = terminalPosition2 * fontSize;
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition2.to_core_point(),
true);
cursorPosition2.to_core_point());
Log::Comment(L"Verify that there's now two selections (one on each row)");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(2u, core->_terminal->GetSelectionRects().size());
Log::Comment(L"Release the mouse");
interactivity->PointerReleased(noMouseDown,
interactivity->PointerReleased(0,
noMouseDown,
WM_LBUTTONUP, //pointerUpdateKind
modifiers,
cursorPosition2.to_core_point());
@@ -360,7 +362,8 @@ namespace ControlUnitTests
Log::Comment(L"click outside the current selection");
const til::point terminalPosition3{ 2, 2 };
const auto cursorPosition3 = terminalPosition3 * fontSize;
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -372,12 +375,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse");
const til::point terminalPosition4{ 3, 2 };
const auto cursorPosition4 = terminalPosition4 * fontSize;
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition4.to_core_point(),
true);
cursorPosition4.to_core_point());
Log::Comment(L"Verify that there's now one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
@@ -409,10 +411,13 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -427,12 +432,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse just a little");
// move not quite a whole cell, but enough to start a selection
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
@@ -559,9 +563,12 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal");
const til::point cursorPosition0{ 6, 0 };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -575,12 +582,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
@@ -603,10 +609,13 @@ namespace ControlUnitTests
const auto leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const Control::MouseButtonState noMouseDown{};
interactivity->GotFocus();
const til::size fontSize{ 9, 21 };
Log::Comment(L"Click on the terminal");
const til::point cursorPosition0{ 6, 0 };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -620,12 +629,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
@@ -636,7 +644,8 @@ namespace ControlUnitTests
til::point expectedEnd{ 2, 0 };
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
interactivity->PointerReleased(noMouseDown,
interactivity->PointerReleased(0,
noMouseDown,
WM_LBUTTONUP,
modifiers,
cursorPosition1.to_core_point());
@@ -646,12 +655,11 @@ namespace ControlUnitTests
Log::Comment(L"Simulate dragging the mouse into the control, without first clicking into the control");
const til::point cursorPosition2{ fontSize.width * 10, 0 };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition2.to_core_point(),
false);
cursorPosition2.to_core_point());
Log::Comment(L"The selection should be unchanged.");
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
@@ -678,6 +686,8 @@ namespace ControlUnitTests
auto expectedViewHeight = 20;
auto expectedBufferHeight = 20;
interactivity->GotFocus();
auto scrollChangedHandler = [&](auto&&, const Control::ScrollPositionChangedArgs& args) mutable {
VERIFY_ARE_EQUAL(expectedTop, args.ViewTop());
VERIFY_ARE_EQUAL(expectedViewHeight, args.ViewHeight());
@@ -724,7 +734,8 @@ namespace ControlUnitTests
Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 4, 4 };
const auto cursorPosition0 = terminalPosition0 * fontSize;
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -737,12 +748,11 @@ namespace ControlUnitTests
// move the mouse as if to make a selection
const til::point terminalPosition1{ 10, 4 };
const auto cursorPosition1 = terminalPosition1 * fontSize;
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's still no selection");
VERIFY_IS_FALSE(core->HasSelection());
}
@@ -772,10 +782,13 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -790,12 +803,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse just a little");
// move not quite a whole cell, but enough to start a selection
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
@@ -850,12 +862,11 @@ namespace ControlUnitTests
// character in the buffer (if, albeit in a new location).
//
// This helps test GH #14462, a regression from #10749.
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition0.to_core_point(),
true);
cursorPosition0.to_core_point());
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
{
@@ -875,12 +886,11 @@ namespace ControlUnitTests
conn->WriteInput(L"Foo\r\n");
expectedAnchor.y -= 1;
VERIFY_ARE_EQUAL(scrollbackLength - 3, core->_terminal->GetScrollOffset());
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
{
@@ -929,7 +939,8 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -978,7 +989,8 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -994,7 +1006,8 @@ namespace ControlUnitTests
// The viewport is only 30 wide, so clamping 35 to the buffer size gets
// us 29, which converted is (32 + 29 + 1) = 62 = '>'
expectedOutput.push_back(L"\x1b[M >&");
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1009,7 +1022,8 @@ namespace ControlUnitTests
// straight up won't be sent to the terminal.
expectedOutput.push_back(L"sentinel"); // Clearly, it won't be this string
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1026,7 +1040,8 @@ namespace ControlUnitTests
VERIFY_ARE_EQUAL(0, core->ScrollOffset());
Log::Comment(L" --- Click on a spot that's still outside the buffer ---");
expectedOutput.push_back(L"\x1b[M >&");
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1040,7 +1055,8 @@ namespace ControlUnitTests
Log::Comment(L" --- Click on a spot that's NOW INSIDE the buffer ---");
// (32 + 35 + 1) = 68 = 'D'
expectedOutput.push_back(L"\x1b[M D&");
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,