From eb22892064b5f5b5aa7c8d26842428240504d663 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 11 Sep 2020 11:44:55 -0400 Subject: [PATCH] TrackShifters decide what parts move or stay fixed... ... for now redundantly with the older logic. Also shorten a function name to Init --- src/menus/ClipMenus.cpp | 2 +- .../notetrack/ui/NoteTrackView.cpp | 9 +- .../wavetrack/ui/WaveTrackView.cpp | 15 +++ src/tracks/ui/TimeShiftHandle.cpp | 94 ++++++++++++++++++- src/tracks/ui/TimeShiftHandle.h | 18 +++- 5 files changed, 130 insertions(+), 8 deletions(-) diff --git a/src/menus/ClipMenus.cpp b/src/menus/ClipMenus.cpp index b38107e4e..b37d30cb5 100644 --- a/src/menus/ClipMenus.cpp +++ b/src/menus/ClipMenus.cpp @@ -654,7 +654,7 @@ double DoClipMove track->GetSelected() && !selectedRegion.isPoint(); state.trackExclusions.clear(); - TimeShiftHandle::CreateListOfCapturedClips( + TimeShiftHandle::Init( state, viewInfo, *track, trackList, syncLocked, t0 ); auto desiredT0 = viewInfo.OffsetTimeByPixels( t0, ( right ? 1 : -1 ) ); diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackView.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackView.cpp index 74ad9d3d3..3af61d5e5 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackView.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackView.cpp @@ -741,12 +741,19 @@ public: } ~NoteTrackShifter() override {} Track &GetTrack() const override { return *mpTrack; } - + HitTestResult HitTest( double ) override { return HitTestResult::Intervals; } + void SelectInterval( const TrackInterval &interval ) override + { + CommonSelectInterval( interval ); + } + + bool SyncLocks() override { return true; } + private: std::shared_ptr mpTrack; }; diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp index b00cb9c5b..059a3124b 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp @@ -1329,6 +1329,21 @@ public: return HitTestResult::Intervals; } + void SelectInterval( const TrackInterval &interval ) override + { + UnfixIntervals( [&](auto &myInterval){ + // Use a slightly different test from CommonSelectInterval, rounding times + // to exact samples according to the clip's rate + auto data = + static_cast( myInterval.Extra() ); + auto clip = data->GetClip().get(); + return !(clip->IsClipStartAfterClip(interval.Start()) || + clip->BeforeClip(interval.End())); + }); + } + + bool SyncLocks() override { return true; } + private: std::shared_ptr mpTrack; }; diff --git a/src/tracks/ui/TimeShiftHandle.cpp b/src/tracks/ui/TimeShiftHandle.cpp index a53324a5c..610d0f556 100644 --- a/src/tracks/ui/TimeShiftHandle.cpp +++ b/src/tracks/ui/TimeShiftHandle.cpp @@ -265,6 +265,19 @@ void TrackShifter::UnfixAll() mFixed = Intervals{}; } +void TrackShifter::SelectInterval( const TrackInterval & ) +{ + UnfixAll(); +} + +void TrackShifter::CommonSelectInterval(const TrackInterval &interval) +{ + UnfixIntervals( [&](auto &myInterval){ + return !(interval.End() < myInterval.Start() || + myInterval.End() < interval.Start()); + }); +} + void TrackShifter::InitIntervals() { mMoving.clear(); @@ -284,15 +297,21 @@ auto CoarseTrackShifter::HitTest( double ) -> HitTestResult return HitTestResult::Track; } +bool CoarseTrackShifter::SyncLocks() +{ + return false; +} + template<> auto MakeTrackShifter::Implementation() -> Function { return [](Track &track) { return std::make_unique(track); }; } -void TimeShiftHandle::CreateListOfCapturedClips +void TimeShiftHandle::Init ( ClipMoveState &state, const ViewInfo &viewInfo, Track &capturedTrack, - TrackList &trackList, bool syncLocked, double clickTime ) + TrackList &trackList, bool syncLocked, double clickTime, + bool capturedAClip ) { // The captured clip is the focus, but we need to create a list // of all clips that have to move, also... @@ -302,9 +321,11 @@ void TimeShiftHandle::CreateListOfCapturedClips // First, if click was in selection, capture selected clips; otherwise // just the clicked-on clip if ( state.capturedClipIsSelection ) + // All selected tracks may move some intervals for (auto t : trackList.Selected()) AddClipsToCaptured( state, viewInfo, t ); else { + // Move intervals only of the chosen channel group state.capturedClipArray.push_back (TrackClip( &capturedTrack, state.capturedClip )); @@ -320,6 +341,7 @@ void TimeShiftHandle::CreateListOfCapturedClips // Now, if sync-lock is enabled, capture any clip that's linked to a // captured clip. if ( syncLocked ) { + // Sync lock propagation of unfixing of intervals // AWD: capturedClipArray expands as the loop runs, so newly-added // clips are considered (the effect is like recursion and terminates // because AddClipsToCaptured doesn't add duplicate clips); to remove @@ -351,6 +373,70 @@ void TimeShiftHandle::CreateListOfCapturedClips #endif } } + + // Analogy of the steps above, but with TrackShifters, follows below + + if ( state.capturedClipIsSelection ) { + // All selected tracks may move some intervals + const TrackInterval interval{ + viewInfo.selectedRegion.t0(), + viewInfo.selectedRegion.t1() + }; + for ( const auto &pair : state.shifters ) { + auto &shifter = *pair.second; + auto &track = shifter.GetTrack(); + if ( track.IsSelected() ) + shifter.SelectInterval( interval ); + } + } + else { + // Move intervals only of the chosen channel group + for ( auto channel : TrackList::Channels( &capturedTrack ) ) { + auto &shifter = *state.shifters[channel]; + if ( capturedAClip ) { + if ( channel != &capturedTrack ) + shifter.SelectInterval(TrackInterval{clickTime, clickTime}); + } + else + shifter.UnfixAll(); + } + } + + // Sync lock propagation of unfixing of intervals + if ( syncLocked ) { + bool change = true; + while( change ) { + change = false; + + // Iterate over all unfixed intervals in all shifters + // that do propagation... + for ( auto &pair : state.shifters ) { + auto &shifter = *pair.second.get(); + if (!shifter.SyncLocks()) + continue; + auto &track = shifter.GetTrack(); + auto &intervals = shifter.MovingIntervals(); + for (auto &interval : intervals) { + + // ...and tell all other tracks to select that interval... + for ( auto &pair2 : state.shifters ) { + auto &shifter2 = *pair2.second.get(); + if (&shifter2.GetTrack() == &track) + continue; + auto size = shifter2.MovingIntervals().size(); + shifter2.SelectInterval( interval ); + change = change || + (shifter2.SyncLocks() && + size != shifter2.MovingIntervals().size()); + } + + } + } + + // ... and repeat if any other interval became unfixed in a + // shifter that propagates + } + } } void TimeShiftHandle::DoSlideHorizontal @@ -542,9 +628,9 @@ UIHandle::Result TimeShiftHandle::Click pShifter = MakeTrackShifter::Call( *track ); } - CreateListOfCapturedClips( + Init( mClipMoveState, viewInfo, *pTrack, trackList, - ProjectSettings::Get( *pProject ).IsSyncLocked(), clickTime ); + ProjectSettings::Get( *pProject ).IsSyncLocked(), clickTime, capturedAClip ); } mSlideUpDownOnly = event.CmdDown() && !multiToolModeActive; diff --git a/src/tracks/ui/TimeShiftHandle.h b/src/tracks/ui/TimeShiftHandle.h index 7aa942996..20bd90b38 100644 --- a/src/tracks/ui/TimeShiftHandle.h +++ b/src/tracks/ui/TimeShiftHandle.h @@ -58,7 +58,17 @@ public: //! Change all intervals from fixed to moving void UnfixAll(); + //! Notifies the shifter that a region is selected, so it may update its fixed and moving intervals + /*! Default behavior: if any part of the track is selected, unfix all parts of it. */ + virtual void SelectInterval( const TrackInterval &interval ); + + //! Whether unfixing of an interval should propagate to all overlapping intervals in the sync lock group + virtual bool SyncLocks() = 0; + protected: + /*! Unfix any of the intervals that intersect the given one; may be useful to override `SelectInterval()` */ + void CommonSelectInterval( const TrackInterval &interval ); + //! Derived class constructor can initialize all intervals reported by the track as fixed, none moving /*! This can't be called by the base class constructor, when GetTrack() isn't yet callable */ void InitIntervals(); @@ -76,6 +86,9 @@ public: HitTestResult HitTest( double ) override; + //! Returns false + bool SyncLocks() override; + private: std::shared_ptr mpTrack; }; @@ -150,9 +163,10 @@ public: std::shared_ptr GetTrack() const { return mCapturedTrack; } // A utility function also used by menu commands - static void CreateListOfCapturedClips + static void Init ( ClipMoveState &state, const ViewInfo &viewInfo, Track &capturedTrack, - TrackList &trackList, bool syncLocked, double clickTime ); + TrackList &trackList, bool syncLocked, double clickTime, + bool capturedAClip = true ); // A utility function also used by menu commands static void DoSlideHorizontal