From cd57e0a26cfbdd9dc791dc6e4bf627b7e94087dc Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 24 Apr 2016 19:02:13 -0400 Subject: [PATCH 1/4] Scrubbing phase 2. Scrub starts with click on the indicator in the ruler... ... and works in any of the six tools. Click and drag in select tool during scrub works just as when not scrubbing. Seeks now only if you left-click or drag in the ruler, but this may change. Mouse motion anywhere on the screen controls scrub as before. No mouse clicks in TrackPanel are used by scrubbing. The Ctrl-Click in TrackPanel is now unused. Should 2.1.0 behavior be restored? That was click to quick play, redundant with click in the (lower part of) the ruler. --- src/TrackPanel.cpp | 65 +-------------- src/toolbars/ToolsToolBar.cpp | 35 +------- src/tracks/ui/Scrubbing.cpp | 24 +++--- src/tracks/ui/Scrubbing.h | 6 +- src/widgets/Ruler.cpp | 151 +++++++++++++++++++++++++++------- src/widgets/Ruler.h | 9 ++ 6 files changed, 154 insertions(+), 136 deletions(-) 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); From c12993a53ba9fde74d1fafc9664160b17b3ea1a6 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Tue, 26 Apr 2016 11:05:13 -0400 Subject: [PATCH 2/4] Simplify with more uses of HideQuickPlayIndicator() --- src/widgets/Ruler.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index 8f28d999a..3f0eeb97b 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -2140,12 +2140,9 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) if (evt.Leaving() || (changeInScrubZone && inScrubZone)) { if (evt.Leaving()) { // Erase the line - mQuickPlayInd = false; - DrawQuickPlayIndicator(NULL); + HideQuickPlayIndicator(); } - Refresh(); - SetCursor(mCursorDefault); mIsWE = false; @@ -2160,8 +2157,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) } else if (evt.Entering() || (changeInScrubZone && !inScrubZone)) { SetCursor(mCursorHand); - mQuickPlayInd = false; - DrawQuickPlayIndicator(NULL); + HideQuickPlayIndicator(); return; } @@ -2328,8 +2324,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) if (evt.LeftUp()) { - mQuickPlayInd = false; - DrawQuickPlayIndicator(NULL); + HideQuickPlayIndicator(); if (HasCapture()) ReleaseMouse(); From 3d222bcd87e376f495c4bfa312acc522785a26e3 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Tue, 26 Apr 2016 11:09:34 -0400 Subject: [PATCH 3/4] Context menu for scrubbing in the ruler --- src/MemoryX.h | 2 - src/commands/CommandFunctors.h | 24 +++++++++++ src/tracks/ui/Scrubbing.cpp | 32 +++++++++++--- src/tracks/ui/Scrubbing.h | 16 ++++--- src/widgets/Ruler.cpp | 78 +++++++++++++++++++--------------- src/widgets/Ruler.h | 1 + 6 files changed, 107 insertions(+), 46 deletions(-) diff --git a/src/MemoryX.h b/src/MemoryX.h index 5130a4c7c..dfd9ea511 100644 --- a/src/MemoryX.h +++ b/src/MemoryX.h @@ -689,8 +689,6 @@ struct Final_action { Final_action(F f) : clean{ f } {} ~Final_action() { clean(); } F clean; - Final_action(const Final_action&) PROHIBITED; - Final_action& operator= (const Final_action&) PROHIBITED; }; // Function template with type deduction lets you construct Final_action diff --git a/src/commands/CommandFunctors.h b/src/commands/CommandFunctors.h index f6d6ec79d..797cedb65 100644 --- a/src/commands/CommandFunctors.h +++ b/src/commands/CommandFunctors.h @@ -10,6 +10,7 @@ #define __AUDACITY_COMMAND_FUNCTORS__ #include +#include #include "../MemoryX.h" class wxEvent; @@ -61,6 +62,24 @@ private: const audCommandKeyFunction mCommandKeyFunction; }; +// This allows functions to be used either by command manager or by a wxMenu popup, +// but the functions MUST ignore the argument! +template +using audCommandPopupFunction = void (OBJ::*)(wxCommandEvent&); + +template +class PopupFunctor final : public CommandFunctor +{ +public: + explicit PopupFunctor(OBJ *This, audCommandPopupFunction pfn) + : mThis{ This }, mCommandPopupFunction{ pfn } {} + void operator () (int, const wxEvent *) override + { wxCommandEvent dummy; (mThis->*mCommandPopupFunction) (dummy); } +private: + OBJ *const mThis; + const audCommandPopupFunction mCommandPopupFunction; +}; + template using audCommandListFunction = void (OBJ::*)(int); @@ -108,6 +127,11 @@ inline CommandFunctorPointer MakeFunctor(OBJ *This, audCommandKeyFunction pfn) { return CommandFunctorPointer{ safenew KeyFunctor{ This, pfn } }; } +template +inline CommandFunctorPointer MakeFunctor(OBJ *This, + audCommandPopupFunction pfn) +{ return CommandFunctorPointer{ safenew PopupFunctor{ This, pfn } }; } + template inline CommandFunctorPointer MakeFunctor(OBJ *This, audCommandListFunction pfn) diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 13542e314..47ea8c2a7 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -141,7 +141,7 @@ namespace { wxString name; wxString label; wxString status; - void (Scrubber::*memFn)(); + void (Scrubber::*memFn)(wxCommandEvent&); bool scroll; bool seek; @@ -634,26 +634,37 @@ void Scrubber::DoScrub(bool scroll, bool seek) } } -void Scrubber::OnScrub() +void Scrubber::OnScrub(wxCommandEvent&) { DoScrub(false, false); } -void Scrubber::OnScrollScrub() +void Scrubber::OnScrollScrub(wxCommandEvent&) { DoScrub(true, false); } -void Scrubber::OnSeek() +void Scrubber::OnSeek(wxCommandEvent&) { DoScrub(false, true); } -void Scrubber::OnScrollSeek() +void Scrubber::OnScrollSeek(wxCommandEvent&) { DoScrub(true, true); } +enum { CMD_ID = 8000 }; + +BEGIN_EVENT_TABLE(Scrubber, wxEvtHandler) + EVT_MENU(CMD_ID, Scrubber::OnScrub) + EVT_MENU(CMD_ID + 1, Scrubber::OnScrollScrub) + EVT_MENU(CMD_ID + 2, Scrubber::OnSeek) + EVT_MENU(CMD_ID + 3, Scrubber::OnScrollSeek) +END_EVENT_TABLE() + +static_assert(nMenuItems == 4, "wrong number of items"); + const wxString &Scrubber::GetUntranslatedStateString() const { static wxString empty; @@ -697,6 +708,17 @@ void Scrubber::AddMenuItems() CheckMenuItem(); } +void Scrubber::PopulateMenu(wxMenu &menu) +{ + int id = CMD_ID; + auto cm = mProject->GetCommandManager(); + for (const auto &item : menuItems) { + if (cm->GetEnabled(item.name)) + menu.Append(id, item.label); + ++id; + } +} + void Scrubber::UncheckAllMenuItems() { auto cm = mProject->GetCommandManager(); diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index 4de24d58d..418769710 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -66,12 +66,16 @@ public: // This returns the same as the enabled state of the menu items: bool CanScrub() const; - void AddMenuItems(); - void OnScrub(); - void OnScrollScrub(); - void OnSeek(); - void OnScrollSeek(); + // For the toolbar + void AddMenuItems(); + // For popup + void PopulateMenu(wxMenu &menu); + + void OnScrub(wxCommandEvent&); + void OnScrollScrub(wxCommandEvent&); + void OnSeek(wxCommandEvent&); + void OnScrollSeek(wxCommandEvent&); // A string to put in the leftmost part of the status bar. const wxString &GetUntranslatedStateString() const; @@ -101,6 +105,8 @@ private: #endif AudacityProject *mProject; + + DECLARE_EVENT_TABLE() }; // Specialist in drawing the scrub speed, and listening for certain events diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index 3f0eeb97b..0feda9263 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -2162,7 +2162,14 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) } if (evt.RightDown() && !(evt.LeftIsDown())) { - ShowMenu(evt.GetPosition()); + if(inScrubZone) + ShowScrubMenu(evt.GetPosition()); + else + ShowMenu(evt.GetPosition()); + + // dismiss and clear Quick-Play indicator + HideQuickPlayIndicator(); + if (HasCapture()) ReleaseMouse(); return; @@ -2471,52 +2478,55 @@ void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt)) OnMouseEvents(e); } -// Pop-up menu +// Pop-up menus void AdornedRulerPanel::ShowMenu(const wxPoint & pos) { - { - wxMenu rulerMenu; + wxMenu rulerMenu; - if (mQuickPlayEnabled) - rulerMenu.Append(OnToggleQuickPlayID, _("Disable Quick-Play")); - else - rulerMenu.Append(OnToggleQuickPlayID, _("Enable Quick-Play")); + if (mQuickPlayEnabled) + rulerMenu.Append(OnToggleQuickPlayID, _("Disable Quick-Play")); + else + rulerMenu.Append(OnToggleQuickPlayID, _("Enable Quick-Play")); - wxMenuItem *dragitem; - if (mPlayRegionDragsSelection && !mProject->IsPlayRegionLocked()) - dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Disable dragging selection")); - else - dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Enable dragging selection")); - dragitem->Enable(mQuickPlayEnabled && !mProject->IsPlayRegionLocked()); + wxMenuItem *dragitem; + if (mPlayRegionDragsSelection && !mProject->IsPlayRegionLocked()) + dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Disable dragging selection")); + else + dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Enable dragging selection")); + dragitem->Enable(mQuickPlayEnabled && !mProject->IsPlayRegionLocked()); #if wxUSE_TOOLTIPS - if (mTimelineToolTip) - rulerMenu.Append(OnTimelineToolTipID, _("Disable Timeline Tooltips")); - else - rulerMenu.Append(OnTimelineToolTipID, _("Enable Timeline Tooltips")); + if (mTimelineToolTip) + rulerMenu.Append(OnTimelineToolTipID, _("Disable Timeline Tooltips")); + else + rulerMenu.Append(OnTimelineToolTipID, _("Enable Timeline Tooltips")); #endif - if (mViewInfo->bUpdateTrackIndicator) - rulerMenu.Append(OnAutoScrollID, _("Do not scroll while playing")); - else - rulerMenu.Append(OnAutoScrollID, _("Update display while playing")); + if (mViewInfo->bUpdateTrackIndicator) + rulerMenu.Append(OnAutoScrollID, _("Do not scroll while playing")); + else + rulerMenu.Append(OnAutoScrollID, _("Update display while playing")); - wxMenuItem *prlitem; - if (!mProject->IsPlayRegionLocked()) - prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Lock Play Region")); - else - prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Unlock Play Region")); - prlitem->Enable(mProject->IsPlayRegionLocked() || (mPlayRegionStart != mPlayRegionEnd)); + wxMenuItem *prlitem; + if (!mProject->IsPlayRegionLocked()) + prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Lock Play Region")); + else + prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Unlock Play Region")); + prlitem->Enable(mProject->IsPlayRegionLocked() || (mPlayRegionStart != mPlayRegionEnd)); - PopupMenu(&rulerMenu, pos); - } + PopupMenu(&rulerMenu, pos); +} - // dismiss and clear Quick-Play indicator - mQuickPlayInd = false; - DrawQuickPlayIndicator(NULL); +void AdornedRulerPanel::ShowScrubMenu(const wxPoint & pos) +{ + auto &scrubber = mProject->GetScrubber(); + PushEventHandler(&scrubber); + auto cleanup = finally([this]{ PopEventHandler(); }); - Refresh(); + wxMenu rulerMenu; + mProject->GetScrubber().PopulateMenu(rulerMenu); + PopupMenu(&rulerMenu, pos); } void AdornedRulerPanel::OnToggleQuickPlay(wxCommandEvent&) diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index 49031ed88..274a42908 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -390,6 +390,7 @@ private: // Pop-up menu // void ShowMenu(const wxPoint & pos); + void ShowScrubMenu(const wxPoint & pos); void DragSelection(); void HandleSnapping(); void OnToggleQuickPlay(wxCommandEvent &evt); From 9ab0e42f29dc3f7be778d7ed4374ef060c3b55ff Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 21 Apr 2016 20:59:38 -0400 Subject: [PATCH 4/4] Control scrub with motion, click, drag, wheel almost anywhere in main window... ... if the event is not handled and skipped by sub-windows first, such as for toolbar button clicks. (But track panel clicks are skipped even after doing something, so they may also cause seeking besides other responses. So click can seek AND set cursor.) This is meant to make drag to seek and wheel for change of speed easier, without needing to keep the mouse in the narrow time ruler. Also lets you click in the ruler, then move in any direction, and not miss the motion event that should start the scrub playback. The event handling is a bit of a hack, using propagation. It does not use capture. --- src/Project.cpp | 5 +++- src/Project.h | 2 +- src/TrackPanel.cpp | 20 +++++++++++++++- src/TrackPanelListener.h | 2 +- src/toolbars/ToolBar.cpp | 8 +++++++ src/toolbars/ToolBar.h | 1 + src/toolbars/ToolDock.cpp | 8 +++++++ src/toolbars/ToolDock.h | 1 + src/tracks/ui/Scrubbing.cpp | 46 +++++++++++++++++++++++++++++++++++-- src/tracks/ui/Scrubbing.h | 8 +++++-- src/widgets/AButton.cpp | 2 ++ src/widgets/Ruler.cpp | 40 ++++++++++++++++++-------------- src/widgets/Ruler.h | 2 ++ 13 files changed, 120 insertions(+), 25 deletions(-) diff --git a/src/Project.cpp b/src/Project.cpp index 0457f9869..ffd8640d3 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -1613,7 +1613,7 @@ void AudacityProject::TP_ScrollWindow(double scrollto) // handler in Track Panel. A positive argument makes the window // scroll down, while a negative argument scrolls up. // -void AudacityProject::TP_ScrollUpDown(int delta) +bool AudacityProject::TP_ScrollUpDown(int delta) { int oldPos = mVsbar->GetThumbPosition(); int pos = oldPos + delta; @@ -1634,7 +1634,10 @@ void AudacityProject::TP_ScrollUpDown(int delta) wxScrollEvent dummy; OnScroll(dummy); + return true; } + else + return false; } void AudacityProject::FixScrollbars() diff --git a/src/Project.h b/src/Project.h index fa7e51a61..069221099 100644 --- a/src/Project.h +++ b/src/Project.h @@ -430,7 +430,7 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame, void TP_ScrollLeft() override; void TP_ScrollRight() override; void TP_ScrollWindow(double scrollto) override; - void TP_ScrollUpDown(int delta) override; + bool TP_ScrollUpDown(int delta) override; void TP_HandleResize() override; // ToolBar diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index bbadb0239..ef8b5a38a 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -5473,6 +5473,13 @@ void TrackPanel::HandleResize(wxMouseEvent & event) /// Handle mouse wheel rotation (for zoom in/out, vertical and horizontal scrolling) void TrackPanel::HandleWheelRotation(wxMouseEvent & event) { + if(!event.HasAnyModifiers()) { + // We will later un-skip if we do anything, but if we don't, + // propagate the event up for the sake of the scrubber + event.Skip(); + event.ResumePropagation(wxEVENT_PROPAGATE_MAX); + } + if (GetTracks()->IsEmpty()) // Scrolling and Zoom in and out commands are disabled when there are no tracks. // This should be disabled too for consistency. Otherwise @@ -5485,6 +5492,9 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event) Track *const pTrack = FindTrack(event.m_x, event.m_y, true, false, &rect); if (pTrack && event.m_x >= GetVRulerOffset()) { HandleWheelRotationInVRuler(event, pTrack, rect); + // Always stop propagation even if the ruler didn't change. The ruler + // is a narrow enough target. + event.Skip(false); return; } } @@ -5574,7 +5584,8 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event) double lines = steps * 4 + mVertScrollRemainder; mVertScrollRemainder = lines - floor(lines); lines = floor(lines); - mListener->TP_ScrollUpDown((int)-lines); + const bool didSomething = mListener->TP_ScrollUpDown((int)-lines); + event.Skip(!didSomething); } } } @@ -5862,6 +5873,13 @@ void TrackPanel::OnMouseEvent(wxMouseEvent & event) if (event.m_wheelRotation != 0) HandleWheelRotation(event); + if (event.LeftDown() || event.LeftIsDown() || event.Moving()) { + // Skip, even if we do something, so that the left click or drag + // may have an additional effect in the scrubber. + event.Skip(); + event.ResumePropagation(wxEVENT_PROPAGATE_MAX); + } + if (!mAutoScrolling) { mMouseMostRecentX = event.m_x; mMouseMostRecentY = event.m_y; diff --git a/src/TrackPanelListener.h b/src/TrackPanelListener.h index 0fe57cc3c..cb479b7bd 100644 --- a/src/TrackPanelListener.h +++ b/src/TrackPanelListener.h @@ -34,7 +34,7 @@ class AUDACITY_DLL_API TrackPanelListener /* not final */ { virtual void TP_ScrollLeft() = 0; virtual void TP_ScrollRight() = 0; virtual void TP_ScrollWindow(double scrollto) = 0; - virtual void TP_ScrollUpDown(int delta) = 0; + virtual bool TP_ScrollUpDown(int delta) = 0; virtual void TP_HandleResize() = 0; }; diff --git a/src/toolbars/ToolBar.cpp b/src/toolbars/ToolBar.cpp index 87a76cf34..ebec57e8c 100644 --- a/src/toolbars/ToolBar.cpp +++ b/src/toolbars/ToolBar.cpp @@ -276,6 +276,7 @@ DEFINE_EVENT_TYPE(EVT_TOOLBAR_UPDATED) BEGIN_EVENT_TABLE( ToolBar, wxPanel ) EVT_PAINT( ToolBar::OnPaint ) EVT_ERASE_BACKGROUND( ToolBar::OnErase ) + EVT_MOUSE_EVENTS( ToolBar::OnMouseEvents ) END_EVENT_TABLE() // @@ -841,6 +842,13 @@ void ToolBar::OnPaint( wxPaintEvent & event ) #endif } +void ToolBar::OnMouseEvents(wxMouseEvent &event) +{ + // Do this hack so scrubber can detect mouse drags anywhere + event.ResumePropagation(wxEVENT_PROPAGATE_MAX); + event.Skip(); +} + int ToolBar::GetResizeGrabberWidth() { return RWIDTH; diff --git a/src/toolbars/ToolBar.h b/src/toolbars/ToolBar.h index e76109dbc..4f1b1b198 100644 --- a/src/toolbars/ToolBar.h +++ b/src/toolbars/ToolBar.h @@ -188,6 +188,7 @@ class ToolBar /* not final */ : public wxPanel void OnErase(wxEraseEvent & event); void OnPaint(wxPaintEvent & event); + void OnMouseEvents(wxMouseEvent &event); protected: wxString mLabel; diff --git a/src/toolbars/ToolDock.cpp b/src/toolbars/ToolDock.cpp index 5fd488d2a..f760bccb3 100644 --- a/src/toolbars/ToolDock.cpp +++ b/src/toolbars/ToolDock.cpp @@ -64,6 +64,7 @@ BEGIN_EVENT_TABLE( ToolDock, wxPanel ) EVT_ERASE_BACKGROUND( ToolDock::OnErase ) EVT_PAINT( ToolDock::OnPaint ) EVT_SIZE( ToolDock::OnSize ) + EVT_MOUSE_EVENTS( ToolDock::OnMouseEvents ) END_EVENT_TABLE() // @@ -556,3 +557,10 @@ void ToolDock::OnPaint( wxPaintEvent & WXUNUSED(event) ) } } } + +void ToolDock::OnMouseEvents(wxMouseEvent &event) +{ + // Do this hack so scrubber can detect mouse drags anywhere + event.ResumePropagation(wxEVENT_PROPAGATE_MAX); + event.Skip(); +} diff --git a/src/toolbars/ToolDock.h b/src/toolbars/ToolDock.h index 4d4619a23..f45989c06 100644 --- a/src/toolbars/ToolDock.h +++ b/src/toolbars/ToolDock.h @@ -70,6 +70,7 @@ class ToolDock final : public wxPanel void OnSize( wxSizeEvent & event ); void OnPaint( wxPaintEvent & event ); void OnGrabber( GrabberEvent & event ); + void OnMouseEvents(wxMouseEvent &event); private: diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 47ea8c2a7..036c4c1fc 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -19,6 +19,7 @@ Paul Licameli split from TrackPanel.cpp #include "../../TrackPanelCellIterator.h" #include "../../commands/CommandFunctors.h" #include "../../toolbars/ControlToolBar.h" +#include "../../widgets/Ruler.h" #include @@ -126,10 +127,12 @@ Scrubber::Scrubber(AudacityProject *project) wxTheApp->Connect (wxEVT_ACTIVATE_APP, wxActivateEventHandler(Scrubber::OnActivateOrDeactivateApp), NULL, this); + mProject->PushEventHandler(this); } Scrubber::~Scrubber() { + mProject->PopEventHandler(); if (wxTheApp) wxTheApp->Disconnect (wxEVT_ACTIVATE_APP, @@ -180,6 +183,7 @@ namespace { } void Scrubber::MarkScrubStart( + // Assume xx is relative to the left edge of TrackPanel! wxCoord xx #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL , bool smoothScrolling @@ -207,7 +211,8 @@ void Scrubber::MarkScrubStart( } #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT -bool Scrubber::MaybeStartScrubbing(const wxMouseEvent &event) +// Assume xx is relative to the left edge of TrackPanel! +bool Scrubber::MaybeStartScrubbing(wxCoord xx) { if (mScrubStartPosition < 0) return false; @@ -222,7 +227,7 @@ bool Scrubber::MaybeStartScrubbing(const wxMouseEvent &event) return false; } - wxCoord position = event.m_x; + wxCoord position = xx; if (abs(mScrubStartPosition - position) >= SCRUBBING_PIXEL_TOLERANCE) { const ViewInfo &viewInfo = mProject->GetViewInfo(); TrackPanel *const trackPanel = mProject->GetTrackPanel(); @@ -450,6 +455,40 @@ void Scrubber::OnActivateOrDeactivateApp(wxActivateEvent &event) event.Skip(); } +void Scrubber::OnMouse(wxMouseEvent &event) +{ + auto isScrubbing = IsScrubbing(); + if (!isScrubbing && HasStartedScrubbing()) { + if (!event.HasAnyModifiers() && + event.GetEventType() == wxEVT_MOTION) { + + // Really start scrub if motion is far enough + auto ruler = mProject->GetRulerPanel(); + auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x; + MaybeStartScrubbing(xx + ); + } + } + else if (isScrubbing && !event.HasAnyModifiers()) { + if(event.LeftDown() || + (event.LeftIsDown() && event.Dragging())) { + mScrubSeekPress = true; + auto ruler = mProject->GetRulerPanel(); + auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x; + ruler->UpdateQuickPlayPos(xx); + } + else if (event.m_wheelRotation) { + double steps = event.m_wheelRotation / + (event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0); + HandleScrollWheel(steps); + } + else + event.Skip(); + } + else + event.Skip(); +} + /////////////////////////////////////////////////////////////////////////////// // class ScrubbingOverlay is responsible for drawing the speed numbers @@ -661,6 +700,9 @@ BEGIN_EVENT_TABLE(Scrubber, wxEvtHandler) EVT_MENU(CMD_ID + 1, Scrubber::OnScrollScrub) EVT_MENU(CMD_ID + 2, Scrubber::OnSeek) EVT_MENU(CMD_ID + 3, Scrubber::OnScrollSeek) + + EVT_MOUSE_EVENTS(Scrubber::OnMouse) + END_EVENT_TABLE() static_assert(nMenuItems == 4, "wrong number of items"); diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index 418769710..9f83ea8f6 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -27,6 +27,7 @@ public: Scrubber(AudacityProject *project); ~Scrubber(); + // Assume xx is relative to the left edge of TrackPanel! void MarkScrubStart( wxCoord xx #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL @@ -35,8 +36,11 @@ public: , bool alwaysSeeking // if false, can switch seeking or scrubbing // by mouse button state ); + // Returns true iff the event should be considered consumed by this: - bool MaybeStartScrubbing(const wxMouseEvent &event); + // Assume xx is relative to the left edge of TrackPanel! + bool MaybeStartScrubbing(wxCoord xx); + void ContinueScrubbing(); // This is meant to be called only from ControlToolBar @@ -61,7 +65,6 @@ public: void HandleScrollWheel(int steps); - void SetSeeking() { mScrubSeekPress = true; } bool PollIsSeeking(); // This returns the same as the enabled state of the menu items: @@ -88,6 +91,7 @@ private: void OnActivateOrDeactivateApp(wxActivateEvent & event); void UncheckAllMenuItems(); void CheckMenuItem(); + void OnMouse(wxMouseEvent &event); private: int mScrubToken; diff --git a/src/widgets/AButton.cpp b/src/widgets/AButton.cpp index 409bcd963..58c336771 100644 --- a/src/widgets/AButton.cpp +++ b/src/widgets/AButton.cpp @@ -452,6 +452,8 @@ void AButton::OnMouseEvent(wxMouseEvent & event) GetActiveProject()->TP_DisplayStatusMessage(wxT("")); } } + else + event.Skip(); } void AButton::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event)) diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index 0feda9263..97f3d5006 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -2080,20 +2080,15 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) : StatusChoice::NoChange ); - // Keep Quick-Play within usable track area. - TrackPanel *tp = mProject->GetTrackPanel(); - int mousePosX, width; - tp->GetTracksUsableArea(&width, NULL); - mousePosX = std::max(evt.GetX(), tp->GetLeftOffset()); - mousePosX = std::min(mousePosX, tp->GetLeftOffset() + width - 1); double t0 = mTracks->GetStartTime(); double t1 = mTracks->GetEndTime(); double sel0 = mProject->GetSel0(); double sel1 = mProject->GetSel1(); - mLastMouseX = mousePosX; - mQuickPlayPos = Pos2Time(mousePosX); + wxCoord mousePosX = evt.GetX(); + UpdateQuickPlayPos(mousePosX); + // If not looping, restrict selection to end of project if (!inScrubZone && !evt.ShiftDown()) { mQuickPlayPos = std::min(t1, mQuickPlayPos); @@ -2103,18 +2098,16 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) // 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(); - } + // Do this hack so scrubber can detect mouse drags anywhere + evt.ResumePropagation(wxEVENT_PROPAGATE_MAX); + + if (scrubber.IsScrubbing()) + evt.Skip(); 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); - } + else + evt.Skip(); mQuickPlayInd = true; wxClientDC dc(this); @@ -2478,6 +2471,19 @@ void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt)) OnMouseEvents(e); } +void AdornedRulerPanel::UpdateQuickPlayPos(wxCoord &mousePosX) +{ + // Keep Quick-Play within usable track area. + TrackPanel *tp = mProject->GetTrackPanel(); + int width; + tp->GetTracksUsableArea(&width, NULL); + mousePosX = std::max(mousePosX, tp->GetLeftOffset()); + mousePosX = std::min(mousePosX, tp->GetLeftOffset() + width - 1); + + mLastMouseX = mousePosX; + mQuickPlayPos = Pos2Time(mousePosX); +} + // Pop-up menus void AdornedRulerPanel::ShowMenu(const wxPoint & pos) diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index 274a42908..069ca6b5a 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -312,6 +312,8 @@ public: void RegenerateTooltips(); void HideQuickPlayIndicator(); + void UpdateQuickPlayPos(wxCoord &mousPosX); + private: void OnCapture(wxCommandEvent & evt); void OnPaint(wxPaintEvent &evt);