Highlight active playlist. Various optimizations and fixes.
This commit is contained in:
@@ -611,7 +611,7 @@ public partial class PlaylistDetailViewModel : ViewModelBase
|
||||
if (Playlist == null || SelectedPlaylistSongs.Count == 0)
|
||||
return;
|
||||
|
||||
int selectedPlaylistSongIndex = Playlist.Songs.IndexOf(SelectedPlaylistSongs[0]);
|
||||
int selectedPlaylistSongIndex = Playlist.IndexOf(SelectedPlaylistSongs[0]);
|
||||
|
||||
if (selectedPlaylistSongIndex == -1)
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Harmonia.Core.Player;
|
||||
using Harmonia.Core.Playlists;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -9,6 +9,7 @@ namespace Harmonia.WinUI.ViewModels;
|
||||
public partial class PlaylistsViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IPlaylistManager _playlistManager;
|
||||
private readonly IAudioPlayer _audioPlayer;
|
||||
|
||||
private ObservableCollection<PlaylistItemViewModel> _playlists = [];
|
||||
public ObservableCollection<PlaylistItemViewModel> Playlists
|
||||
@@ -39,13 +40,29 @@ public partial class PlaylistsViewModel : ViewModelBase
|
||||
}
|
||||
}
|
||||
|
||||
public PlaylistsViewModel(IPlaylistManager playlistManager)
|
||||
private PlaylistItemViewModel? _activePlaylist = null;
|
||||
public PlaylistItemViewModel? ActivePlaylist
|
||||
{
|
||||
get
|
||||
{
|
||||
return _activePlaylist;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetProperty(ref _activePlaylist, value);
|
||||
}
|
||||
}
|
||||
|
||||
public PlaylistsViewModel(IPlaylistManager playlistManager, IAudioPlayer audioPlayer)
|
||||
{
|
||||
_playlistManager = playlistManager;
|
||||
_playlistManager.PlaylistAdded += OnPlaylistAdded;
|
||||
_playlistManager.PlaylistRemoved += OnPlaylistRemoved;
|
||||
_playlistManager.CurrentPlaylistChanged += OnCurrentPlaylistChanged;
|
||||
|
||||
_audioPlayer = audioPlayer;
|
||||
_audioPlayer.PlayingSongChanged += OnPlayingSongChanged;
|
||||
|
||||
_playlists = new ObservableCollection<PlaylistItemViewModel>(_playlistManager.Playlists.Select(p => new PlaylistItemViewModel(p)));
|
||||
_selectedPlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _playlistManager.CurrentPlaylist);
|
||||
}
|
||||
@@ -70,4 +87,9 @@ public partial class PlaylistsViewModel : ViewModelBase
|
||||
Playlists.Remove(playlistView);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayingSongChanged(object? sender, EventArgs e)
|
||||
{
|
||||
ActivePlaylist = Playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist);
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,34 @@
|
||||
d:DataContext="{d:DesignInstance Type=vm:PlaylistsViewModel, IsDesignTimeCreatable=True}"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<SolidColorBrush x:Key="PlaylistItemIconBrush" Color="#dddddd"/>
|
||||
<SolidColorBrush x:Key="PlaylistItemTitleBrush" Color="#dddddd"/>
|
||||
<SolidColorBrush x:Key="PlaylistItemSubtitleBrush" Color="#aaaaaa"/>
|
||||
|
||||
|
||||
<!-- Playlist Icon Path -->
|
||||
<Style x:Key="PlaylistIconPath" TargetType="PathIcon">
|
||||
<Setter Property="Foreground" Value="{StaticResource PlaylistItemIconBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="SelectedPlaylistIconPath" TargetType="PathIcon" BasedOn="{StaticResource PlaylistIconPath}">
|
||||
<Setter Property="Foreground" Value="{StaticResource AccentTextFillColorPrimaryBrush}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Playlist Title Text Block -->
|
||||
<Style x:Key="PlaylistTitleTextBlock" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{StaticResource PlaylistItemTitleBrush}"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontWeight" Value="Medium"/>
|
||||
<Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
|
||||
<Setter Property="LineHeight" Value="0"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="SelectedPlaylistTitleTextBlock" TargetType="TextBlock" BasedOn="{StaticResource PlaylistTitleTextBlock}">
|
||||
<Setter Property="Foreground" Value="{StaticResource AccentTextFillColorPrimaryBrush}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Playlist Item Subtitle Text Block -->
|
||||
<Style x:Key="PlaylistSubtitleTextBlock" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{StaticResource PlaylistItemSubtitleBrush}"/>
|
||||
@@ -23,26 +49,31 @@
|
||||
<Setter Property="LineHeight" Value="0"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="SelectedPlaylistSubtitleTextBlock" TargetType="TextBlock" BasedOn="{StaticResource PlaylistSubtitleTextBlock}">
|
||||
<Setter Property="Foreground" Value="{StaticResource AccentTextFillColorSecondaryBrush}"/>
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="PlaylistTemplate" x:DataType="vm:PlaylistItemViewModel">
|
||||
<Grid>
|
||||
<Grid x:Name="PlaylistListViewItem">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="200" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||||
<Viewbox Height="24" Width="24">
|
||||
<PathIcon Data="{StaticResource MusicNoteList}" />
|
||||
<PathIcon x:Name="MusicNoteListPathIcon" Data="{StaticResource MusicNoteList}" />
|
||||
</Viewbox>
|
||||
<TextBlock Text="{x:Bind Name, Mode=OneWay}" FontSize="16" VerticalAlignment="Center" />
|
||||
<TextBlock x:Name="PlaylistTitleTextBlock" Text="{x:Bind Name, Mode=OneWay}" Style="{StaticResource PlaylistTitleTextBlock}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Spacing="8">
|
||||
<TextBlock Text="{x:Bind Summary, Mode=OneWay}" Style="{StaticResource PlaylistSubtitleTextBlock}" />
|
||||
<TextBlock x:Name="PlaylistSubtitleTextBlock" Text="{x:Bind Summary, Mode=OneWay}" Style="{StaticResource PlaylistSubtitleTextBlock}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="10">
|
||||
<ListView
|
||||
Name="PlaylistsListView"
|
||||
ItemsSource="{Binding Playlists, Mode=OneWay}"
|
||||
SelectedItem="{Binding SelectedPlaylist, Mode=TwoWay}"
|
||||
ItemTemplate="{StaticResource PlaylistTemplate}">
|
||||
|
||||
@@ -1,12 +1,99 @@
|
||||
using CommunityToolkit.WinUI;
|
||||
using Harmonia.WinUI.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Harmonia.WinUI.Views;
|
||||
|
||||
public sealed partial class PlaylistsView : UserControl
|
||||
{
|
||||
private readonly PlaylistsViewModel _viewModel;
|
||||
|
||||
public PlaylistsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_viewModel = (PlaylistsViewModel)DataContext;
|
||||
_viewModel.PropertyChanging += OnViewModelPropertyChanging;
|
||||
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
|
||||
}
|
||||
|
||||
private void OnViewModelPropertyChanging(object? sender, PropertyChangingEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(_viewModel.ActivePlaylist):
|
||||
UpdateListViewItemStyle(PlaylistItemStyle.Normal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(_viewModel.ActivePlaylist):
|
||||
UpdateListViewItemStyle(PlaylistItemStyle.Selected);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateListViewItemStyle(PlaylistItemStyle style)
|
||||
{
|
||||
ListViewItem listViewItem = (ListViewItem)PlaylistsListView.ContainerFromItem(_viewModel.ActivePlaylist);
|
||||
|
||||
if (listViewItem is null)
|
||||
return;
|
||||
|
||||
FrameworkElement? frameworkElement = TryGetFrameworkElement(listViewItem, "PlaylistListViewItem");
|
||||
|
||||
if (frameworkElement == null)
|
||||
return;
|
||||
|
||||
string playlistItemIconBrushName = style == PlaylistItemStyle.Selected ? "SelectedPlaylistIconPath" : "PlaylistIconPath";
|
||||
string playlistItemTitleBrushName = style == PlaylistItemStyle.Selected ? "SelectedPlaylistTitleTextBlock" : "PlaylistTitleTextBlock";
|
||||
string playlistItemSubtitleBrushName = style == PlaylistItemStyle.Selected ? "SelectedPlaylistSubtitleTextBlock" : "PlaylistSubtitleTextBlock";
|
||||
|
||||
UpdateElementStyle(frameworkElement, "MusicNoteListPathIcon", playlistItemIconBrushName);
|
||||
UpdateElementStyle(frameworkElement, "PlaylistTitleTextBlock", playlistItemTitleBrushName);
|
||||
UpdateElementStyle(frameworkElement, "PlaylistSubtitleTextBlock", playlistItemSubtitleBrushName);
|
||||
}
|
||||
|
||||
private static FrameworkElement? TryGetFrameworkElement(ListViewItem listViewItem, string elementName)
|
||||
{
|
||||
if (listViewItem.Name == elementName)
|
||||
{
|
||||
return listViewItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (listViewItem.FindDescendant(elementName) is not FrameworkElement frameworkElement)
|
||||
return null;
|
||||
|
||||
return frameworkElement;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateElementStyle(FrameworkElement dependencyObject, string elementName, string resourceName)
|
||||
{
|
||||
if (dependencyObject.FindDescendant(elementName) is not FrameworkElement frameworkElement)
|
||||
return;
|
||||
|
||||
Resources.TryGetValue(resourceName, out object? resource);
|
||||
|
||||
if (resource == null)
|
||||
Application.Current.Resources.TryGetValue(resourceName, out resource);
|
||||
|
||||
if (resource is not Style style)
|
||||
return;
|
||||
|
||||
frameworkElement.Style = style;
|
||||
}
|
||||
}
|
||||
|
||||
public enum PlaylistItemStyle
|
||||
{
|
||||
Normal,
|
||||
Selected
|
||||
}
|
||||
Reference in New Issue
Block a user