From aeece118e8950d2aeee60ef4e1b2a2ee752129cd Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 4 Aug 2018 11:49:08 -0400 Subject: [PATCH] Rewrite the handling of quick play indicators... ... All updates of position are done in DoGetRectangle(). Ruler need only expose one function, DrawOverlays(). Don't redraw indicators twice when dragging (hiding and showing again), making some flicker. Just do one update. --- src/toolbars/ControlToolBar.cpp | 2 +- src/tracks/ui/Scrubbing.cpp | 4 +- src/widgets/Ruler.cpp | 227 ++++++++++++++++---------------- src/widgets/Ruler.h | 19 ++- 4 files changed, 124 insertions(+), 128 deletions(-) diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 31136b83e..913acb6d5 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -735,7 +735,7 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, // Let other UI update appearance if (p) - p->GetRulerPanel()->HideQuickPlayIndicator(); + p->GetRulerPanel()->DrawBothOverlays(); return token; } diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 12d48fef0..05ac323ea 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -655,7 +655,7 @@ void Scrubber::StopScrubbing() ctb->SetPlay(false, ControlToolBar::PlayAppearance::Straight); } - mProject->GetRulerPanel()->HideQuickPlayIndicator(); + mProject->GetRulerPanel()->DrawBothOverlays(); CheckMenuItems(); } @@ -903,7 +903,7 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event) return; } else - ruler->ShowQuickPlayIndicator(); + ruler->DrawBothOverlays(); } if (!scrubber.ShouldDrawScrubSpeed()) { diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index 0927ebc3e..4f4bd2ce4 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -1750,13 +1750,22 @@ class QuickPlayIndicatorOverlay; // This is an overlay drawn on the ruler. It draws the little triangle or // the double-headed arrow. -class QuickPlayRulerOverlay final : public Overlay +class AdornedRulerPanel::QuickPlayRulerOverlay final : public Overlay { public: QuickPlayRulerOverlay(QuickPlayIndicatorOverlay &partner); virtual ~QuickPlayRulerOverlay(); - void Update(wxCoord xx) { mNewQPIndicatorPos = xx; } + // Available to this and to partner + + int mNewQPIndicatorPos { -1 }; + bool mNewQPIndicatorSnapped {}; + bool mNewPreviewingScrub {}; + + bool mNewScrub {}; + bool mNewSeek {}; + + void Update(); private: AdornedRulerPanel *GetRuler() const; @@ -1765,7 +1774,11 @@ private: void Draw(OverlayPanel &panel, wxDC &dc) override; QuickPlayIndicatorOverlay &mPartner; - int mOldQPIndicatorPos { -1 }, mNewQPIndicatorPos { -1 }; + + // Used by this only + int mOldQPIndicatorPos { -1 }; + bool mOldScrub {}; + bool mOldSeek {}; }; /********************************************************************** @@ -1777,7 +1790,7 @@ private: // This is an overlay drawn on a different window, the track panel. // It draws the pale guide line that follows mouse movement. -class QuickPlayIndicatorOverlay final : public Overlay +class AdornedRulerPanel::QuickPlayIndicatorOverlay final : public Overlay { friend QuickPlayRulerOverlay; @@ -1786,8 +1799,6 @@ public: virtual ~QuickPlayIndicatorOverlay(); - void Update(int x, bool snapped = false, bool previewScrub = false); - private: std::pair DoGetRectangle(wxSize size) override; void Draw(OverlayPanel &panel, wxDC &dc) override; @@ -1797,9 +1808,9 @@ private: std::unique_ptr mPartner { std::make_unique(*this) }; - int mOldQPIndicatorPos { -1 }, mNewQPIndicatorPos { -1 }; - bool mOldQPIndicatorSnapped {}, mNewQPIndicatorSnapped {}; - bool mOldPreviewingScrub {}, mNewPreviewingScrub {}; + int mOldQPIndicatorPos { -1 }; + bool mOldQPIndicatorSnapped {}; + bool mOldPreviewingScrub {}; }; /********************************************************************** @@ -1808,26 +1819,69 @@ private: **********************************************************************/ -QuickPlayRulerOverlay::QuickPlayRulerOverlay(QuickPlayIndicatorOverlay &partner) +AdornedRulerPanel::QuickPlayRulerOverlay::QuickPlayRulerOverlay( + QuickPlayIndicatorOverlay &partner) : mPartner(partner) { GetRuler()->AddOverlay(this); } -QuickPlayRulerOverlay::~QuickPlayRulerOverlay() +AdornedRulerPanel::QuickPlayRulerOverlay::~QuickPlayRulerOverlay() { auto ruler = GetRuler(); if (ruler) ruler->RemoveOverlay(this); } -AdornedRulerPanel *QuickPlayRulerOverlay::GetRuler() const +AdornedRulerPanel *AdornedRulerPanel::QuickPlayRulerOverlay::GetRuler() const { return mPartner.mProject->GetRulerPanel(); } -std::pair QuickPlayRulerOverlay::DoGetRectangle(wxSize /*size*/) +void AdornedRulerPanel::QuickPlayRulerOverlay::Update() { + const auto project = mPartner.mProject; + auto ruler = GetRuler(); + + // Hide during transport, or if mouse is not in the ruler, unless scrubbing + if ((ruler->mPrevZone == StatusChoice::Leaving || project->IsAudioActive()) + && !project->GetScrubber().IsScrubbing()) + mNewQPIndicatorPos = -1; + else { + double latestEnd = + std::max(ruler->mTracks->GetEndTime(), project->GetSel1()); + if (ruler->mQuickPlayPos >= latestEnd) + mNewQPIndicatorPos = -1; + else { + // This will determine the x coordinate of the line and of the + // ruler indicator + mNewQPIndicatorPos = ruler->Time2Pos(ruler->mQuickPlayPos); + + // These determine which shape is drawn on the ruler, and whether + // in the scrub or the qp zone + const auto &scrubber = mPartner.mProject->GetScrubber(); + mNewScrub = + ruler->mMouseEventState == AdornedRulerPanel::mesNone && + (ruler->mPrevZone == AdornedRulerPanel::StatusChoice::EnteringScrubZone || + (scrubber.HasMark())); + mNewSeek = mNewScrub && + (scrubber.Seeks() || scrubber.TemporarilySeeks()); + + // These two will determine the color of the line stroked over + // the track panel, green for scrub or yellow for snapped or white + mNewPreviewingScrub = + ruler->mPrevZone == StatusChoice::EnteringScrubZone && + !project->GetScrubber().IsScrubbing(); + mNewQPIndicatorSnapped = ruler->mIsSnapped; + } + } +} + +std::pair +AdornedRulerPanel::QuickPlayRulerOverlay::DoGetRectangle(wxSize /*size*/) +{ + Update(); + const auto x = mOldQPIndicatorPos; if (x >= 0) { // These dimensions are always sufficient, even if a little @@ -1843,23 +1897,26 @@ std::pair QuickPlayRulerOverlay::DoGetRectangle(wxSize /*size*/) { xx, yy, indsize * 2 + 1, GetRuler()->GetSize().GetHeight() }, - (x != mNewQPIndicatorPos) + (x != mNewQPIndicatorPos + || (mOldScrub != mNewScrub) + || (mOldSeek != mNewSeek) ) }; } else return { {}, mNewQPIndicatorPos >= 0 }; } -void QuickPlayRulerOverlay::Draw(OverlayPanel & /*panel*/, wxDC &dc) +void AdornedRulerPanel::QuickPlayRulerOverlay::Draw( + OverlayPanel & /*panel*/, wxDC &dc) { mOldQPIndicatorPos = mNewQPIndicatorPos; + mOldScrub = mNewScrub; + mOldSeek = mNewSeek; if (mOldQPIndicatorPos >= 0) { auto ruler = GetRuler(); - const auto &scrubber = mPartner.mProject->GetScrubber(); - auto scrub = ruler->ShowingScrubControl(); - auto seek = scrub && (scrubber.Seeks() || scrubber.TemporarilySeeks()); - auto width = scrub ? IndicatorBigWidth() : IndicatorSmallWidth; - ruler->DoDrawIndicator(&dc, mOldQPIndicatorPos, true, width, scrub, seek); + auto width = mOldScrub ? IndicatorBigWidth() : IndicatorSmallWidth; + ruler->DoDrawIndicator( + &dc, mOldQPIndicatorPos, true, width, mOldScrub, mOldSeek); } } @@ -1869,44 +1926,41 @@ void QuickPlayRulerOverlay::Draw(OverlayPanel & /*panel*/, wxDC &dc) **********************************************************************/ -QuickPlayIndicatorOverlay::QuickPlayIndicatorOverlay(AudacityProject *project) +AdornedRulerPanel::QuickPlayIndicatorOverlay::QuickPlayIndicatorOverlay( + AudacityProject *project) : mProject(project) { auto tp = mProject->GetTrackPanel(); tp->AddOverlay(this); } -QuickPlayIndicatorOverlay::~QuickPlayIndicatorOverlay() +AdornedRulerPanel::QuickPlayIndicatorOverlay::~QuickPlayIndicatorOverlay() { auto tp = mProject->GetTrackPanel(); if (tp) tp->RemoveOverlay(this); } -void QuickPlayIndicatorOverlay::Update(int x, bool snapped, bool previewScrub) +std::pair +AdornedRulerPanel::QuickPlayIndicatorOverlay::DoGetRectangle(wxSize size) { - mNewQPIndicatorPos = x; - mPartner->Update(x); - mNewQPIndicatorSnapped = snapped; - mNewPreviewingScrub = previewScrub; -} + mPartner->Update(); -std::pair QuickPlayIndicatorOverlay::DoGetRectangle(wxSize size) -{ wxRect rect(mOldQPIndicatorPos, 0, 1, size.GetHeight()); return std::make_pair( rect, - (mOldQPIndicatorPos != mNewQPIndicatorPos || - mOldQPIndicatorSnapped != mNewQPIndicatorSnapped || - mOldPreviewingScrub != mNewPreviewingScrub) + (mOldQPIndicatorPos != mPartner->mNewQPIndicatorPos || + mOldQPIndicatorSnapped != mPartner->mNewQPIndicatorSnapped || + mOldPreviewingScrub != mPartner->mNewPreviewingScrub) ); } -void QuickPlayIndicatorOverlay::Draw(OverlayPanel &panel, wxDC &dc) +void AdornedRulerPanel::QuickPlayIndicatorOverlay::Draw( + OverlayPanel &panel, wxDC &dc) { - mOldQPIndicatorPos = mNewQPIndicatorPos; - mOldQPIndicatorSnapped = mNewQPIndicatorSnapped; - mOldPreviewingScrub = mNewPreviewingScrub; + mOldQPIndicatorPos = mPartner->mNewQPIndicatorPos; + mOldQPIndicatorSnapped = mPartner->mNewQPIndicatorSnapped; + mOldPreviewingScrub = mPartner->mNewPreviewingScrub; if (mOldQPIndicatorPos >= 0) { mOldPreviewingScrub @@ -2271,7 +2325,7 @@ void AdornedRulerPanel::OnRecordStartStop(wxCommandEvent & evt) UpdateButtonStates(); // The quick play indicator is useless during recording - HideQuickPlayIndicator(); + CallAfter( [this]{ DrawBothOverlays(); } ); } else { SetCursor(mCursorHand); @@ -2405,6 +2459,11 @@ bool AdornedRulerPanel::IsWithinMarker(int mousePosX, double markerTime) void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) { + // Creation of overlays on demand here -- constructor of AdornedRulerPanel + // is too early to do it + if (!mOverlay) + mOverlay = std::make_unique(mProject); + // Disable mouse actions on Timeline while recording. if (mIsRecording) { if (HasCapture()) @@ -2485,7 +2544,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) // Don't do this, it slows down drag-scrub on Mac. // Timer updates of display elsewhere make it unnecessary. // Done here, it's too frequent. - // ShowQuickPlayIndicator(); + // DrawBothOverlays(); return; } @@ -2504,7 +2563,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) if (evt.Leaving() || (changeInZone && zone != StatusChoice::EnteringQP)) { if (evt.Leaving()) { // Erase the line - HideQuickPlayIndicator(); + DrawBothOverlays(); } SetCursor(mCursorDefault); @@ -2518,7 +2577,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) } else if (evt.Entering() || (changeInZone && zone == StatusChoice::EnteringQP)) { SetCursor(mCursorHand); - HideQuickPlayIndicator(); + DrawBothOverlays(); return; } @@ -2539,14 +2598,11 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) scrubber.MarkScrubStart(evt.m_x, TracksPrefs::GetPinnedHeadPreference(), false); UpdateStatusBarAndTooltips(StatusChoice::EnteringScrubZone); - // repaint_all so that the indicator changes shape. - bool repaint_all = true; - ShowQuickPlayIndicator(repaint_all); + DrawBothOverlays(); return; } else if ( !HasCapture() && inScrubZone) { // mouse going down => we are (probably) seeking - bool repaint_all = false; if (evt.LeftDown()) { //wxLogDebug("down"); scrubber.mInOneShotMode = !scrubber.IsScrubbing(); @@ -2554,11 +2610,8 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) TracksPrefs::GetPinnedHeadPreference(), false); UpdateStatusBarAndTooltips(StatusChoice::EnteringScrubZone); } - else if( changeInZone ) { - repaint_all = true; - } - ShowQuickPlayIndicator(repaint_all); + DrawBothOverlays(); return; } else if ( mQuickPlayEnabled) { @@ -2582,19 +2635,19 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) if( inQPZone ){ HandleQPClick(evt, mousePosX); HandleQPDrag(evt, mousePosX); - ShowQuickPlayIndicator(); + DrawBothOverlays(); } } else if (evt.LeftIsDown() && HasCapture()) { HandleQPDrag(evt, mousePosX); - ShowQuickPlayIndicator(); + DrawBothOverlays(); } else if (evt.LeftUp() && HasCapture()) { HandleQPRelease(evt); - ShowQuickPlayIndicator(); + DrawBothOverlays(); } else // if (!inScrubZone) - ShowQuickPlayIndicator(); + DrawBothOverlays(); } } @@ -2653,8 +2706,6 @@ void AdornedRulerPanel::HandleQPDrag(wxMouseEvent &/*event*/, wxCoord mousePosX) case mesNone: // If close to either end of play region, snap to closest if (isWithinStart || isWithinEnd) { - HideQuickPlayIndicator(); - if (fabs(mQuickPlayPos - mOldPlayRegionStart) < fabs(mQuickPlayPos - mOldPlayRegionEnd)) mQuickPlayPos = mOldPlayRegionStart; else @@ -2662,8 +2713,6 @@ void AdornedRulerPanel::HandleQPDrag(wxMouseEvent &/*event*/, wxCoord mousePosX) } break; case mesDraggingPlayRegionStart: - HideQuickPlayIndicator(); - // Don't start dragging until beyond tolerance initial playback start if (!mIsDragging && isWithinStart) mQuickPlayPos = mOldPlayRegionStart; @@ -2679,15 +2728,11 @@ void AdornedRulerPanel::HandleQPDrag(wxMouseEvent &/*event*/, wxCoord mousePosX) break; case mesDraggingPlayRegionEnd: if (!mIsDragging && isWithinEnd) { - HideQuickPlayIndicator(); - mQuickPlayPos = mOldPlayRegionEnd; } else mIsDragging = true; if (isWithinStart) { - HideQuickPlayIndicator(); - mQuickPlayPos = mOldPlayRegionStart; } mPlayRegionEnd = mQuickPlayPos; @@ -2699,8 +2744,6 @@ void AdornedRulerPanel::HandleQPDrag(wxMouseEvent &/*event*/, wxCoord mousePosX) // Don't start dragging until mouse is beyond tolerance of initial click. if (isWithinClick || mLeftDownClick == -1) { - HideQuickPlayIndicator(); - mQuickPlayPos = mLeftDownClick; mPlayRegionStart = mLeftDownClick; mPlayRegionEnd = mLeftDownClick; @@ -2711,8 +2754,6 @@ void AdornedRulerPanel::HandleQPDrag(wxMouseEvent &/*event*/, wxCoord mousePosX) break; case mesSelectingPlayRegionRange: if (isWithinClick) { - HideQuickPlayIndicator(); - mQuickPlayPos = mLeftDownClick; } @@ -2740,8 +2781,6 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt) else return; - HideQuickPlayIndicator(); - if (mPlayRegionEnd < mPlayRegionStart) { // Swap values to ensure mPlayRegionStart < mPlayRegionEnd double tmp = mPlayRegionStart; @@ -2916,13 +2955,10 @@ void AdornedRulerPanel::SetPanelSize() GetParent()->PostSizeEventToParent(); } -bool AdornedRulerPanel::ShowingScrubControl() const +void AdornedRulerPanel::DrawBothOverlays() { - const auto &scrubber = mProject->GetScrubber(); - return - mMouseEventState == AdornedRulerPanel::mesNone && - (mPrevZone == AdornedRulerPanel::StatusChoice::EnteringScrubZone || - (scrubber.HasMark())); + mProject->GetTrackPanel()->DrawOverlays( false ); + DrawOverlays( false ); } void AdornedRulerPanel::OnContextMenu(wxContextMenuEvent & WXUNUSED(event)) @@ -2965,7 +3001,8 @@ void AdornedRulerPanel::OnTogglePinnedState(wxCommandEvent & /*event*/) void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt)) { - HideQuickPlayIndicator(); + mPrevZone = StatusChoice::Leaving; + DrawBothOverlays(); wxMouseEvent e(wxEVT_LEFT_UP); e.m_x = mLastMouseX; @@ -3182,7 +3219,8 @@ void AdornedRulerPanel::ShowContextMenu( MenuChoice choice, const wxPoint *pPosi } // dismiss and clear Quick-Play indicator - HideQuickPlayIndicator(); + mPrevZone = StatusChoice::Leaving; + DrawBothOverlays(); if (HasCapture()) ReleaseMouse(); @@ -3348,43 +3386,6 @@ void AdornedRulerPanel::DoDrawIndicator } } -QuickPlayIndicatorOverlay *AdornedRulerPanel::GetOverlay() -{ - if (!mOverlay) - mOverlay = std::make_unique(mProject); - - return mOverlay.get(); -} - -void AdornedRulerPanel::ShowQuickPlayIndicator( bool repaint_all) -{ - ShowOrHideQuickPlayIndicator(true, repaint_all); -} - -void AdornedRulerPanel::HideQuickPlayIndicator(bool repaint_all) -{ - ShowOrHideQuickPlayIndicator(false, repaint_all); -} - -// Draws the vertical line and green triangle indicating the Quick Play cursor position. -void AdornedRulerPanel::ShowOrHideQuickPlayIndicator(bool show, bool repaint_all) -{ - double latestEnd = std::max(mTracks->GetEndTime(), mProject->GetSel1()); - if (!show || (mQuickPlayPos >= latestEnd)) { - GetOverlay()->Update(-1); - } - else { - const int x = Time2Pos(mQuickPlayPos); - bool previewScrub = - mPrevZone == StatusChoice::EnteringScrubZone && - !mProject->GetScrubber().IsScrubbing(); - GetOverlay()->Update(x, mIsSnapped, previewScrub); - } - - mProject->GetTrackPanel()->DrawOverlays(repaint_all); - DrawOverlays(repaint_all); -} - void AdornedRulerPanel::SetPlayRegion(double playRegionStart, double playRegionEnd) { @@ -3408,8 +3409,6 @@ void AdornedRulerPanel::ClearPlayRegion() mPlayRegionStart = -1; mPlayRegionEnd = -1; - HideQuickPlayIndicator(); - Refresh(); } diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index ff671090f..53bbb9e71 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -309,9 +309,6 @@ private: DECLARE_EVENT_TABLE() }; -class QuickPlayIndicatorOverlay; -class QuickPlayRulerOverlay; - // This is an Audacity Specific ruler panel. class AUDACITY_DLL_API AdornedRulerPanel final : public OverlayPanel { @@ -358,16 +355,14 @@ public: void RegenerateTooltips(StatusChoice choice); - void ShowQuickPlayIndicator( bool repaint_all=false); - void HideQuickPlayIndicator( bool repaint_all=false); void UpdateQuickPlayPos(wxCoord &mousPosX); bool ShowingScrubRuler() const { return mShowScrubbing; } void OnToggleScrubRuler(/*wxCommandEvent& */); void OnToggleScrubRulerFromMenu(wxCommandEvent& ); void SetPanelSize(); - - bool ShowingScrubControl() const; + + void DrawBothOverlays(); private: @@ -403,8 +398,6 @@ public: static TempAllowFocus TemporarilyAllowFocus(); private: - QuickPlayIndicatorOverlay *GetOverlay(); - void ShowOrHideQuickPlayIndicator(bool show, bool repaint_all=false); void DoDrawPlayRegion(wxDC * dc); enum class MenuChoice { QuickPlay, Scrub }; @@ -484,8 +477,6 @@ private: int mLastMouseX; // Pixel position bool mIsDragging; - std::unique_ptr mOverlay; - StatusChoice mPrevZone { StatusChoice::NoChange }; bool mShowScrubbing { false }; @@ -494,6 +485,12 @@ private: wxWindow *mButtons[3]; bool mNeedButtonUpdate { true }; + + // Cooperating objects + class QuickPlayIndicatorOverlay; + std::unique_ptr mOverlay; + + class QuickPlayRulerOverlay; }; #endif //define __AUDACITY_RULER__