mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-08 23:31:03 +00:00
Compare commits
4 Commits
dev/lhecke
...
dev/duhowe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eb8537aa8 | ||
|
|
51fe036223 | ||
|
|
32d20b6287 | ||
|
|
e8555da23d |
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user