From cc0190054ceaf42d9dea00eadcf68e6b1476bafb Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 9 May 2016 21:53:32 -0400 Subject: [PATCH 1/4] Fix regression in scroll scrub --- src/tracks/ui/Scrubbing.cpp | 18 +++++++----------- src/tracks/ui/Scrubbing.h | 1 - 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 3a420820b..a0ba72711 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -201,7 +201,7 @@ void Scrubber::MarkScrubStart( // needed for the decision to start scrubbing later when handling // drag events. #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - SetScrollScrubbing (smoothScrolling); + mSmoothScrollingScrub = smoothScrolling; #endif mAlwaysSeeking = alwaysSeeking; @@ -303,8 +303,10 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) // Wait to test again mScrubStartClockTimeMillis = ::wxGetLocalTimeMillis(); - if (IsScrubbing()) + if (IsScrubbing()) { + mProject->GetPlaybackScroller().Activate(mSmoothScrollingScrub); mScrubHasFocus = true; + } // Return true whether we started scrub, or are still waiting to decide. return true; @@ -380,7 +382,7 @@ void Scrubber::StopScrubbing() UncheckAllMenuItems(); mScrubStartPosition = -1; - SetScrollScrubbing (false); + mProject->GetPlaybackScroller().Activate(false); if (!IsScrubbing()) { @@ -393,12 +395,6 @@ void Scrubber::StopScrubbing() mProject->GetRulerPanel()->HideQuickPlayIndicator(); } -void Scrubber::SetScrollScrubbing(bool scrollScrubbing) -{ - mSmoothScrollingScrub = scrollScrubbing; - mProject->GetPlaybackScroller().Activate(scrollScrubbing); -} - bool Scrubber::IsScrubbing() const { if (mScrubToken <= 0) @@ -409,7 +405,6 @@ bool Scrubber::IsScrubbing() const const_cast(*this).mScrubToken = -1; const_cast(*this).mScrubStartPosition = -1; #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - // Don't call SetScrollScrubbing const_cast(*this).mSmoothScrollingScrub = false; #endif return false; @@ -677,7 +672,8 @@ void Scrubber::DoScrub(bool scroll, bool seek) MarkScrubStart(xx, scroll, seek); } else if(!match) { - SetScrollScrubbing(scroll); + mSmoothScrollingScrub = scroll; + mProject->GetPlaybackScroller().Activate(scroll); mAlwaysSeeking = seek; UncheckAllMenuItems(); CheckMenuItem(); diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index 315221008..b813ca7e4 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -57,7 +57,6 @@ public: bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing() { return mSmoothScrollingScrub; } - void SetScrollScrubbing(bool scrollScrubbing); bool IsAlwaysSeeking() const { return mAlwaysSeeking; } From a3b7305386b6f6c393f07fbedb74b199e1c259db Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 9 May 2016 23:16:07 -0400 Subject: [PATCH 2/4] Remove obsolete instructions in Mouse Preferences for scrubbing --- src/prefs/MousePrefs.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/prefs/MousePrefs.cpp b/src/prefs/MousePrefs.cpp index 4e49aa059..e51bc752b 100644 --- a/src/prefs/MousePrefs.cpp +++ b/src/prefs/MousePrefs.cpp @@ -110,13 +110,6 @@ void MousePrefs::CreateList() AddItem(_("Left-Drag"), _("Select"), _("Set Selection Range")); AddItem(_("Shift-Left-Click"), _("Select"), _("Extend Selection Range")); AddItem(_("Left-Double-Click"), _("Select"), _("Select Clip or Entire Track")); -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - AddItem(CTRL + _("-Left-Click"), _("Select"), _("Scrub")); - AddItem(CTRL + _("-Left-Drag"), _("Select"), _("Seek")); -#endif -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - AddItem(CTRL + _("-Left-Double-Click"), _("Select"), _("Scroll-scrub")); -#endif #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL AddItem(_("Wheel-Rotate"), _("Select"), _("Change scrub speed")); #endif From 5944391e24176b108abe3984b2fb0fccb1b936b3 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 9 May 2016 23:21:56 -0400 Subject: [PATCH 3/4] Let's commit to EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL --- src/Experimental.h | 1 - src/Project.cpp | 2 -- src/TrackPanel.cpp | 10 ++-------- src/tracks/ui/Scrubbing.cpp | 35 +++++------------------------------ src/tracks/ui/Scrubbing.h | 7 ++----- 5 files changed, 9 insertions(+), 46 deletions(-) diff --git a/src/Experimental.h b/src/Experimental.h index d69528842..d5f93f9df 100644 --- a/src/Experimental.h +++ b/src/Experimental.h @@ -173,7 +173,6 @@ // The following enable parts of the scrubbing user interface. #define EXPERIMENTAL_SCRUBBING_BASIC #ifdef EXPERIMENTAL_SCRUBBING_BASIC - #define EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL #define EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL #endif #endif diff --git a/src/Project.cpp b/src/Project.cpp index 02e85036c..487378388 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -5364,7 +5364,6 @@ void AudacityProject::PlaybackScroller::OnTimer(wxCommandEvent &event) // Let other listeners get the notification event.Skip(); -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL if (mActive && mProject->IsAudioActive()) { // Pan the view, so that we center the play indicator. @@ -5382,5 +5381,4 @@ void AudacityProject::PlaybackScroller::OnTimer(wxCommandEvent &event) viewInfo.h = std::max(0.0, viewInfo.h); trackPanel->Refresh(false); } -#endif } diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index f78c09c22..ce36069d1 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -5472,12 +5472,9 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event) (event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0); if (event.ShiftDown() -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL // Don't pan during smooth scrolling. That would conflict with keeping // the play indicator centered. - && !GetProject()->GetScrubber().IsScrollScrubbing() -#endif - ) + && !GetProject()->GetScrubber().IsScrollScrubbing()) { // MM: Scroll left/right when used with Shift key down mListener->TP_ScrollWindow( @@ -5506,15 +5503,12 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event) // Time corresponding to mouse position wxCoord xx; double center_h; -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL if (GetProject()->GetScrubber().IsScrollScrubbing()) { // Expand or contract about the center, ignoring mouse position center_h = mViewInfo->h + (GetScreenEndTime() - mViewInfo->h) / 2.0; xx = mViewInfo->TimeToPosition(center_h, trackLeftEdge); } - else -#endif - { + else { xx = event.m_x; center_h = mViewInfo->PositionToTime(xx, trackLeftEdge); } diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index a0ba72711..19306230f 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -188,11 +188,7 @@ namespace { void Scrubber::MarkScrubStart( // Assume xx is relative to the left edge of TrackPanel! - wxCoord xx -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - , bool smoothScrolling -#endif - , bool alwaysSeeking + wxCoord xx, bool smoothScrolling, bool alwaysSeeking ) { UncheckAllMenuItems(); @@ -200,9 +196,7 @@ void Scrubber::MarkScrubStart( // Don't actually start scrubbing, but collect some information // needed for the decision to start scrubbing later when handling // drag events. -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL mSmoothScrollingScrub = smoothScrolling; -#endif mAlwaysSeeking = alwaysSeeking; ControlToolBar * const ctb = mProject->GetControlToolBar(); @@ -350,15 +344,13 @@ void Scrubber::ContinueScrubbing() // Cause OnTimer() to suppress the speed display mScrubSpeedDisplayCountdown = 1; -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL if (mSmoothScrollingScrub) { const double speed = FindScrubSpeed(seek, time); result = gAudioIO->EnqueueScrubBySignedSpeed(speed, mMaxScrubSpeed, seek); } else -#endif result = gAudioIO->EnqueueScrubByPosition - (time, seek ? 1.0 : mMaxScrubSpeed, seek); + (time, seek ? 1.0 : mMaxScrubSpeed, seek); } if (result) @@ -366,12 +358,9 @@ void Scrubber::ContinueScrubbing() // else, if seek requested, try again at a later time when we might // enqueue a long enough stutter -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL if (mSmoothScrollingScrub) ; - else -#endif - { + else { if (mScrubSpeedDisplayCountdown > 0) --mScrubSpeedDisplayCountdown; } @@ -404,9 +393,7 @@ bool Scrubber::IsScrubbing() const else { const_cast(*this).mScrubToken = -1; const_cast(*this).mScrubStartPosition = -1; -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL const_cast(*this).mSmoothScrollingScrub = false; -#endif return false; } } @@ -417,10 +404,8 @@ bool Scrubber::ShouldDrawScrubSpeed() mScrubHasFocus && ( // Draw for (non-scroll) scrub, sometimes, but never for seek (!PollIsSeeking() && mScrubSpeedDisplayCountdown > 0) -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL // Draw always for scroll-scrub and for scroll-seek || mSmoothScrollingScrub -#endif ); } @@ -442,9 +427,7 @@ void Scrubber::HandleScrollWheel(int steps) newSpeed <= AudioIO::GetMaxScrubSpeed()) { mLogMaxScrubSpeed = newLogMaxScrubSpeed; mMaxScrubSpeed = newSpeed; -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL if (!mSmoothScrollingScrub) -#endif // Show the speed for one second mScrubSpeedDisplayCountdown = kOneSecondCountdown + 1; } @@ -539,11 +522,9 @@ void ScrubbingOverlay::Draw(OverlayPanel &, wxDC &dc) // (b) Error alerts // So they were changed to 'orange' and 'lime'. static const wxColour clrNoScroll(215, 162, 0), clrScroll(0, 204, 153); -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL if (scrubber.IsScrollScrubbing()) dc.SetTextForeground(clrScroll); else -#endif dc.SetTextForeground(clrNoScroll); dc.DrawText(mLastScrubSpeedText, mLastScrubRect.GetX(), mLastScrubRect.GetY()); @@ -598,23 +579,17 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event) // Find the text const double maxScrubSpeed = GetScrubber().GetMaxScrubSpeed(); const double speed = -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL scrubber.IsScrollScrubbing() ? scrubber.FindScrubSpeed (seeking, mProject->GetViewInfo().PositionToTime(position.x, trackPanel->GetLeftOffset())) - : -#endif - maxScrubSpeed; + : maxScrubSpeed; const wxChar *format = -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL scrubber.IsScrollScrubbing() ? seeking ? wxT("%+.2fX") : wxT("%+.2f") - : -#endif - wxT("%.2f"); + : wxT("%.2f"); mNextScrubSpeedText = wxString::Format(format, speed); diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index b813ca7e4..c8d405758 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -29,11 +29,8 @@ public: // Assume xx is relative to the left edge of TrackPanel! void MarkScrubStart( - wxCoord xx -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - , bool smoothScrolling -#endif - , bool alwaysSeeking // if false, can switch seeking or scrubbing + wxCoord xx, bool smoothScrolling, + bool alwaysSeeking // if false, can switch seeking or scrubbing // by mouse button state ); From 19ef2f66819c648e6c093ddecb6e5ef1fd663ffa Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Tue, 10 May 2016 09:28:06 -0400 Subject: [PATCH 4/4] Implement drag-scrub, compatibly with the existing move-scrub... ... Start scrub by click or double click on the scrub head; release button or not; then move. If you release before moving, you get scrubbing as before, controlled by motion. Click or drag to switch in and out of seeking. Stop with ESC, spacebar, etc. No change of selection. But now if you drag, then scrubbing contines until you release the mouse or otherwise stop with a key. If by release of the mouse, then the selection changes as if by a click at the last play position. If you hold shift, then, as if by shift-click. If drag begins with a double-click, then the play head remains centered and the track moves. --- src/AudioIO.cpp | 19 +++++++++- src/AudioIO.h | 4 +++ src/Menus.cpp | 38 ++++++++++++++++++-- src/Menus.h | 1 + src/tracks/ui/Scrubbing.cpp | 69 ++++++++++++++++++++++++++++++------- src/tracks/ui/Scrubbing.h | 4 ++- src/widgets/Ruler.cpp | 17 +++++++-- src/widgets/Ruler.h | 2 ++ 8 files changed, 135 insertions(+), 19 deletions(-) diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index b3b9c2443..d757e4da3 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -402,6 +402,14 @@ struct AudioIO::ScrubQueue } ~ScrubQueue() {} + double LastTimeInQueue() const + { + // Needed by the main thread sometimes + wxCriticalSectionLocker locker(mUpdating); + const Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size]; + return previous.mS1 / mRate; + } + bool Producer(double end, double maxSpeed, bool bySpeed, bool maySkip) { // Main thread indicates a scrubbing interval @@ -670,7 +678,7 @@ private: const double mRate; const long mMinStutter; wxLongLong mLastScrubTimeMillis; - wxCriticalSection mUpdating; + mutable wxCriticalSection mUpdating; }; #endif @@ -2426,6 +2434,15 @@ bool AudioIO::EnqueueScrubBySignedSpeed(double speed, double maxSpeed, bool mayS else return false; } + +double AudioIO::GetLastTimeInScrubQueue() const +{ + if (mScrubQueue) + return mScrubQueue->LastTimeInQueue(); + else + return -1.0; +} + #endif bool AudioIO::IsBusy() diff --git a/src/AudioIO.h b/src/AudioIO.h index 8bb62bc67..c57501276 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -208,6 +208,10 @@ class AUDACITY_DLL_API AudioIO final { * Return true if some work was really enqueued. */ bool EnqueueScrubBySignedSpeed(double speed, double maxSpeed, bool maySkip); + + /** \brief return the ending time of the last enqueued scrub interval. + */ + double GetLastTimeInScrubQueue() const; #endif /** \brief Returns true if audio i/o is busy starting, stopping, playing, diff --git a/src/Menus.cpp b/src/Menus.cpp index d78b6aecf..0aa854920 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -2281,8 +2281,13 @@ void AudacityProject::OnRecordAppend() GetControlToolBar()->OnRecord(evt); } -// The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and "OnStopSelect" merged. void AudacityProject::OnPlayStopSelect() +{ + DoPlayStopSelect(false, false); +} + +// The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and "OnStopSelect" merged. +void AudacityProject::DoPlayStopSelect(bool click, bool shift) { wxCommandEvent evt; ControlToolBar *toolbar = GetControlToolBar(); @@ -2291,7 +2296,36 @@ void AudacityProject::OnPlayStopSelect() if (gAudioIO->IsStreamActive(GetAudioIOToken())) { toolbar->SetPlay(false); //Pops toolbar->SetStop(true); //Pushes stop down - mViewInfo.selectedRegion.setT0(gAudioIO->GetStreamTime(), false); + + // change the selection + auto time = gAudioIO->GetStreamTime(); + auto &selection = mViewInfo.selectedRegion; + if (shift && click) { + // Change the region selection, as if by shift-click at the play head + auto t0 = selection.t0(), t1 = selection.t1(); + if (time < t0) + // Grow selection + t0 = time; + else if (time > t1) + // Grow selection + t1 = time; + else { + // Shrink selection, changing the nearer boundary + if (fabs(t0 - time) < fabs(t1 - time)) + t0 = time; + else + t1 = time; + } + selection.setTimes(t0, t1); + } + else if (click) + // Set a point selection, as if by a click at the play head + selection.setTimes(time, time); + else + // How stop and set cursor always worked + // -- change t0, collapsing to point only if t1 was greater + selection.setT0(time, false); + ModifyState(false); // without bWantsAutoSave toolbar->OnStop(evt); } diff --git a/src/Menus.h b/src/Menus.h index edd2d8fbd..7a32590d7 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -80,6 +80,7 @@ void OnSeekRightLong(); bool MakeReadyToPlay(bool loop = false, bool cutpreview = false); // Helper function that sets button states etc. void OnPlayStop(); +void DoPlayStopSelect(bool click, bool shift); void OnPlayStopSelect(); void OnPlayOneSecond(); void OnPlayToSelection(); diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 19306230f..7175175ad 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -230,6 +230,9 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) if (IsScrubbing()) return false; else { + const auto state = ::wxGetMouseState(); + mDragging = state.LeftIsDown(); + const bool busy = gAudioIO->IsBusy(); if (busy && gAudioIO->GetNumCaptureChannels() > 0) { // Do not stop recording, and don't try to start scrubbing after @@ -259,6 +262,14 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) mScrubStartPosition = position; } + if (mDragging && mSmoothScrollingScrub) { + auto delta = time0 - time1; + time0 = std::max(0.0, std::min(maxTime, + (viewInfo.h + mProject->GetScreenEndTime()) / 2 + )); + time1 = time0 + delta; + } + AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions()); options.timeTrack = NULL; options.scrubDelay = (kTimerInterval / 1000.0); @@ -272,8 +283,10 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) p->GetTranscriptionToolBar()->GetPlaySpeed(); } #else - // That idea seems unpopular... just make it one - mMaxScrubSpeed = options.maxScrubSpeed = 1.0; + // That idea seems unpopular... just make it one for move-scrub, + // but big for drag-scrub + mMaxScrubSpeed = options.maxScrubSpeed = + mDragging ? AudioIO::GetMaxScrubSpeed() : 1.0; #endif options.maxScrubTime = mProject->GetTracks()->GetEndTime(); ControlToolBar::PlayAppearance appearance = @@ -300,6 +313,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) if (IsScrubbing()) { mProject->GetPlaybackScroller().Activate(mSmoothScrollingScrub); mScrubHasFocus = true; + mLastScrubPosition = xx; } // Return true whether we started scrub, or are still waiting to decide. @@ -309,13 +323,19 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) void Scrubber::ContinueScrubbing() { + const wxMouseState state(::wxGetMouseState()); + + if (mDragging && !state.LeftIsDown()) { + // Stop and set cursor + mProject->DoPlayStopSelect(true, state.ShiftDown()); + return; + } // Thus scrubbing relies mostly on periodic polling of mouse and keys, // not event notifications. But there are a few event handlers that // leave messages for this routine, in mScrubSeekPress and in mScrubHasFocus. // Seek only when the pointer is in the panel. Else, scrub. - const wxMouseState state(::wxGetMouseState()); TrackPanel *const trackPanel = mProject->GetTrackPanel(); // Decide whether to skip play, because either mouse is down now, @@ -333,13 +353,21 @@ void Scrubber::ContinueScrubbing() } const wxPoint position = trackPanel->ScreenToClient(state.GetPosition()); - // When we don't have focus, enqueue silent scrubs until we regain focus. + const auto &viewInfo = mProject->GetViewInfo(); + bool result = false; if (!mScrubHasFocus) + // When we don't have focus, enqueue silent scrubs until we regain focus. result = gAudioIO->EnqueueScrubBySignedSpeed(0, mMaxScrubSpeed, false); + else if (mDragging && mSmoothScrollingScrub) { + const auto lastTime = gAudioIO->GetLastTimeInScrubQueue(); + const auto delta = mLastScrubPosition - position.x; + const double time = viewInfo.OffsetTimeByPixels(lastTime, delta); + result = gAudioIO->EnqueueScrubByPosition(time, mMaxScrubSpeed, false); + mLastScrubPosition = position.x; + } else { - const double time = mProject->GetViewInfo().PositionToTime(position.x, trackPanel->GetLeftOffset()); - + const double time = viewInfo.PositionToTime(position.x, trackPanel->GetLeftOffset()); if (seek) // Cause OnTimer() to suppress the speed display mScrubSpeedDisplayCountdown = 1; @@ -372,6 +400,7 @@ void Scrubber::StopScrubbing() mScrubStartPosition = -1; mProject->GetPlaybackScroller().Activate(false); + mDragging = false; if (!IsScrubbing()) { @@ -382,6 +411,11 @@ void Scrubber::StopScrubbing() } mProject->GetRulerPanel()->HideQuickPlayIndicator(); + + // Need this in case ruler gets the mouse-up event after escaping scrubbing: + // prevent reappearance of the + // quick play guideline + mProject->GetRulerPanel()->IgnoreMouseUp(); } bool Scrubber::IsScrubbing() const @@ -400,6 +434,9 @@ bool Scrubber::IsScrubbing() const bool Scrubber::ShouldDrawScrubSpeed() { + if (mDragging) + return false; + return IsScrubbing() && mScrubHasFocus && ( // Draw for (non-scroll) scrub, sometimes, but never for seek @@ -419,6 +456,10 @@ double Scrubber::FindScrubSpeed(bool seeking, double time) const void Scrubber::HandleScrollWheel(int steps) { + if (mDragging) + // Not likely you would spin it with the left button down, but... + return; + const int newLogMaxScrubSpeed = mLogMaxScrubSpeed + steps; static const double maxScrubSpeedBase = pow(2.0, 1.0 / ScrubSpeedStepsPerOctave); @@ -450,7 +491,8 @@ void Scrubber::Forwarder::OnMouse(wxMouseEvent &event) if (isScrubbing && !event.HasAnyModifiers()) { if(event.LeftDown() || (event.LeftIsDown() && event.Dragging())) { - scrubber.mScrubSeekPress = true; + if (!scrubber.mDragging) + scrubber.mScrubSeekPress = true; auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x; ruler->UpdateQuickPlayPos(xx); ruler->ShowQuickPlayIndicator(); @@ -541,12 +583,13 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event) auto position = ::wxGetMousePosition(); { - auto xx = ruler->ScreenToClient(position).x; - ruler->UpdateQuickPlayPos(xx); + if(scrubber.HasStartedScrubbing()) { + auto xx = ruler->ScreenToClient(position).x; + ruler->UpdateQuickPlayPos(xx); - if(!isScrubbing && scrubber.HasStartedScrubbing()) { - // Really start scrub if motion is far enough - scrubber.MaybeStartScrubbing(xx); + if (!isScrubbing) + // Really start scrub if motion is far enough + scrubber.MaybeStartScrubbing(xx); } if (!isScrubbing) { @@ -627,7 +670,7 @@ Scrubber &ScrubbingOverlay::GetScrubber() bool Scrubber::PollIsSeeking() { - return mAlwaysSeeking || ::wxGetMouseState().LeftIsDown(); + return !mDragging && (mAlwaysSeeking || ::wxGetMouseState().LeftIsDown()); } void Scrubber::DoScrub(bool scroll, bool seek) diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index c8d405758..9a1a6d797 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -109,10 +109,12 @@ private: bool mScrubHasFocus; int mScrubSpeedDisplayCountdown; wxCoord mScrubStartPosition; + wxCoord mLastScrubPosition {}; double mMaxScrubSpeed; bool mScrubSeekPress; bool mSmoothScrollingScrub; - bool mAlwaysSeeking{}; + bool mAlwaysSeeking {}; + bool mDragging {}; #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL int mLogMaxScrubSpeed; diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index 23c279ed7..9174b2272 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -2582,6 +2582,17 @@ bool AdornedRulerPanel::IsWithinMarker(int mousePosX, double markerTime) void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) { + if (mIgnoreMouseUp) { + if (evt.Dragging()) + return; + else if (evt.ButtonUp()) { + mIgnoreMouseUp = false; + return; + } + else + mIgnoreMouseUp = false; + } + // PRL: why do I need these two lines on Windows but not on Mac? if (evt.ButtonDown(wxMOUSE_BTN_ANY)) SetFocus(); @@ -2910,10 +2921,12 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt) if (mDoubleClick) return; - HideQuickPlayIndicator(); - if (HasCapture()) ReleaseMouse(); + else + return; + + HideQuickPlayIndicator(); mCaptureState = CaptureState{}; diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index 177177a0b..f533c0736 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -350,6 +350,7 @@ public: void ShowQuickPlayIndicator(); void HideQuickPlayIndicator(); void UpdateQuickPlayPos(wxCoord &mousPosX); + void IgnoreMouseUp() { mIgnoreMouseUp = true; } private: void OnCapture(wxCommandEvent & evt); @@ -531,6 +532,7 @@ private: mutable wxFont mButtonFont; bool mDoubleClick {}; + bool mIgnoreMouseUp {}; DECLARE_EVENT_TABLE()