From f9dd6b4066217ffc430d9b0984dadcb93668099a Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 8 May 2016 16:54:02 -0400 Subject: [PATCH] Reimplement the play/record indicator in the ruler as an overlay... ... fixing the lag between the green line and the triangle for scrolling play --- src/tracks/ui/PlayIndicatorOverlay.cpp | 131 +++++++++++++++---------- src/tracks/ui/PlayIndicatorOverlay.h | 34 +++++-- src/widgets/Ruler.cpp | 56 +++-------- src/widgets/Ruler.h | 8 +- 4 files changed, 120 insertions(+), 109 deletions(-) diff --git a/src/tracks/ui/PlayIndicatorOverlay.cpp b/src/tracks/ui/PlayIndicatorOverlay.cpp index 8a14bc930..d20c2f835 100644 --- a/src/tracks/ui/PlayIndicatorOverlay.cpp +++ b/src/tracks/ui/PlayIndicatorOverlay.cpp @@ -30,30 +30,26 @@ namespace { { return (m >= l && m < h); } + + enum { IndicatorMediumWidth = 13 }; } -PlayIndicatorOverlay::PlayIndicatorOverlay(AudacityProject *project) - : mProject(project) - , mLastIndicatorX(-1) - , mNewIndicatorX(-1) +PlayIndicatorOverlayBase::PlayIndicatorOverlayBase(AudacityProject *project, bool isMaster) +: mProject(project) +, mIsMaster(isMaster) { - mProject->Connect(EVT_TRACK_PANEL_TIMER, - wxCommandEventHandler(PlayIndicatorOverlay::OnTimer), - NULL, - this); } -PlayIndicatorOverlay::~PlayIndicatorOverlay() +PlayIndicatorOverlayBase::~PlayIndicatorOverlayBase() { - mProject->Disconnect(EVT_TRACK_PANEL_TIMER, - wxCommandEventHandler(PlayIndicatorOverlay::OnTimer), - NULL, - this); } -std::pair PlayIndicatorOverlay::DoGetRectangle(wxSize size) +std::pair PlayIndicatorOverlayBase::DoGetRectangle(wxSize size) { - wxRect rect(mLastIndicatorX, 0, 1, size.GetHeight()); + auto width = mIsMaster ? 1 : IndicatorMediumWidth; + + // May be excessive height, but little matter + wxRect rect(mLastIndicatorX - width / 2, 0, width, size.GetHeight()); return std::make_pair( rect, mLastIndicatorX != mNewIndicatorX @@ -61,56 +57,75 @@ std::pair PlayIndicatorOverlay::DoGetRectangle(wxSize size) } -void PlayIndicatorOverlay::Draw(OverlayPanel &panel, wxDC &dc) +void PlayIndicatorOverlayBase::Draw(OverlayPanel &panel, wxDC &dc) { - TrackPanel &tp = static_cast(panel); - TrackPanelCellIterator begin(&tp, true); - TrackPanelCellIterator end(&tp, false); - + // Set play/record color + bool rec = (gAudioIO->GetNumCaptureChannels() > 0); + AColor::IndicatorColor(&dc, !rec); mLastIndicatorX = mNewIndicatorX; if (!between_incexc(0, mLastIndicatorX, dc.GetSize().GetWidth())) return; - const ZoomInfo &viewInfo = mProject->GetZoomInfo(); - TrackPanel *const trackPanel = mProject->GetTrackPanel(); + if(auto tp = dynamic_cast(&panel)) { + wxASSERT(mIsMaster); - double pos = viewInfo.PositionToTime(mLastIndicatorX, trackPanel->GetLeftOffset()); + TrackPanelCellIterator begin(tp, true); + TrackPanelCellIterator end(tp, false); - // Set play/record color - bool rec = (gAudioIO->GetNumCaptureChannels() > 0); - AColor::IndicatorColor(&dc, !rec); - - mProject->GetRulerPanel()->DrawIndicator(pos, rec); - - // Draw indicator in all visible tracks - for (; begin != end; ++begin) - { - TrackPanelCellIterator::value_type data(*begin); - Track *const pTrack = data.first; - if (!pTrack) - continue; - - // Don't draw the indicator in label tracks - if (pTrack->GetKind() == Track::Label) + // Draw indicator in all visible tracks + for (; begin != end; ++begin) { - continue; - } + TrackPanelCellIterator::value_type data(*begin); + Track *const pTrack = data.first; + if (!pTrack) + continue; - // Draw the NEW indicator in its NEW location - // AColor::Line includes both endpoints so use GetBottom() - const wxRect &rect = data.second; - AColor::Line(dc, - mLastIndicatorX, - rect.GetTop(), - mLastIndicatorX, - rect.GetBottom()); + // Don't draw the indicator in label tracks + if (pTrack->GetKind() == Track::Label) + { + continue; + } + + // Draw the NEW indicator in its NEW location + // AColor::Line includes both endpoints so use GetBottom() + const wxRect &rect = data.second; + AColor::Line(dc, + mLastIndicatorX, + rect.GetTop(), + mLastIndicatorX, + rect.GetBottom()); + } } + else if(auto ruler = dynamic_cast(&panel)) { + wxASSERT(!mIsMaster); + + ruler->DoDrawIndicator(&dc, mLastIndicatorX, !rec, IndicatorMediumWidth, false); + } + else + wxASSERT(false); } -void PlayIndicatorOverlay::Erase(wxDC &dc, wxDC &src) +PlayIndicatorOverlay::PlayIndicatorOverlay(AudacityProject *project) +: PlayIndicatorOverlayBase(project, true) { - Overlay::Erase(dc, src); - mProject->GetRulerPanel()->ClearIndicator(); + mProject->Connect(EVT_TRACK_PANEL_TIMER, + wxCommandEventHandler(PlayIndicatorOverlay::OnTimer), + NULL, + this); +} + +PlayIndicatorOverlay::~PlayIndicatorOverlay() +{ + if (mPartner) { + auto ruler = mProject->GetRulerPanel(); + if(ruler) + ruler->RemoveOverlay(mPartner.get()); + } + + mProject->Disconnect(EVT_TRACK_PANEL_TIMER, + wxCommandEventHandler(PlayIndicatorOverlay::OnTimer), + NULL, + this); } void PlayIndicatorOverlay::OnTimer(wxCommandEvent &event) @@ -118,6 +133,15 @@ void PlayIndicatorOverlay::OnTimer(wxCommandEvent &event) // Let other listeners get the notification event.Skip(); + // Ensure that there is an overlay attached to the ruler + if (!mPartner) { + auto ruler = mProject->GetRulerPanel(); + if (ruler) { + mPartner = std::make_unique(mProject, false); + ruler->AddOverlay(mPartner.get()); + } + } + if (!mProject->IsAudioActive()) { const auto &scrubber = mProject->GetScrubber(); if (scrubber.HasStartedScrubbing()) @@ -158,4 +182,7 @@ void PlayIndicatorOverlay::OnTimer(wxCommandEvent &event) mNewIndicatorX = viewInfo.TimeToPosition(playPos, mProject->GetTrackPanel()->GetLeftOffset()); } + + if(mPartner) + mPartner->Update(mNewIndicatorX); } diff --git a/src/tracks/ui/PlayIndicatorOverlay.h b/src/tracks/ui/PlayIndicatorOverlay.h index 0dab86a2a..30fe749d6 100644 --- a/src/tracks/ui/PlayIndicatorOverlay.h +++ b/src/tracks/ui/PlayIndicatorOverlay.h @@ -12,28 +12,44 @@ Paul Licameli split from TrackPanel.cpp #define __AUDACITY_PLAY_INDICATOR_OVERLAY__ #include +#include "../../MemoryX.h" #include "../../widgets/Overlay.h" class AudacityProject; -class PlayIndicatorOverlay final : public wxEvtHandler, public Overlay +// Common class for overlaying track panel or ruler +class PlayIndicatorOverlayBase : public wxEvtHandler, public Overlay +{ +public: + PlayIndicatorOverlayBase(AudacityProject *project, bool isMaster); + virtual ~PlayIndicatorOverlayBase(); + + void Update(int newIndicatorX) { mNewIndicatorX = newIndicatorX; } + +private: + std::pair DoGetRectangle(wxSize size) override; + void Draw(OverlayPanel &panel, wxDC &dc) override; + +protected: + + AudacityProject *const mProject; + const bool mIsMaster; + int mLastIndicatorX { -1 }; + int mNewIndicatorX { -1 }; +}; + +// Master object for track panel, creates the other object for the ruler +class PlayIndicatorOverlay final : public PlayIndicatorOverlayBase { public: PlayIndicatorOverlay(AudacityProject *project); virtual ~PlayIndicatorOverlay(); private: - std::pair DoGetRectangle(wxSize size) override; - void Draw(OverlayPanel &panel, wxDC &dc) override; - void Erase(wxDC &dc, wxDC &src) override; - void OnTimer(wxCommandEvent &event); - - AudacityProject *mProject; - int mLastIndicatorX; - int mNewIndicatorX; + std::unique_ptr mPartner; }; #endif diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index 0068ef64a..121454e59 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -1813,9 +1813,10 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent, mLeftOffset = 0; mIndTime = -1; - mIndType = -1; + mQuickPlayInd = false; mLastQuickPlayX = -1; + mPlayRegionStart = -1; mPlayRegionLock = false; mPlayRegionEnd = -1; @@ -2131,12 +2132,6 @@ void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt)) DoDrawMarks(&backDC, true); - if (mIndType >= 0) - { - const bool scrub = mProject->GetScrubber().HasStartedScrubbing(); - DoDrawIndicator(&backDC, mIndTime, mIndType != 0, IndicatorMediumWidth, false); - } - DoDrawPlayRegion(&backDC); DoDrawPushbuttons(&backDC); @@ -3466,39 +3461,12 @@ void AdornedRulerPanel::SetLeftOffset(int offset) mRuler.SetUseZoomInfo(offset, mViewInfo); } -// -//This draws the little triangular indicator on the -//AdornedRulerPanel. -// -void AdornedRulerPanel::ClearIndicator() -{ - mIndType = -1; - - Refresh(); -} - -void AdornedRulerPanel::DrawIndicator( double time, bool rec ) -{ - mIndTime = time; - - if (mIndTime < 0) - { - ClearIndicator(); - return; - } - - mIndType = ( rec ? 0 : 1 ); - - Refresh(); -} - // Draws the play/recording position indicator. void AdornedRulerPanel::DoDrawIndicator - (wxDC * dc, double time, bool playing, int width, bool scrub) + (wxDC * dc, wxCoord xx, bool playing, int width, bool scrub) { ADCChanger changer(dc); // Undo pen and brush changes at function exit - const int x = Time2Pos(time); AColor::IndicatorColor( dc, playing ); wxPoint tri[ 3 ]; @@ -3510,26 +3478,26 @@ void AdornedRulerPanel::DoDrawIndicator auto yy = mShowScrubbing ? mScrubZone.y : (mInner.GetBottom() + 1) - 1 /* bevel */ - height; - tri[ 0 ].x = x - IndicatorOffset; + tri[ 0 ].x = xx - IndicatorOffset; tri[ 0 ].y = yy; - tri[ 1 ].x = x - IndicatorOffset; + tri[ 1 ].x = xx - IndicatorOffset; tri[ 1 ].y = yy + height; - tri[ 2 ].x = x - IndicatorHalfWidth; + tri[ 2 ].x = xx - IndicatorHalfWidth; tri[ 2 ].y = yy + height / 2; dc->DrawPolygon( 3, tri ); - tri[ 0 ].x = tri[ 1 ].x = x + IndicatorOffset; - tri[ 2 ].x = x + IndicatorHalfWidth; + tri[ 0 ].x = tri[ 1 ].x = xx + IndicatorOffset; + tri[ 2 ].x = xx + IndicatorHalfWidth; dc->DrawPolygon( 3, tri ); } else { // Down pointing triangle auto height = IndicatorHeightForWidth(width); const int IndicatorHalfWidth = width / 2; - tri[ 0 ].x = x - IndicatorHalfWidth; + tri[ 0 ].x = xx - IndicatorHalfWidth; tri[ 0 ].y = mInner.y; - tri[ 1 ].x = x + IndicatorHalfWidth; + tri[ 1 ].x = xx + IndicatorHalfWidth; tri[ 1 ].y = mInner.y; - tri[ 2 ].x = x; + tri[ 2 ].x = xx; tri[ 2 ].y = mInner.y + height; dc->DrawPolygon( 3, tri ); } @@ -3591,7 +3559,7 @@ void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc, bool repainting) auto scrub = mPrevZone == StatusChoice::EnteringScrubZone || mProject->GetScrubber().HasStartedScrubbing(); auto width = scrub ? IndicatorBigWidth() : IndicatorSmallWidth; - DoDrawIndicator(dc, mQuickPlayPos, true, width, scrub); + DoDrawIndicator(dc, Time2Pos(mQuickPlayPos), true, width, scrub); } void AdornedRulerPanel::SetPlayRegion(double playRegionStart, diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index f65185a03..a07edd821 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -299,9 +299,7 @@ public: void SetLeftOffset(int offset); - void DrawIndicator(double time, bool rec); void DrawSelection(); - void ClearIndicator(); void SetPlayRegion(double playRegionStart, double playRegionEnd); void ClearPlayRegion(); @@ -380,7 +378,10 @@ private: void DoDrawEdge(wxDC *dc); void DoDrawMarks(wxDC * dc, bool /*text */ ); void DoDrawSelection(wxDC * dc); - void DoDrawIndicator(wxDC * dc, double time, bool playing, int width, bool scrub); +public: + void DoDrawIndicator(wxDC * dc, wxCoord xx, bool playing, int width, bool scrub); + +private: void DoEraseIndicator(wxDC *dc, int x); QuickPlayIndicatorOverlay *GetOverlay(); void DrawQuickPlayIndicator(wxDC * dc /*NULL to DELETE old only*/, bool repainting = false); @@ -440,7 +441,6 @@ private: int mLeftOffset; // Number of pixels before we hit the 'zero position'. - int mIndType; // -1 = No indicator, 0 = Record, 1 = Play double mIndTime; bool mQuickPlayInd; double mQuickPlayPos;