From b88958670891812ec874aa02910fae4b42267315 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Mon, 3 Mar 2025 18:34:40 -0800 Subject: [PATCH] Playback: Implement Selection Follows Playback Option to make selection follow the playback, within the lag of the output buffer, including if Always Stop After Current or Repeat One is enabled. Allows easily queueing up a list of tracks in Always Stop mode, then hitting the Play button again to play the next track. Enabled by default, but always optional to disable. Signed-off-by: Christopher Snowhill --- Application/AppController.h | 2 ++ Application/AppController.m | 10 +++++++ Base.lproj/MainMenu.xib | 6 +++++ Playlist/PlaylistController.m | 49 ++++++++++++++++++++++++++++++++--- en.lproj/MainMenu.strings | 3 +++ es.lproj/MainMenu.strings | 3 +++ 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/Application/AppController.h b/Application/AppController.h index 782b5f280..f3d677f9a 100644 --- a/Application/AppController.h +++ b/Application/AppController.h @@ -103,6 +103,8 @@ - (void)showPathSuggester; + (void)globalShowPathSuggester; +- (void)selectTrack:(id)sender; + - (IBAction)showRubberbandSettings:(id)sender; + (void)globalShowRubberbandSettings; diff --git a/Application/AppController.m b/Application/AppController.m index d630eb4e6..654a8bc41 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -658,6 +658,7 @@ static BOOL consentLastEnabled = NO; [userDefaultsValuesDict setObject:@(44100) forKey:@"synthSampleRate"]; [userDefaultsValuesDict setObject:@NO forKey:@"alwaysStopAfterCurrent"]; + [userDefaultsValuesDict setObject:@YES forKey:@"selectionFollowsPlayback"]; // Register and sync defaults [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict]; @@ -853,4 +854,13 @@ static BOOL consentLastEnabled = NO; [kAppController showRubberbandSettings:kAppController]; } +- (void)selectTrack:(id)sender { + PlaylistEntry *pe = (PlaylistEntry *)sender; + @try { + [playlistView selectRowIndexes:[NSIndexSet indexSetWithIndex:pe.index] byExtendingSelection:NO]; + } + @catch(id anException) { + } +} + @end diff --git a/Base.lproj/MainMenu.xib b/Base.lproj/MainMenu.xib index e3b9f9dc6..86b9fd3d7 100644 --- a/Base.lproj/MainMenu.xib +++ b/Base.lproj/MainMenu.xib @@ -1671,6 +1671,12 @@ + + + + + + diff --git a/Playlist/PlaylistController.m b/Playlist/PlaylistController.m index 1ddb4cfcd..a9f756cc5 100644 --- a/Playlist/PlaylistController.m +++ b/Playlist/PlaylistController.m @@ -1288,11 +1288,20 @@ static void *playlistControllerContext = &playlistControllerContext; } - (PlaylistEntry *)getNextEntry:(PlaylistEntry *)pe ignoreRepeatOne:(BOOL)ignoreRepeatOne { + BOOL selectionFollows = [[NSUserDefaults standardUserDefaults] boolForKey:@"selectionFollowsPlayback"]; + if(!ignoreRepeatOne && [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysStopAfterCurrent"]) { + if(selectionFollows) { + PlaylistEntry *nextPe = [self getNextEntry:pe ignoreRepeatOne:YES]; + [appController selectTrack:nextPe]; + } return nil; } if(!ignoreRepeatOne && [self repeat] == RepeatModeRepeatOne) { + if(selectionFollows) { + [appController selectTrack:pe]; + } return pe; } @@ -1310,11 +1319,19 @@ static void *playlistControllerContext = &playlistControllerContext; [self commitPersistentStore]; + if(selectionFollows) { + [appController selectTrack:pe]; + } + return pe; } if([self shuffle] != ShuffleOff) { - return [self shuffledEntryAtIndex:(pe.shuffleIndex + 1)]; + PlaylistEntry *nextPe = [self shuffledEntryAtIndex:(pe.shuffleIndex + 1)]; + if(selectionFollows && nextPe) { + [appController selectTrack:nextPe]; + } + return nextPe; } else { NSInteger i; @@ -1342,7 +1359,13 @@ static void *playlistControllerContext = &playlistControllerContext; } } - return [self entryAtIndex:i]; + PlaylistEntry *nextPe = [self entryAtIndex:i]; + + if(selectionFollows) { + [appController selectTrack:nextPe]; + } + + return nextPe; } } @@ -1361,16 +1384,29 @@ static void *playlistControllerContext = &playlistControllerContext; } - (PlaylistEntry *)getPrevEntry:(PlaylistEntry *)pe ignoreRepeatOne:(BOOL)ignoreRepeatOne { + BOOL selectionFollows = [[NSUserDefaults standardUserDefaults] boolForKey:@"selectionFollowsPlayback"]; + if(!ignoreRepeatOne && [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysStopAfterCurrent"]) { + if(selectionFollows) { + PlaylistEntry *prevPe = [self getPrevEntry:pe ignoreRepeatOne:YES]; + [appController selectTrack:prevPe]; + } return nil; } if(!ignoreRepeatOne && [self repeat] == RepeatModeRepeatOne) { + if(selectionFollows) { + [appController selectTrack:pe]; + } return pe; } if([self shuffle] != ShuffleOff) { - return [self shuffledEntryAtIndex:(pe.shuffleIndex - 1)]; + PlaylistEntry *prevPe = [self shuffledEntryAtIndex:(pe.shuffleIndex - 1)]; + if(selectionFollows && prevPe) { + [appController selectTrack:prevPe]; + } + return prevPe; } else { NSInteger i; if(pe.index < 0) // Was a current entry, now removed. @@ -1380,7 +1416,12 @@ static void *playlistControllerContext = &playlistControllerContext; i = pe.index - 1; } - return [self entryAtIndex:i]; + PlaylistEntry *prevPe = [self entryAtIndex:i]; + if(selectionFollows) { + [appController selectTrack:prevPe]; + } + + return prevPe; } } diff --git a/en.lproj/MainMenu.strings b/en.lproj/MainMenu.strings index e25767503..b8e3f1387 100644 --- a/en.lproj/MainMenu.strings +++ b/en.lproj/MainMenu.strings @@ -419,6 +419,9 @@ /* Class = "NSMenuItem"; title = "Select Currently Playing"; ObjectID = "1823"; */ "1823.title" = "Select Currently Playing"; +/* Class = "NSMenuItem"; title = "Selection Follows Playback"; ObjectID = "Q7K-Eu-1Vf"; */ +"Q7K-Eu-1Vf.title" = "Selection Follows Playback"; + /* Class = "NSMenuItem"; title = "View"; ObjectID = "1848"; */ "1848.title" = "View"; diff --git a/es.lproj/MainMenu.strings b/es.lproj/MainMenu.strings index 6f1d5b50b..bd00d01b9 100644 --- a/es.lproj/MainMenu.strings +++ b/es.lproj/MainMenu.strings @@ -419,6 +419,9 @@ /* Class = "NSMenuItem"; title = "Select Currently Playing"; ObjectID = "1823"; */ "1823.title" = "Seleccionar reproducción actual"; +/* Class = "NSMenuItem"; title = "Selection Follows Playback"; ObjectID = "Q7K-Eu-1Vf"; */ +"Q7K-Eu-1Vf.title" = "Selección sigue la reproducción"; + /* Class = "NSMenuItem"; title = "View"; ObjectID = "1848"; */ "1848.title" = "Visualización";