diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 6c8efd12c..bbadb0239 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -1525,13 +1525,7 @@ void TrackPanel::SetCursorAndTipWhenSelectTool( Track * t, // If not shift-down and not snapping center, then // choose boundaries only in snapping tolerance, // and may choose center. - // But don't change the cursor when scrubbing. SelectionBoundary boundary = -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - GetProject()->GetScrubber().IsScrubbing() - ? SBNone - : -#endif ChooseBoundary(event, t, rect, !bShiftDown, !bShiftDown); #ifdef USE_MIDI @@ -1705,14 +1699,7 @@ void TrackPanel::HandleCursor(const wxMouseEvent & event) tip = ttb->GetMessageForTool(tool); - const auto &scrubber = GetProject()->GetScrubber(); - if (scrubber.HasStartedScrubbing()) { - if (scrubber.IsScrollScrubbing()) - tip = _("Move to adjust speed, click to skip, ESC to stop."); - else - tip = _("Move to scrub, click to seek, ESC to stop."); - } - else if( tool != selectTool ) + if( tool != selectTool ) { // We don't include the select tool in // SetCursorAndTipByTool() because it's more complex than @@ -1773,11 +1760,7 @@ void TrackPanel::HandleSelect(wxMouseEvent & event) mFreqSelMode = FREQ_SEL_INVALID; #endif - } else if (event.LeftDClick() && !event.ShiftDown() -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - && !event.CmdDown() -#endif - ) { + } else if (event.LeftDClick() && !event.ShiftDown()) { if (!mCapturedTrack) { wxRect rect; mCapturedTrack = @@ -1950,29 +1933,9 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, #endif ) { -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - if ( -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - event.LeftDClick() || -#endif - event.LeftDown()) { - SetCapturedTrack(nullptr, IsUncaptured); - GetProject()->GetScrubber().MarkScrubStart( - event -#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL - , event.LeftDClick() -#endif - , false - ); - return; - } - -#else - + // Used to jump the play head, but it is redundant with timeline quick play // StartOrJumpPlayback(event); -#endif - // Not starting a drag SetCapturedTrack(NULL, IsUncaptured); return; @@ -2693,14 +2656,6 @@ void TrackPanel::Stretch(int mouseXCoordinate, int trackLeftEdge, /// handle it here. void TrackPanel::SelectionHandleDrag(wxMouseEvent & event, Track *clickedTrack) { -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - Scrubber &scrubber = GetProject()->GetScrubber(); - if (scrubber.IsScrubbing() || - GetProject()->GetScrubber().MaybeStartScrubbing(event)) - // Do nothing more, don't change selection - return; -#endif - // AS: If we're not in the process of selecting (set in // the SelectionHandleClick above), fuhggeddaboudit. if (mMouseCapture!=IsSelecting) @@ -6366,20 +6321,6 @@ void TrackPanel::HandleTrackSpecificMouseEvent(wxMouseEvent & event) return; } -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - if (GetProject()->GetScrubber().IsScrubbing() && - GetRect().Contains(event.GetPosition()) && - (!pTrack || - pTrack->GetKind() == Track::Wave)) { - if (event.LeftDown()) { - GetProject()->GetScrubber().SetSeeking(); - return; - } - else if (event.LeftIsDown()) - return; - } -#endif - bool handled = false; if (pTrack && (pTrack->GetKind() == Track::Wave) && diff --git a/src/toolbars/ToolsToolBar.cpp b/src/toolbars/ToolsToolBar.cpp index 6ef363e49..22cf8af67 100644 --- a/src/toolbars/ToolsToolBar.cpp +++ b/src/toolbars/ToolsToolBar.cpp @@ -55,9 +55,6 @@ #include "../Theme.h" #include "../Experimental.h" -#ifdef EXPERIMENTAL_SCRUBBING_BASIC -#include "../tracks/ui/Scrubbing.h" -#endif #include "../widgets/AButton.h" @@ -87,21 +84,7 @@ ToolsToolBar::ToolsToolBar() wxASSERT( drawTool == drawTool - firstTool ); wxASSERT( multiTool == multiTool - firstTool ); - { -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - - mMessageOfTool[selectTool] = -#if defined(__WXMAC__) - _("Click and drag to select audio, Command-Click to scrub, Command-Double-Click to scroll-scrub, Command-drag to seek") -#else - _("Click and drag to select audio, Ctrl-Click to scrub, Ctrl-Double-Click to scroll-scrub, Ctrl-drag to seek") -#endif - ; - -#else - mMessageOfTool[selectTool] = _("Click and drag to select audio"); -#endif - } + mMessageOfTool[selectTool] = _("Click and drag to select audio"); mMessageOfTool[envelopeTool] = _("Click and drag to edit the amplitude envelope"); mMessageOfTool[drawTool] = _("Click and drag to edit the samples"); @@ -219,14 +202,6 @@ void ToolsToolBar::SetCurrentTool(int tool, bool show) //In multi-mode the current tool is shown by the //cursor icon. The buttons are not updated. -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - if (tool != selectTool) { - AudacityProject *const p = GetActiveProject(); - if (p) - p->GetScrubber().StopScrubbing(); - } -#endif - bool leavingMulticlipMode = IsDown(multiTool) && show && tool != multiTool; @@ -290,14 +265,6 @@ void ToolsToolBar::OnTool(wxCommandEvent & evt) else mTool[i]->PopUp(); -#ifdef EXPERIMENTAL_SCRUBBING_BASIC - if (0 != mCurrentTool) { - AudacityProject *const p = GetActiveProject(); - if (p) - p->GetScrubber().StopScrubbing(); - } -#endif - RedrawAllProjects(); gPrefs->Write(wxT("/GUI/ToolBars/Tools/MultiToolActive"), diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 1ff68a785..13542e314 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -180,7 +180,7 @@ namespace { } void Scrubber::MarkScrubStart( - const wxMouseEvent &event + wxCoord xx #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL , bool smoothScrolling #endif @@ -189,8 +189,6 @@ void Scrubber::MarkScrubStart( { UncheckAllMenuItems(); - const wxCoord xx = event.m_x; - // Don't actually start scrubbing, but collect some information // needed for the decision to start scrubbing later when handling // drag events. @@ -204,7 +202,6 @@ void Scrubber::MarkScrubStart( ControlToolBar * const ctb = mProject->GetControlToolBar(); ctb->SetPlay(true, ControlToolBar::PlayAppearance::Scrub); ctb->UpdateStatusBar(mProject); - mProject->GetTrackPanel()->HandleCursor(event); CheckMenuItem(); } @@ -299,9 +296,11 @@ void Scrubber::ContinueScrubbing() // Seek only when the pointer is in the panel. Else, scrub. const wxMouseState state(::wxGetMouseState()); TrackPanel *const trackPanel = mProject->GetTrackPanel(); - const wxPoint position = trackPanel->ScreenToClient(state.GetPosition()); - const bool inPanel = trackPanel->GetRect().Contains(position); - const bool seek = inPanel && (mScrubSeekPress || PollIsSeeking()); + + // Decide whether to skip play, because either mouse is down now, + // or there was a left click event. (This is then a delayed reaction, in a + // timer callback, to a left click event detected elsewhere.) + const bool seek = PollIsSeeking() || mScrubSeekPress; { // Show the correct status for seeking. @@ -312,6 +311,7 @@ void Scrubber::ContinueScrubbing() mAlwaysSeeking = backup; } + const wxPoint position = trackPanel->ScreenToClient(state.GetPosition()); // When we don't have focus, enqueue silent scrubs until we regain focus. bool result = false; if (!mScrubHasFocus) @@ -615,9 +615,7 @@ void Scrubber::DoScrub(bool scroll, bool seek) if (!wasScrubbing) { auto tp = mProject->GetTrackPanel(); wxCoord xx = tp->ScreenToClient(::wxGetMouseState().GetPosition()).x; - wxMouseEvent evt; - evt.SetX(xx); - MarkScrubStart(evt, scroll, seek); + MarkScrubStart(xx, scroll, seek); } else if(!match) { mSmoothScrollingScrub = scroll; @@ -677,6 +675,12 @@ std::vector Scrubber::GetAllUntranslatedStatusStrings() return move(results); } +bool Scrubber::CanScrub() const +{ + auto cm = mProject->GetCommandManager(); + return cm->GetEnabled(menuItems[0].name); +} + void Scrubber::AddMenuItems() { auto cm = mProject->GetCommandManager(); diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index 54e64f874..4de24d58d 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -28,7 +28,7 @@ public: ~Scrubber(); void MarkScrubStart( - const wxMouseEvent &event + wxCoord xx #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL , bool smoothScrolling #endif @@ -52,6 +52,8 @@ public: bool IsScrubbing() const; bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing() { return mSmoothScrollingScrub; } + bool IsAlwaysSeeking() const + { return mAlwaysSeeking; } bool ShouldDrawScrubSpeed(); double FindScrubSpeed(bool seeking, double time) const; @@ -62,6 +64,8 @@ public: void SetSeeking() { mScrubSeekPress = true; } bool PollIsSeeking(); + // This returns the same as the enabled state of the menu items: + bool CanScrub() const; void AddMenuItems(); void OnScrub(); diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index b0f7e7ce6..8f28d999a 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -1717,8 +1717,6 @@ void QuickPlayIndicatorOverlay::Draw if (mOldQPIndicatorPos >= 0) { mOldPreviewingScrub ? AColor::IndicatorColor(&dc, true) // Draw green line for preview. - // Drawing during actual scrub not by this class, - // but by PlayIndicatorOverlay : mOldQPIndicatorSnapped ? AColor::SnapGuidePen(&dc) : AColor::Light(&dc, false) @@ -2061,22 +2059,26 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) if (mIsRecording) return; - // Handle status bar messages - if(evt.Leaving()) { - mProject->TP_DisplayStatusMessage(wxT("")); - } - else if(evt.Entering()) { - // Insert timeline status bar messages here - mProject->TP_DisplayStatusMessage - (wxT("")); - } + const bool inScrubZone = + // only if scrubbing is allowed now + mProject->GetScrubber().CanScrub() && + evt.m_y < IndicatorBigHeight(); - // Store the initial play region state - if(mMouseEventState == mesNone) { - mOldPlayRegionStart = mPlayRegionStart; - mOldPlayRegionEnd = mPlayRegionEnd; - mPlayRegionLock = mProject->IsPlayRegionLocked(); - } + const bool changeInScrubZone = (inScrubZone != mPrevInScrubZone); + mPrevInScrubZone = inScrubZone; + + auto &scrubber = mProject->GetScrubber(); + + // Handle status bar messages + UpdateStatusBar ( + evt.Leaving() + ? StatusChoice::Leaving + : evt.Entering() || changeInScrubZone + ? inScrubZone + ? StatusChoice::EnteringScrubZone + : StatusChoice::EnteringQP + : StatusChoice::NoChange + ); // Keep Quick-Play within usable track area. TrackPanel *tp = mProject->GetTrackPanel(); @@ -2085,11 +2087,6 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) mousePosX = std::max(evt.GetX(), tp->GetLeftOffset()); mousePosX = std::min(mousePosX, tp->GetLeftOffset() + width - 1); - bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart); - bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd); - bool isWithinClick = (mLeftDownClick >= 0) && IsWithinMarker(mousePosX, mLeftDownClick); - bool canDragSel = !mPlayRegionLock && mPlayRegionDragsSelection; - double t0 = mTracks->GetStartTime(); double t1 = mTracks->GetEndTime(); double sel0 = mProject->GetSel0(); @@ -2098,13 +2095,55 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) mLastMouseX = mousePosX; mQuickPlayPos = Pos2Time(mousePosX); // If not looping, restrict selection to end of project - if (!evt.ShiftDown()) { + if (!inScrubZone && !evt.ShiftDown()) { mQuickPlayPos = std::min(t1, mQuickPlayPos); } - if (evt.Leaving()) { - mQuickPlayInd = false; - DrawQuickPlayIndicator(NULL); + if (scrubber.HasStartedScrubbing()) { + // If already clicked for scrub, preempt the usual event handling, + // no matter what the y coordinate. + + if (scrubber.IsScrubbing()) { + if(evt.LeftDown() || evt.Dragging()) + // Cause scrub in progress to jump + scrubber.SetSeeking(); + } + else if (evt.LeftDClick()) + // On the second button down, switch the pending scrub to scrolling + scrubber.MarkScrubStart(evt.m_x, true, false); + else if (!evt.Button(wxMOUSE_BTN_ANY)) { + // Really start scrub if motion is far enough + scrubber.MaybeStartScrubbing(evt); + } + + mQuickPlayInd = true; + wxClientDC dc(this); + DrawQuickPlayIndicator(&dc); + + return; + } + + // Store the initial play region state + if(mMouseEventState == mesNone) { + mOldPlayRegionStart = mPlayRegionStart; + mOldPlayRegionEnd = mPlayRegionEnd; + mPlayRegionLock = mProject->IsPlayRegionLocked(); + } + + bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart); + bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd); + bool isWithinClick = (mLeftDownClick >= 0) && IsWithinMarker(mousePosX, mLeftDownClick); + bool canDragSel = !mPlayRegionLock && mPlayRegionDragsSelection; + + // Handle entering and leaving of the bar, or movement from + // one portion (quick play or scrub) to the other + if (evt.Leaving() || (changeInScrubZone && inScrubZone)) { + if (evt.Leaving()) { + // Erase the line + mQuickPlayInd = false; + DrawQuickPlayIndicator(NULL); + } + Refresh(); SetCursor(mCursorDefault); @@ -2114,9 +2153,12 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) delete mSnapManager; mSnapManager = NULL; } - return; + + if(evt.Leaving()) + return; + // else, may detect a scrub click below } - else if (evt.Entering()) { + else if (evt.Entering() || (changeInScrubZone && !inScrubZone)) { SetCursor(mCursorHand); mQuickPlayInd = false; DrawQuickPlayIndicator(NULL); @@ -2127,6 +2169,15 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) ShowMenu(evt.GetPosition()); if (HasCapture()) ReleaseMouse(); + return; + } + else if (inScrubZone) { + if (evt.LeftDown()) + scrubber.MarkScrubStart(evt.m_x, false, false); + UpdateStatusBar(StatusChoice::EnteringScrubZone); + wxClientDC dc(this); + DrawQuickPlayIndicator(&dc); + return; } if (!mQuickPlayEnabled) @@ -2377,6 +2428,45 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) } } +void AdornedRulerPanel::UpdateStatusBar(StatusChoice choice) +{ + if (choice == StatusChoice::NoChange) + return; + + const auto &scrubber = mProject->GetScrubber(); + const bool scrubbing = scrubber.HasStartedScrubbing(); + if (scrubbing && choice != StatusChoice::Leaving) + // Don't distinguish zones + choice = StatusChoice::EnteringScrubZone; + wxString message{}; + + switch (choice) { + case StatusChoice::EnteringQP: + { + // message = Insert timeline status bar message here + } + break; + + case StatusChoice::EnteringScrubZone: + { + if (scrubbing) { + if(!scrubber.IsAlwaysSeeking()) + message = _("Click or drag to seek"); + } + else + message = _("Click to scrub, Double-Click to scroll, Drag to seek"); + } + break; + + case StatusChoice::Leaving: + default: + break; + } + + // Display a message, or empty message + mProject->TP_DisplayStatusMessage(message); +} + void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt)) { DrawQuickPlayIndicator(NULL); @@ -2745,7 +2835,10 @@ void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc) } const int x = Time2Pos(mQuickPlayPos); - GetOverlay()->Update(x, mIsSnapped, mPrevInScrubZone); + bool previewScrub = + mPrevInScrubZone && + !mProject->GetScrubber().IsScrubbing(); + GetOverlay()->Update(x, mIsSnapped, previewScrub); DoEraseIndicator(dc, mLastQuickPlayX); mLastQuickPlayX = x; diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index 10ef82e7e..49031ed88 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -317,6 +317,15 @@ private: void OnPaint(wxPaintEvent &evt); void OnSize(wxSizeEvent &evt); void OnMouseEvents(wxMouseEvent &evt); + + enum class StatusChoice { + EnteringQP, + EnteringScrubZone, + Leaving, + NoChange + }; + void UpdateStatusBar(StatusChoice choice); + void OnCaptureLost(wxMouseCaptureLostEvent &evt); void DoDrawBorder(wxDC * dc);