From 7719ccf86461f8f691e280e80176f8fd01260339 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 8998de211..3b6924fd3 100644 --- a/Application/AppController.h +++ b/Application/AppController.h @@ -108,6 +108,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 5ffbff92b..e4eda4201 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -695,6 +695,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]; @@ -882,4 +883,13 @@ static BOOL consentLastEnabled = NO; [[SparkleBridge sharedStandardUpdaterController] checkForUpdates:[[NSApplication sharedApplication] delegate]]; } +- (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 079a00d81..ef856c2f6 100644 --- a/Base.lproj/MainMenu.xib +++ b/Base.lproj/MainMenu.xib @@ -1715,6 +1715,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";