From a85c65e833f8be4651efcc9d8f1737e248afbeb8 Mon Sep 17 00:00:00 2001 From: Brian Bicknell Date: Sun, 26 Jul 2026 23:44:06 -0400 Subject: [PATCH] Removed "PlaylistSongs" field, in order to maintain one source of truth (Playlist.Songs). No not allow reordering when a filter is present on the playlist. --- .../ViewModels/PlaylistDetailViewModel.cs | 107 +++++++----------- Harmonia.WinUI/Views/PlaylistDetailView.xaml | 6 +- 2 files changed, 44 insertions(+), 69 deletions(-) diff --git a/Harmonia.WinUI/ViewModels/PlaylistDetailViewModel.cs b/Harmonia.WinUI/ViewModels/PlaylistDetailViewModel.cs index b1aa457..6d02965 100644 --- a/Harmonia.WinUI/ViewModels/PlaylistDetailViewModel.cs +++ b/Harmonia.WinUI/ViewModels/PlaylistDetailViewModel.cs @@ -56,19 +56,6 @@ public partial class PlaylistDetailViewModel : ViewModelBase } } - private ObservableCollection _playlistSongs = []; - public ObservableCollection PlaylistSongs - { - get - { - return _playlistSongs; - } - set - { - SetProperty(ref _playlistSongs, value); - } - } - private string? _filter; public string? Filter { @@ -79,10 +66,13 @@ public partial class PlaylistDetailViewModel : ViewModelBase set { SetProperty(ref _filter, value); + OnPropertyChanged(nameof(CanReorderSongs)); RestartFilterTimer(); } } + public bool CanReorderSongs => string.IsNullOrWhiteSpace(Filter); + private ObservableCollection _filteredPlaylistSongs = []; public ObservableCollection FilteredPlaylistSongs { @@ -123,6 +113,8 @@ public partial class PlaylistDetailViewModel : ViewModelBase public bool IsUserUpdating { get; set; } private bool _isUserInitiatingSongChange; + private PlaylistSong? _pendingMovedSong; + private int _pendingMovedSongIndex = -1; public event EventHandler? PlayingSongChangedAutomatically; @@ -152,10 +144,7 @@ public partial class PlaylistDetailViewModel : ViewModelBase FilteredPlaylistSongs.CollectionChanged += OnFilteredPlaylistSongsCollectionChanged; Playlist = _playlistManager.CurrentPlaylist; // Testing - UpdatePlaylistSongs(Playlist); - - // Testing - //Task.Run(() => PlayDemoSong(playlistRepository)); + UpdateFilteredSongs(); } private void OnFilteredPlaylistSongsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) @@ -163,47 +152,42 @@ public partial class PlaylistDetailViewModel : ViewModelBase if (IsUserUpdating == false) return; - int x = 1; + // A drag-reorder in the ListView raises a Remove followed by an Add for the same item. + // Reordering is only enabled while unfiltered, so view indices map 1:1 to Playlist.Songs. + switch (e.Action) + { + case NotifyCollectionChangedAction.Remove: + _pendingMovedSong = e.OldItems?.Cast().FirstOrDefault(); + _pendingMovedSongIndex = e.OldStartingIndex; + break; + case NotifyCollectionChangedAction.Add: + PlaylistSong? addedSong = e.NewItems?.Cast().FirstOrDefault(); + + if (addedSong != null && addedSong == _pendingMovedSong) + { + MoveSongInPlaylist(_pendingMovedSongIndex, e.NewStartingIndex); + } + + _pendingMovedSong = null; + _pendingMovedSongIndex = -1; + break; + } } - //private async Task PlayDemoSong(IPlaylistRepository playlistRepository) - //{ - // if (playlistRepository.Get().Count == 0) - // { - // playlistRepository.AddPlaylist(); - // } - - // Playlist playlist = playlistRepository.Get().First(); - - // if (playlist.Songs.Count > 0) - // await _audioPlayer.LoadAsync(playlist.Songs[0], PlaybackMode.LoadOnly); - //} - - private void UpdatePlaylistSongs(Playlist? playlist) + private void MoveSongInPlaylist(int oldIndex, int newIndex) { - PlaylistSong[] playlistSongs = playlist?.Songs.ToArray() ?? []; + if (Playlist == null || oldIndex < 0 || oldIndex == newIndex) + return; - PlaylistSongs = [.. playlistSongs]; - UpdateFilteredSongs(); + Playlist.MoveSong(oldIndex, newIndex); } private void OnPlaylistChanged(object? sender, EventArgs e) { - if (Playlist != null) - { - Playlist.PlaylistUpdated -= OnPlaylistUpdated; - } - + Playlist?.PlaylistUpdated -= OnPlaylistUpdated; Playlist = _audioPlayer.Playlist; + Playlist?.PlaylistUpdated += OnPlaylistUpdated; - if (Playlist != null) - { - Playlist.PlaylistUpdated += OnPlaylistUpdated; - } - - PlaylistSong[] playlistSongs = _audioPlayer.Playlist?.Songs.ToArray() ?? []; - - PlaylistSongs = [.. playlistSongs]; UpdateFilteredSongs(); } @@ -215,32 +199,23 @@ public partial class PlaylistDetailViewModel : ViewModelBase switch (e.Action) { case PlaylistUpdateAction.Add: - _dispatcherQueue.TryEnqueue(() => AddSongs(e.Songs, e.Index)); - break; case PlaylistUpdateAction.Remove: - _dispatcherQueue.TryEnqueue(() => RemoveSongsFromCollection(e.Songs)); + _dispatcherQueue.TryEnqueue(UpdateFilteredSongs); + break; + case PlaylistUpdateAction.Move: + case PlaylistUpdateAction.Reset: + _dispatcherQueue.TryEnqueue(() => ApplyReorderedSongs(e.Songs)); break; } } - private void AddSongs(PlaylistSong[] playlistSongs, int index = 0) + private void ApplyReorderedSongs(PlaylistSong[] playlistSongs) { - // TODO: Performance improvements - int currentIndex = index; - + // UpdateFilteredSongs assumes the projection order matches Playlist.Songs order, + // so remove reordered songs first to let them be reinserted at their new positions. foreach (PlaylistSong playlistSong in playlistSongs) { - PlaylistSongs.Insert(currentIndex++, playlistSong); - } - - UpdateFilteredSongs(); - } - - private void RemoveSongsFromCollection(PlaylistSong[] playlistSongs) - { - foreach (PlaylistSong playlistSong in playlistSongs) - { - PlaylistSongs.Remove(playlistSong); + FilteredPlaylistSongs.Remove(playlistSong); } UpdateFilteredSongs(); diff --git a/Harmonia.WinUI/Views/PlaylistDetailView.xaml b/Harmonia.WinUI/Views/PlaylistDetailView.xaml index b709a72..8129985 100644 --- a/Harmonia.WinUI/Views/PlaylistDetailView.xaml +++ b/Harmonia.WinUI/Views/PlaylistDetailView.xaml @@ -184,11 +184,11 @@ Name="PlaylistListView" ItemsSource="{Binding FilteredPlaylistSongs}" ItemTemplate="{StaticResource SongTemplate}" - CanReorderItems="True" - CanDragItems="True" + CanReorderItems="{Binding CanReorderSongs, Mode=OneWay}" + CanDragItems="{Binding CanReorderSongs, Mode=OneWay}" DragItemsStarting="PlaylistListView_DragItemsStarting" DragItemsCompleted="PlaylistListView_DragItemsCompleted" - AllowDrop="True" + AllowDrop="{Binding CanReorderSongs, Mode=OneWay}" SelectionMode="Extended" SelectionChanged="PlaylistListView_SelectionChanged">