diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 23a5f2efa..ff598a680 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -825,6 +825,8 @@ list( APPEND SOURCES tracks/playabletrack/wavetrack/ui/SpectrumVZoomHandle.h tracks/playabletrack/wavetrack/ui/SpectrumView.cpp tracks/playabletrack/wavetrack/ui/SpectrumView.h + tracks/playabletrack/wavetrack/ui/WaveTrackAffordanceControls.cpp + tracks/playabletrack/wavetrack/ui/WaveTrackAffordanceControls.h tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp tracks/playabletrack/wavetrack/ui/WaveTrackControls.h tracks/playabletrack/wavetrack/ui/WaveTrackShifter.cpp diff --git a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp index 8b29d9c99..0cc5adea5 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp @@ -172,7 +172,8 @@ ChooseColorSet( float bin0, float bin1, float selBinLo, void DrawClipSpectrum(TrackPanelDrawingContext &context, WaveTrackCache &waveTrackCache, const WaveClip *clip, - const wxRect & rect) + const wxRect & rect, + bool selected) { auto &dc = context.dc; const auto artist = TrackArtist::Get( context ); @@ -608,13 +609,19 @@ void DrawClipSpectrum(TrackPanelDrawingContext &context, // Draw clip edges, as also in waveform view, which improves the appearance // of split views - params.DrawClipEdges( dc, rect ); + { + //increase virtual view size by px to hide edges that should not be visible + auto clipRect = ClipParameters::GetClipRect(*clip, zoomInfo, rect.Inflate(1, 0), 1); + if (!clipRect.IsEmpty()) + TrackArt::DrawClipEdges(dc, clipRect, selected); + } } } -void SpectrumView::DoDraw( TrackPanelDrawingContext &context, - const WaveTrack *track, +void SpectrumView::DoDraw(TrackPanelDrawingContext& context, + const WaveTrack* track, + const WaveClip* selectedClip, const wxRect & rect ) { const auto artist = TrackArtist::Get( context ); @@ -625,7 +632,7 @@ void SpectrumView::DoDraw( TrackPanelDrawingContext &context, WaveTrackCache cache(track->SharedPointer()); for (const auto &clip: track->GetClips()) - DrawClipSpectrum( context, cache, clip.get(), rect ); + DrawClipSpectrum( context, cache, clip.get(), rect, clip.get() == selectedClip ); DrawBoldBoundaries( context, track, rect ); } @@ -653,14 +660,18 @@ void SpectrumView::Draw( wxAntialiasMode aamode = dc.GetGraphicsContext()->GetAntialiasMode(); dc.GetGraphicsContext()->SetAntialiasMode(wxANTIALIAS_NONE); #endif + + auto waveTrackView = GetWaveTrackView().lock(); + wxASSERT(waveTrackView.use_count()); - DoDraw( context, wt.get(), rect ); + auto seletedClip = waveTrackView->GetSelectedClip().lock(); + DoDraw( context, wt.get(), seletedClip.get(), rect ); #if defined(__WXMAC__) dc.GetGraphicsContext()->SetAntialiasMode(aamode); #endif } - CommonTrackView::Draw( context, rect, iPass ); + WaveTrackSubView::Draw( context, rect, iPass ); } static const WaveTrackSubViews::RegisteredFactory key{ diff --git a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.h b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.h index 787370e2e..97b930ff2 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.h +++ b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.h @@ -38,6 +38,7 @@ private: static void DoDraw( TrackPanelDrawingContext &context, const WaveTrack *track, + const WaveClip* selectedClip, const wxRect & rect ); std::vector DetailedHitTest( diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackAffordanceControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackAffordanceControls.cpp new file mode 100644 index 000000000..8f846f6fe --- /dev/null +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackAffordanceControls.cpp @@ -0,0 +1,105 @@ +/*!******************************************************************** +* + Audacity: A Digital Audio Editor + + WaveTrackAffordanceControls.cpp + + Vitaly Sverchinsky + + **********************************************************************/ + +#include "WaveTrackAffordanceControls.h" + +#include + +#include "../../../../AllThemeResources.h" +#include "../../../../TrackPanelMouseEvent.h" +#include "../../../../TrackArtist.h" +#include "../../../../TrackPanelDrawingContext.h" +#include "../../../../ViewInfo.h" +#include "../../../../WaveTrack.h" +#include "../../../../WaveClip.h" +#include "../../../ui/AffordanceHandle.h" +#include "WaveTrackView.h"//need only ClipParameters + + +WaveTrackAffordanceControls::WaveTrackAffordanceControls(const std::shared_ptr& pTrack) + : CommonTrackCell(pTrack) +{ +} + +std::vector WaveTrackAffordanceControls::HitTest(const TrackPanelMouseState& state, const AudacityProject* pProject) +{ + mFocusClip.reset(); + + std::vector results; + + const auto track = FindTrack(); + + const auto waveTrack = std::static_pointer_cast(track->SubstitutePendingChangedTrack()); + + const auto rect = state.rect; + + auto px = state.state.m_x; + auto py = state.state.m_y; + + auto& zoomInfo = ViewInfo::Get(*pProject); + for (const auto& clip : waveTrack->GetClips()) + { + auto affordanceRect = ClipParameters::GetClipRect(*clip.get(), zoomInfo, rect); + + if (affordanceRect.Contains(px, py)) + { + results.push_back(AffordanceHandle::HitAnywhere(mAffordanceHandle, track)); + mFocusClip = clip; + break; + } + } + + return results; +} + +void WaveTrackAffordanceControls::Draw(TrackPanelDrawingContext& context, const wxRect& rect, unsigned iPass) +{ + if (iPass == TrackArtist::PassBackground) { + auto track = FindTrack(); + const auto artist = TrackArtist::Get(context); + + TrackArt::DrawBackgroundWithSelection(context, rect, track.get(), artist->blankSelectedBrush, artist->blankBrush); + + const auto waveTrack = std::static_pointer_cast(track->SubstitutePendingChangedTrack()); + const auto& zoomInfo = *artist->pZoomInfo; + + context.dc.SetClippingRegion(rect); + + auto px = context.lastState.m_x; + auto py = context.lastState.m_y; + + for (const auto& clip : waveTrack->GetClips()) + { + auto affordanceRect = ClipParameters::GetClipRect( + *clip.get(), + zoomInfo, + rect.Inflate(TrackArt::ClipFrameRadius, 0), + TrackArt::ClipFrameRadius + ); + if (affordanceRect.IsEmpty()) + continue; + + auto selected = GetSelectedClip().lock() == clip; + auto highlight = selected || affordanceRect.Contains(px, py); + TrackArt::DrawClipAffordance(context.dc, affordanceRect, highlight, selected); + + } + context.dc.DestroyClippingRegion(); + } +} + +std::weak_ptr WaveTrackAffordanceControls::GetSelectedClip() const +{ + if (auto handle = mAffordanceHandle.lock()) + { + return handle->Clicked() ? mFocusClip : std::weak_ptr(); + } + return {}; +} diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackAffordanceControls.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackAffordanceControls.h new file mode 100644 index 000000000..b77cb60f2 --- /dev/null +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackAffordanceControls.h @@ -0,0 +1,30 @@ +/*!******************************************************************** +* + Audacity: A Digital Audio Editor + + WaveTrackAffordanceControls.h + + Vitaly Sverchinsky + + **********************************************************************/ + +#pragma once + +#include "../../../ui/CommonTrackPanelCell.h" + +class AffordanceHandle; +class WaveClip; + +class AUDACITY_DLL_API WaveTrackAffordanceControls : public CommonTrackCell +{ + std::weak_ptr mFocusClip; + std::weak_ptr mAffordanceHandle; +public: + WaveTrackAffordanceControls(const std::shared_ptr& pTrack); + + std::vector HitTest(const TrackPanelMouseState& state, const AudacityProject* pProject) override; + + void Draw(TrackPanelDrawingContext& context, const wxRect& rect, unsigned iPass) override; + + std::weak_ptr GetSelectedClip() const; +}; diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp index fb91fd388..e78518aae 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp @@ -38,8 +38,11 @@ Paul Licameli split from TrackPanel.cpp #include "../../../ui/ButtonHandle.h" #include "../../../../TrackInfo.h" +#include "WaveTrackAffordanceControls.h" + namespace { + using WaveTrackSubViewPtrs = std::vector< std::shared_ptr< WaveTrackSubView > >; // Structure that collects and modifies information on sub-view positions @@ -760,6 +763,11 @@ void WaveTrackSubView::DrawBoldBoundaries( } } +std::weak_ptr WaveTrackSubView::GetWaveTrackView() const +{ + return mwWaveTrackView; +} + WaveTrackView &WaveTrackView::Get( WaveTrack &track ) { return static_cast< WaveTrackView& >( TrackView::Get( track ) ); @@ -1018,6 +1026,11 @@ WaveTrackView::GetAllSubViews() return results; } +std::shared_ptr WaveTrackView::DoGetAffordanceControls() +{ + return std::make_shared(FindTrack()); +} + void WaveTrackView::DoSetMinimized( bool minimized ) { BuildSubViews(); @@ -1229,20 +1242,23 @@ ClipParameters::ClipParameters } } -void ClipParameters::DrawClipEdges( wxDC &dc, const wxRect &rect ) const +wxRect ClipParameters::GetClipRect(const WaveClip& clip, const ZoomInfo& zoomInfo, const wxRect& viewRect, int clipOffsetX) { - // Draw clip edges - dc.SetPen(*wxGREY_PEN); - if (tpre < 0) { - AColor::Line(dc, - mid.x - 1, mid.y, - mid.x - 1, mid.y + rect.height); - } - if (tpost > t1) { - AColor::Line(dc, - mid.x + mid.width, mid.y, - mid.x + mid.width, mid.y + rect.height); - } + auto srs = 1. / static_cast(clip.GetRate()); + //to prevent overlap left and right most samples with frame border + auto margin = .25 * srs; + auto edgeLeft = static_cast(viewRect.GetLeft()); + auto edgeRight = static_cast(viewRect.GetRight()); + auto left = std::clamp(zoomInfo.TimeToPosition(clip.GetOffset() - margin, viewRect.x + clipOffsetX, true), edgeLeft, edgeRight); + auto right = std::clamp(zoomInfo.TimeToPosition(clip.GetEndTime() - srs + margin, viewRect.x + clipOffsetX, true), edgeLeft, edgeRight); + if (right - left > 0) + { + //after clamping we can expect that left and right + //are small enough to be put into int + return wxRect(static_cast(left), viewRect.y, static_cast(right - left), viewRect.height); + } + //off the screen + return wxRect(); } void WaveTrackView::Reparent( const std::shared_ptr &parent ) @@ -1252,6 +1268,17 @@ void WaveTrackView::Reparent( const std::shared_ptr &parent ) WaveTrackSubViews::ForEach( [&parent](WaveTrackSubView &subView){ subView.Reparent( parent ); } ); + if (mpAffordanceCellControl) + mpAffordanceCellControl->Reparent(parent); +} + +std::weak_ptr WaveTrackView::GetSelectedClip() +{ + if (auto affordance = std::dynamic_pointer_cast(GetAffordanceControls())) + { + return affordance->GetSelectedClip(); + } + return {}; } void WaveTrackView::BuildSubViews() const diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h index f12efdb69..e0dafcd3e 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h @@ -21,6 +21,7 @@ class CutlineHandle; class TranslatableString; class WaveTrack; class WaveTrackView; +class WaveClip; class AUDACITY_DLL_API WaveTrackSubView : public CommonTrackView { @@ -47,6 +48,8 @@ protected: TrackPanelDrawingContext &context, const WaveTrack *track, const wxRect &rect ); + std::weak_ptr GetWaveTrackView() const; + private: std::weak_ptr mCloseHandle; std::weak_ptr mAdjustHandle; @@ -122,6 +125,9 @@ public: bool GetMultiView() const { return mMultiView; } void SetMultiView( bool value ) { mMultiView = value; } + + std::weak_ptr GetSelectedClip(); + private: void BuildSubViews() const; void DoSetDisplay(Display display, bool exclusive = true); @@ -141,6 +147,8 @@ private: Refinement GetSubViews( const wxRect &rect ) override; protected: + std::shared_ptr DoGetAffordanceControls() override; + void DoSetMinimized( bool minimized ) override; // Placements are in correspondence with the array of sub-views @@ -188,7 +196,9 @@ struct AUDACITY_DLL_API ClipParameters wxRect mid; int leftOffset; - void DrawClipEdges( wxDC &dc, const wxRect &rect ) const; + // returns a clip rectangle restricted by viewRect, + // and with clipOffsetX - clip horizontal origin offset within view rect + static wxRect GetClipRect(const WaveClip& clip, const ZoomInfo& zoomInfo, const wxRect& viewRect, int clipOffsetX = 0); }; #endif diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp index 68c0c0063..15e77a479 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp @@ -670,7 +670,8 @@ void DrawClipWaveform(TrackPanelDrawingContext &context, const WaveClip *clip, const wxRect & rect, bool dB, - bool muted) + bool muted, + bool selected) { auto &dc = context.dc; const auto artist = TrackArtist::Get( context ); @@ -900,7 +901,12 @@ void DrawClipWaveform(TrackPanelDrawingContext &context, if (h == 0.0 && tOffset < 0.0) { TrackArt::DrawNegativeOffsetTrackArrows( context, rect ); } - params.DrawClipEdges( dc, rect ); + { + //increase virtual view size by px to hide edges that should not be visible + auto clipRect = ClipParameters::GetClipRect(*clip, zoomInfo, rect.Inflate(1, 0), 1); + if (!clipRect.IsEmpty()) + TrackArt::DrawClipEdges(dc, clipRect, selected); + } } void DrawTimeSlider( TrackPanelDrawingContext &context, @@ -967,7 +973,8 @@ void DrawTimeSlider( TrackPanelDrawingContext &context, //#include "tracks/ui/TimeShiftHandle.h" void WaveformView::DoDraw(TrackPanelDrawingContext &context, const WaveTrack *track, - const wxRect & rect, + const WaveClip* selectedClip, + const wxRect& rect, bool muted) { auto &dc = context.dc; @@ -988,10 +995,11 @@ void WaveformView::DoDraw(TrackPanelDrawingContext &context, TrackArt::DrawBackgroundWithSelection( context, rect, track, blankSelectedBrush, blankBrush ); - for (const auto &clip: track->GetClips()) + for (const auto& clip : track->GetClips()) + { DrawClipWaveform(context, track, clip.get(), rect, - dB, muted); - + dB, muted, clip.get() == selectedClip); + } DrawBoldBoundaries( context, track, rect ); const auto drawSliders = artist->drawSliders; @@ -1028,13 +1036,17 @@ void WaveformView::Draw( dc.GetGraphicsContext()->SetAntialiasMode(wxANTIALIAS_NONE); #endif - DoDraw(context, wt.get(), rect, muted); + auto waveTrackView = GetWaveTrackView().lock(); + wxASSERT(waveTrackView.use_count()); + + auto selectedClip = waveTrackView->GetSelectedClip().lock(); + DoDraw(context, wt.get(), selectedClip.get(), rect, muted); #if defined(__WXMAC__) dc.GetGraphicsContext()->SetAntialiasMode(aamode); #endif } - CommonTrackView::Draw( context, rect, iPass ); + WaveTrackSubView::Draw( context, rect, iPass ); } static const WaveTrackSubViews::RegisteredFactory key{ diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveformView.h b/src/tracks/playabletrack/wavetrack/ui/WaveformView.h index 072fa2901..492c68186 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveformView.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveformView.h @@ -38,6 +38,7 @@ private: const wxRect &rect, unsigned iPass ) override; static void DoDraw(TrackPanelDrawingContext &context, const WaveTrack *track, + const WaveClip* selectedClip, const wxRect & rect, bool muted);