Added playlists view context menu for rename, lock/unlock, and delete.
This commit is contained in:
@@ -82,6 +82,11 @@ public class PlaylistManager : IPlaylistManager
|
|||||||
|
|
||||||
playlistRepository.Delete(playlist);
|
playlistRepository.Delete(playlist);
|
||||||
|
|
||||||
|
if (CurrentPlaylist == playlist)
|
||||||
|
{
|
||||||
|
CurrentPlaylist = _playlists.Count > 0 ? _playlists[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
PlaylistRemoved?.Invoke(this, new(playlist));
|
PlaylistRemoved?.Invoke(this, new(playlist));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -681,18 +681,6 @@ public partial class PlaylistDetailViewModel : ViewModelBase
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_playlistManager.RemovePlaylist(Playlist);
|
_playlistManager.RemovePlaylist(Playlist);
|
||||||
|
|
||||||
if (_playlistManager.CurrentPlaylist == Playlist)
|
|
||||||
{
|
|
||||||
if (_playlistManager.Playlists.Count > 0)
|
|
||||||
{
|
|
||||||
_playlistManager.CurrentPlaylist = _playlistManager.Playlists.ElementAt(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_playlistManager.CurrentPlaylist = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SortAllSongs(SortItem? sortItem)
|
private void SortAllSongs(SortItem? sortItem)
|
||||||
|
|||||||
@@ -35,6 +35,19 @@ public partial class PlaylistItemViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _isLocked;
|
||||||
|
public bool IsLocked
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _isLocked;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref _isLocked, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public PlaylistItemViewModel(Playlist playlist)
|
public PlaylistItemViewModel(Playlist playlist)
|
||||||
{
|
{
|
||||||
Playlist = playlist;
|
Playlist = playlist;
|
||||||
@@ -55,6 +68,7 @@ public partial class PlaylistItemViewModel : ObservableObject
|
|||||||
private void UpdateMetaData()
|
private void UpdateMetaData()
|
||||||
{
|
{
|
||||||
Name = Playlist.Name ?? "Untitled Playlist";
|
Name = Playlist.Name ?? "Untitled Playlist";
|
||||||
|
IsLocked = Playlist.IsLocked;
|
||||||
|
|
||||||
TimeSpan total = TimeSpan.FromSeconds(Playlist.Songs.Sum(s => s.Song.Length.TotalSeconds));
|
TimeSpan total = TimeSpan.FromSeconds(Playlist.Songs.Sum(s => s.Song.Length.TotalSeconds));
|
||||||
Summary = $"{Playlist.Songs.Count} songs / {total:h\\:mm\\:ss}";
|
Summary = $"{Playlist.Songs.Count} songs / {total:h\\:mm\\:ss}";
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
using Harmonia.Core.Player;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Harmonia.Core.Player;
|
||||||
using Harmonia.Core.Playlists;
|
using Harmonia.Core.Playlists;
|
||||||
|
using Harmonia.WinUI.Messaging;
|
||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Harmonia.WinUI.ViewModels;
|
namespace Harmonia.WinUI.ViewModels;
|
||||||
|
|
||||||
@@ -11,6 +14,7 @@ public partial class PlaylistsViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
private readonly IPlaylistManager _playlistManager;
|
private readonly IPlaylistManager _playlistManager;
|
||||||
private readonly IAudioPlayer _audioPlayer;
|
private readonly IAudioPlayer _audioPlayer;
|
||||||
|
private readonly IMessageService _messageService;
|
||||||
private readonly DispatcherQueue _dispatcherQueue;
|
private readonly DispatcherQueue _dispatcherQueue;
|
||||||
|
|
||||||
private ObservableCollection<PlaylistItemViewModel> _playlists = [];
|
private ObservableCollection<PlaylistItemViewModel> _playlists = [];
|
||||||
@@ -55,12 +59,31 @@ public partial class PlaylistsViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlaylistsViewModel(IPlaylistManager playlistManager, IAudioPlayer audioPlayer)
|
private PlaylistItemViewModel? _flyoutPlaylist = null;
|
||||||
|
public PlaylistItemViewModel? FlyoutPlaylist
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _flyoutPlaylist;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref _flyoutPlaylist, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAsyncRelayCommand<PlaylistItemViewModel?> RenamePlaylistCommand { get; }
|
||||||
|
public IRelayCommand<PlaylistItemViewModel?> LockPlaylistCommand { get; }
|
||||||
|
public IRelayCommand<PlaylistItemViewModel?> UnlockPlaylistCommand { get; }
|
||||||
|
public IAsyncRelayCommand<PlaylistItemViewModel?> DeletePlaylistCommand { get; }
|
||||||
|
|
||||||
|
public PlaylistsViewModel(IPlaylistManager playlistManager, IAudioPlayer audioPlayer, IMessageService messageService)
|
||||||
{
|
{
|
||||||
_playlistManager = playlistManager;
|
_playlistManager = playlistManager;
|
||||||
_playlistManager.PlaylistAdded += OnPlaylistAdded;
|
_playlistManager.PlaylistAdded += OnPlaylistAdded;
|
||||||
_playlistManager.PlaylistRemoved += OnPlaylistRemoved;
|
_playlistManager.PlaylistRemoved += OnPlaylistRemoved;
|
||||||
_playlistManager.CurrentPlaylistChanged += OnCurrentPlaylistChanged;
|
_playlistManager.CurrentPlaylistChanged += OnCurrentPlaylistChanged;
|
||||||
|
_messageService = messageService;
|
||||||
|
|
||||||
_audioPlayer = audioPlayer;
|
_audioPlayer = audioPlayer;
|
||||||
_audioPlayer.PlayingSongChanged += OnPlayingSongChanged;
|
_audioPlayer.PlayingSongChanged += OnPlayingSongChanged;
|
||||||
@@ -70,6 +93,11 @@ public partial class PlaylistsViewModel : ViewModelBase
|
|||||||
_playlists = new ObservableCollection<PlaylistItemViewModel>(_playlistManager.Playlists.Select(p => new PlaylistItemViewModel(p)));
|
_playlists = new ObservableCollection<PlaylistItemViewModel>(_playlistManager.Playlists.Select(p => new PlaylistItemViewModel(p)));
|
||||||
_selectedPlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _playlistManager.CurrentPlaylist);
|
_selectedPlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _playlistManager.CurrentPlaylist);
|
||||||
_activePlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist);
|
_activePlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist);
|
||||||
|
|
||||||
|
RenamePlaylistCommand = new AsyncRelayCommand<PlaylistItemViewModel?>(RenamePlaylist);
|
||||||
|
LockPlaylistCommand = new RelayCommand<PlaylistItemViewModel?>(LockPlaylist);
|
||||||
|
UnlockPlaylistCommand = new RelayCommand<PlaylistItemViewModel?>(UnlockPlaylist);
|
||||||
|
DeletePlaylistCommand = new AsyncRelayCommand<PlaylistItemViewModel?>(DeletePlaylistAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCurrentPlaylistChanged(object? sender, EventArgs e)
|
private void OnCurrentPlaylistChanged(object? sender, EventArgs e)
|
||||||
@@ -102,4 +130,57 @@ public partial class PlaylistsViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
ActivePlaylist = Playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist);
|
ActivePlaylist = Playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RenamePlaylist(PlaylistItemViewModel? playlistItem)
|
||||||
|
{
|
||||||
|
if (playlistItem is null || playlistItem.Playlist is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Playlist playlist = playlistItem.Playlist;
|
||||||
|
|
||||||
|
string newName = await _messageService.InputTextAsync(
|
||||||
|
$"Rename Playlist - {playlist.Name}",
|
||||||
|
"Enter a new name for the playlist:",
|
||||||
|
playlist.Name ?? string.Empty);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(newName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
playlist.SetName(newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LockPlaylist(PlaylistItemViewModel? playlistItem)
|
||||||
|
{
|
||||||
|
if (playlistItem is null || playlistItem.Playlist is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Playlist playlist = playlistItem.Playlist;
|
||||||
|
|
||||||
|
playlist.Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnlockPlaylist(PlaylistItemViewModel? playlistItem)
|
||||||
|
{
|
||||||
|
if (playlistItem is null || playlistItem.Playlist is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Playlist playlist = playlistItem.Playlist;
|
||||||
|
|
||||||
|
playlist.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeletePlaylistAsync(PlaylistItemViewModel? playlistItem)
|
||||||
|
{
|
||||||
|
if (playlistItem is null || playlistItem.Playlist is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Playlist playlist = playlistItem.Playlist;
|
||||||
|
|
||||||
|
bool confirmed = await _messageService.ConfirmAsync($"Delete Playlist - {playlist.Name}", "Are you sure you want to delete this playlist?");
|
||||||
|
|
||||||
|
if (!confirmed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_playlistManager.RemovePlaylist(playlist);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,11 +7,15 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vm="using:Harmonia.WinUI.ViewModels"
|
xmlns:vm="using:Harmonia.WinUI.ViewModels"
|
||||||
|
xmlns:converter="using:Harmonia.WinUI.Converters"
|
||||||
xmlns:playlists="using:Harmonia.Core.Playlists"
|
xmlns:playlists="using:Harmonia.Core.Playlists"
|
||||||
DataContext="{Binding Source={StaticResource Locator}, Path=PlaylistsViewModel}"
|
DataContext="{Binding Source={StaticResource Locator}, Path=PlaylistsViewModel}"
|
||||||
d:DataContext="{d:DesignInstance Type=vm:PlaylistsViewModel, IsDesignTimeCreatable=True}"
|
d:DataContext="{d:DesignInstance Type=vm:PlaylistsViewModel, IsDesignTimeCreatable=True}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
|
<converter:BooleanToVisibilityConverter x:Key="BoolToVis" />
|
||||||
|
<converter:BooleanToVisibilityConverter x:Key="InverseBoolToVis" TrueValue="Collapsed" FalseValue="Visible"/>
|
||||||
|
|
||||||
<SolidColorBrush x:Key="PlaylistItemIconBrush" Color="#dddddd"/>
|
<SolidColorBrush x:Key="PlaylistItemIconBrush" Color="#dddddd"/>
|
||||||
<SolidColorBrush x:Key="PlaylistItemTitleBrush" Color="#dddddd"/>
|
<SolidColorBrush x:Key="PlaylistItemTitleBrush" Color="#dddddd"/>
|
||||||
<SolidColorBrush x:Key="PlaylistItemSubtitleBrush" Color="#aaaaaa"/>
|
<SolidColorBrush x:Key="PlaylistItemSubtitleBrush" Color="#aaaaaa"/>
|
||||||
@@ -76,7 +80,44 @@
|
|||||||
Name="PlaylistsListView"
|
Name="PlaylistsListView"
|
||||||
ItemsSource="{Binding Playlists, Mode=OneWay}"
|
ItemsSource="{Binding Playlists, Mode=OneWay}"
|
||||||
SelectedItem="{Binding SelectedPlaylist, Mode=TwoWay}"
|
SelectedItem="{Binding SelectedPlaylist, Mode=TwoWay}"
|
||||||
ItemTemplate="{StaticResource PlaylistTemplate}">
|
ItemTemplate="{StaticResource PlaylistTemplate}"
|
||||||
|
RightTapped="PlaylistsListView_RightTapped">
|
||||||
|
<ListView.ContextFlyout>
|
||||||
|
<MenuFlyout
|
||||||
|
x:Name="PlaylistListViewMenuFlyout"
|
||||||
|
ShouldConstrainToRootBounds="False"
|
||||||
|
SystemBackdrop="{StaticResource AcrylicBackgroundFillColorDefaultBackdrop}">
|
||||||
|
<MenuFlyout.MenuFlyoutPresenterStyle>
|
||||||
|
<Style TargetType="MenuFlyoutPresenter">
|
||||||
|
<Setter Property="Padding" Value="10"></Setter>
|
||||||
|
<Setter Property="Background" Value="Transparent"></Setter>
|
||||||
|
</Style>
|
||||||
|
</MenuFlyout.MenuFlyoutPresenterStyle>
|
||||||
|
|
||||||
|
<MenuFlyoutItem Text="{Binding FlyoutPlaylist.Name}" IsEnabled="False" FontWeight="SemiLight">
|
||||||
|
<MenuFlyoutItem.Resources>
|
||||||
|
<SolidColorBrush x:Key="MenuFlyoutItemForegroundDisabled" Color="#aaaaaa"/>
|
||||||
|
</MenuFlyoutItem.Resources>
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
|
||||||
|
<MenuFlyoutSeparator></MenuFlyoutSeparator>
|
||||||
|
|
||||||
|
<MenuFlyoutItem Text="Rename" Command="{Binding RenamePlaylistCommand}" CommandParameter="{Binding FlyoutPlaylist}">
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
|
||||||
|
<MenuFlyoutItem Text="Lock" Command="{Binding LockPlaylistCommand}" CommandParameter="{Binding FlyoutPlaylist}" Visibility="{Binding FlyoutPlaylist.IsLocked, Converter={StaticResource InverseBoolToVis}}">
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
|
||||||
|
<MenuFlyoutItem Text="Unlock" Command="{Binding UnlockPlaylistCommand}" CommandParameter="{Binding FlyoutPlaylist}" Visibility="{Binding FlyoutPlaylist.IsLocked, Converter={StaticResource BoolToVis}}">
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
|
||||||
|
<MenuFlyoutSeparator></MenuFlyoutSeparator>
|
||||||
|
|
||||||
|
<MenuFlyoutItem Text="Remove" Foreground="#ff99a4" Command="{Binding DeletePlaylistCommand}" CommandParameter="{Binding FlyoutPlaylist}">
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
|
||||||
|
</MenuFlyout>
|
||||||
|
</ListView.ContextFlyout>
|
||||||
</ListView>
|
</ListView>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -19,6 +19,16 @@ public sealed partial class PlaylistsView : UserControl
|
|||||||
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
|
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
|
||||||
|
|
||||||
PlaylistsListView.ContainerContentChanging += OnContainerContentChanging;
|
PlaylistsListView.ContainerContentChanging += OnContainerContentChanging;
|
||||||
|
|
||||||
|
foreach (MenuFlyoutItemBase item in PlaylistListViewMenuFlyout.Items)
|
||||||
|
{
|
||||||
|
item.DataContext = _viewModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlaylistsListView_RightTapped(object sender, Microsoft.UI.Xaml.Input.RightTappedRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_viewModel.FlyoutPlaylist = (e.OriginalSource as FrameworkElement)?.DataContext as PlaylistItemViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
|
private void OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
|
||||||
|
|||||||
Reference in New Issue
Block a user