Added playlist view.

This commit is contained in:
2025-03-27 01:37:32 -04:00
parent 97f6040122
commit b7ba8341a1
13 changed files with 302 additions and 49 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework> <TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>Harmonia.WinUI</RootNamespace> <RootNamespace>Harmonia.WinUI</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
@@ -43,6 +43,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.1.240916" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250310001" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250310001" />
</ItemGroup> </ItemGroup>
@@ -59,6 +60,9 @@
<Page Update="Resources\Converters.xaml"> <Page Update="Resources\Converters.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Page> </Page>
<Page Update="Views\PlaylistView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\PlayerView.xaml"> <Page Update="Views\PlayerView.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>

View File

@@ -5,6 +5,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Harmonia.WinUI" xmlns:local="using:Harmonia.WinUI"
xmlns:views="using:Harmonia.WinUI.Views" xmlns:views="using:Harmonia.WinUI.Views"
xmlns:Media="using:CommunityToolkit.WinUI.Media"
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"
mc:Ignorable="d" mc:Ignorable="d"
@@ -18,6 +19,22 @@
<RowDefinition Height="*"></RowDefinition> <RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.RowSpan="2">
<Image Source="/Assets/Default.png" Stretch="UniformToFill" VerticalAlignment="Center" HorizontalAlignment="Center"></Image>
<Canvas Background="#99000000"></Canvas>
<Border>
<Border.Background>
<Media:BackdropBlurBrush Amount="20"></Media:BackdropBlurBrush>
</Border.Background>
</Border>
</Grid>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<views:PlaylistView Grid.Column="1"></views:PlaylistView>
</Grid>
<!--<views:PlayingSongInfo Grid.Row="0"></views:PlayingSongInfo>--> <!--<views:PlayingSongInfo Grid.Row="0"></views:PlayingSongInfo>-->
<views:PlayerView Grid.Row="1"></views:PlayerView> <views:PlayerView Grid.Row="1"></views:PlayerView>
</Grid> </Grid>

View File

@@ -1,36 +1,11 @@
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure, namespace Harmonia.WinUI;
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Harmonia.WinUI public sealed partial class MainWindow : Window
{ {
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
public MainWindow() public MainWindow()
{ {
this.InitializeComponent(); InitializeComponent();
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
//myButton.Content = "Clicked";
}
} }
} }

View File

@@ -122,4 +122,13 @@
M11 1a2 2 0 0 0-2 2v4a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h5V3a3 3 0 0 1 6 0v4a.5.5 0 0 1-1 0V3a2 2 0 0 0-2-2M3 8a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1z M11 1a2 2 0 0 0-2 2v4a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h5V3a3 3 0 0 1 6 0v4a.5.5 0 0 1-1 0V3a2 2 0 0 0-2-2M3 8a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1z
</x:String> </x:String>
<x:String x:Key="AddIcon">
M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4
</x:String>
<x:String x:Key="MoreIcon">
M3 9.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3m5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3m5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3
</x:String>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -10,6 +10,11 @@
<Setter Property="BorderThickness" Value="0"/> <Setter Property="BorderThickness" Value="0"/>
</Style> </Style>
<!-- Flat Round Button -->
<Style x:Key="FlatRoundButton" TargetType="Button" BasedOn="{StaticResource FlatButton}">
<Setter Property="CornerRadius" Value="32"/>
</Style>
<!-- Flat Button Path Icon --> <!-- Flat Button Path Icon -->
<Style x:Key="FlatButtonIcon" TargetType="PathIcon"> <Style x:Key="FlatButtonIcon" TargetType="PathIcon">
<Setter Property="Width" Value="18"/> <Setter Property="Width" Value="18"/>

View File

@@ -4,7 +4,7 @@ namespace Harmonia.WinUI.Storage;
public interface IStorageProvider public interface IStorageProvider
{ {
public Task<string> GetFileAsync(FilePickerOptions? options = null); public Task<string?> GetSingleFileAsync(FilePickerOptions? options = null);
public Task<string[]> GetFilesAsync(FilePickerOptions? options = null); public Task<string[]> GetFilesAsync(FilePickerOptions? options = null);
public Task<string> GetPathAsync(); public Task<string?> GetPathAsync();
} }

View File

@@ -1,4 +1,5 @@
using System; using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -7,16 +8,18 @@ using Windows.Storage.Pickers;
namespace Harmonia.WinUI.Storage; namespace Harmonia.WinUI.Storage;
public class WindowsStorageProvider(MainWindow mainWindow) : IStorageProvider public class WindowsStorageProvider : IStorageProvider
{ {
public async Task<string> GetFileAsync(FilePickerOptions? options = null) private static MainWindow MainWindow => App.ServiceProvider.GetRequiredService<MainWindow>();
public async Task<string?> GetSingleFileAsync(FilePickerOptions? options = null)
{ {
FileOpenPicker fileOpenPicker = GetFileOpenPicker(options); FileOpenPicker fileOpenPicker = GetFileOpenPicker(options);
InitializePicker(fileOpenPicker); InitializePicker(fileOpenPicker);
StorageFile storageFile = await fileOpenPicker.PickSingleFileAsync(); StorageFile? storageFile = await fileOpenPicker.PickSingleFileAsync();
return storageFile.Path; return storageFile?.Path;
} }
public async Task<string[]> GetFilesAsync(FilePickerOptions? options = null) public async Task<string[]> GetFilesAsync(FilePickerOptions? options = null)
@@ -67,7 +70,7 @@ public class WindowsStorageProvider(MainWindow mainWindow) : IStorageProvider
return [.. fileTypes.SelectMany(fileType => fileType.Patterns)]; return [.. fileTypes.SelectMany(fileType => fileType.Patterns)];
} }
public async Task<string> GetPathAsync() public async Task<string?> GetPathAsync()
{ {
var folderPicker = new FolderPicker var folderPicker = new FolderPicker
{ {
@@ -77,14 +80,14 @@ public class WindowsStorageProvider(MainWindow mainWindow) : IStorageProvider
InitializePicker(folderPicker); InitializePicker(folderPicker);
StorageFolder folder = await folderPicker.PickSingleFolderAsync(); StorageFolder? folder = await folderPicker.PickSingleFolderAsync();
return folder.Path; return folder?.Path;
} }
private void InitializePicker(object target) private static void InitializePicker(object target)
{ {
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(mainWindow); var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(target, hWnd); WinRT.Interop.InitializeWithWindow.Initialize(target, hWnd);
} }
} }

View File

@@ -1,4 +1,5 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Harmonia.Core.Engine;
using Harmonia.Core.Models; using Harmonia.Core.Models;
using Harmonia.Core.Player; using Harmonia.Core.Player;
using Harmonia.WinUI.Caching; using Harmonia.WinUI.Caching;
@@ -7,6 +8,7 @@ using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Imaging; using Microsoft.UI.Xaml.Media.Imaging;
using System; using System;
using System.ComponentModel;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
@@ -196,6 +198,7 @@ public partial class PlayerViewModel : ViewModelBase
{ {
_audioPlayer = audioPlayer; _audioPlayer = audioPlayer;
_audioPlayer.PlayingSongChanged += OnPlayingSongChanged; _audioPlayer.PlayingSongChanged += OnPlayingSongChanged;
_audioPlayer.PropertyChanged += OnAudioPlayerPropertyChanged;
_audioBitmapImageCache = audioBitmapCache; _audioBitmapImageCache = audioBitmapCache;
@@ -232,6 +235,28 @@ public partial class PlayerViewModel : ViewModelBase
DispatcherQueue.GetForCurrentThread().TryEnqueue(() => SongImageSource = bitmapImage); DispatcherQueue.GetForCurrentThread().TryEnqueue(() => SongImageSource = bitmapImage);
} }
private void OnAudioPlayerPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(_audioPlayer.State):
UpdateTimer();
break;
}
}
private void UpdateTimer()
{
if (_audioPlayer.State == AudioPlaybackState.Playing)
{
_timer.Start();
}
else
{
_timer.Stop();
}
}
private void TickTock(object? sender, object e) private void TickTock(object? sender, object e)
{ {
Position = _audioPlayer.Position; Position = _audioPlayer.Position;

View File

@@ -20,6 +20,8 @@ using System.Threading.Tasks;
using System.Timers; using System.Timers;
using System.Windows.Input; using System.Windows.Input;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Windows.System;
using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;
using Timer = System.Timers.Timer; using Timer = System.Timers.Timer;
namespace Harmonia.WinUI.ViewModels; namespace Harmonia.WinUI.ViewModels;
@@ -31,6 +33,7 @@ public partial class PlaylistViewModel : ViewModelBase
private readonly IAudioFileScanner _audioFileScanner; private readonly IAudioFileScanner _audioFileScanner;
private readonly IAudioEngine _audioEngine; private readonly IAudioEngine _audioEngine;
private readonly IStorageProvider _storageProvider; private readonly IStorageProvider _storageProvider;
private readonly DispatcherQueue _dispatcherQueue;
private Timer? _filterTimer; private Timer? _filterTimer;
@@ -121,7 +124,8 @@ public partial class PlaylistViewModel : ViewModelBase
IAudioBitmapImageCache audioBitmapImageCache, IAudioBitmapImageCache audioBitmapImageCache,
IAudioFileScanner audioFileScanner, IAudioFileScanner audioFileScanner,
IAudioEngine audioEngine, IAudioEngine audioEngine,
IStorageProvider storageProvider) IStorageProvider storageProvider,
IPlaylistRepository playlistRepository)
{ {
_audioPlayer = audioPlayer; _audioPlayer = audioPlayer;
_audioPlayer.PlaylistChanged += OnPlaylistChanged; _audioPlayer.PlaylistChanged += OnPlaylistChanged;
@@ -131,6 +135,23 @@ public partial class PlaylistViewModel : ViewModelBase
_audioFileScanner = audioFileScanner; _audioFileScanner = audioFileScanner;
_audioEngine = audioEngine; _audioEngine = audioEngine;
_storageProvider = storageProvider; _storageProvider = storageProvider;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
// Testing
Task.Run(() => PlayDemoSong(playlistRepository));
}
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 OnPlaylistChanged(object? sender, EventArgs e) private void OnPlaylistChanged(object? sender, EventArgs e)
@@ -161,10 +182,10 @@ public partial class PlaylistViewModel : ViewModelBase
switch (e.Action) switch (e.Action)
{ {
case PlaylistUpdateAction.Add: case PlaylistUpdateAction.Add:
DispatcherQueue.GetForCurrentThread().TryEnqueue(() => AddSongs(e.Songs, e.Index)); _dispatcherQueue.TryEnqueue(() => AddSongs(e.Songs, e.Index));
break; break;
case PlaylistUpdateAction.Remove: case PlaylistUpdateAction.Remove:
DispatcherQueue.GetForCurrentThread().TryEnqueue(() => RemoveSongsFromCollection(e.Songs)); _dispatcherQueue.TryEnqueue(() => RemoveSongsFromCollection(e.Songs));
break; break;
} }
} }
@@ -232,7 +253,7 @@ public partial class PlaylistViewModel : ViewModelBase
_filterTimer.Dispose(); _filterTimer.Dispose();
_filterTimer = null; _filterTimer = null;
DispatcherQueue.GetForCurrentThread().TryEnqueue(UpdateFilteredSongs); _dispatcherQueue.TryEnqueue(UpdateFilteredSongs);
} }
private void UpdateFilteredSongs() private void UpdateFilteredSongs()
@@ -304,10 +325,12 @@ public partial class PlaylistViewModel : ViewModelBase
private FilePickerFileType GetAudioFileTypes() private FilePickerFileType GetAudioFileTypes()
{ {
string[] patterns = _audioEngine.SupportedFormats.Select(format => format.Replace("*", "")).ToArray();
return new() return new()
{ {
Name = "Audio Files", Name = "Audio Files",
Patterns = [.. _audioEngine.SupportedFormats] Patterns = patterns
}; };
} }
@@ -316,7 +339,7 @@ public partial class PlaylistViewModel : ViewModelBase
if (Playlist == null) if (Playlist == null)
return; return;
string path = await _storageProvider.GetPathAsync(); string? path = await _storageProvider.GetPathAsync();
if (string.IsNullOrWhiteSpace(path)) if (string.IsNullOrWhiteSpace(path))
return; return;

View File

@@ -14,6 +14,7 @@
<Style x:Key="PlayerGrid" TargetType="Grid"> <Style x:Key="PlayerGrid" TargetType="Grid">
<Setter Property="Background" Value="#1a1a1a"/> <Setter Property="Background" Value="#1a1a1a"/>
<Setter Property="Padding" Value="10"/> <Setter Property="Padding" Value="10"/>
<Setter Property="Background" Value="Transparent"/>
</Style> </Style>
<Style x:Key="SongImage" TargetType="Image"> <Style x:Key="SongImage" TargetType="Image">
<Setter Property="Width" Value="80"/> <Setter Property="Width" Value="80"/>

View File

@@ -0,0 +1,134 @@
<UserControl
x:Class="Harmonia.WinUI.Views.PlaylistView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Harmonia.WinUI.Views"
xmlns:vm="using:Harmonia.WinUI.ViewModels"
xmlns:playlists="using:Harmonia.Core.Playlists"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding Source={StaticResource Locator}, Path=PlaylistViewModel}"
d:DataContext="{d:DesignInstance Type=vm:PlaylistViewModel, IsDesignTimeCreatable=True}"
mc:Ignorable="d">
<UserControl.Resources>
<!--<SolidColorBrush x:Key="PlaylistBackground" Color="#393a45"/>-->
<SolidColorBrush x:Key="PlaylistBackground" Color="#292a35"/>
<SolidColorBrush x:Key="PlaylistItemHighlightColor" Color="#494a55"/>
<!--<SolidColorBrush x:Key="PlaylistBackground" Color="#222"/>-->
<SolidColorBrush x:Key="SongItemTitleBrush" Color="#dddddd"/>
<SolidColorBrush x:Key="SongItemTitleBrushHighlighted" Color="#A2D2F6"/>
<SolidColorBrush x:Key="SongItemSubtitleBrush" Color="#aaaaaa"/>
<!--<SolidColorBrush x:Key="SongItemSubtitleBrushHighlighted" Color="#76b9ed"/>-->
<SolidColorBrush x:Key="SongItemSubtitleBrushHighlighted" Color="#ddA2D2F6"/>
<SolidColorBrush x:Key="SongItemFooterBrush" Color="#888"/>
<SolidColorBrush x:Key="SongItemFooterBrushHighlighted" Color="#aaA2D2F6"/>
<!-- Image Border -->
<Style x:Key="PlaylistSongImageBorder" TargetType="Border">
<Setter Property="Width" Value="75"/>
<Setter Property="Height" Value="75"/>
<Setter Property="CornerRadius" Value="8"/>
</Style>
<DataTemplate x:Key="SongTemplate" x:DataType="playlists:PlaylistSong">
<!-- Background was formerly transparent -->
<Border DoubleTapped="PlaylistListViewItem_DoubleTapped" Background="Transparent">
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Style="{StaticResource PlaylistSongImageBorder}">
<Grid>
<Image Loaded="Image_Loaded" Unloaded="Image_Unloaded"></Image>
<Canvas Background="#19000000"></Canvas>
</Grid>
</Border>
<Grid Grid.Column="1" Margin="10 0 0 0" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="60"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Foreground="{StaticResource SongItemTitleBrush}" Text="{x:Bind Song, Converter={StaticResource SongTitle}, Mode=OneWay}" FontSize="15" FontWeight="SemiBold" LineStackingStrategy="BlockLineHeight" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" LineHeight="0">
</TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Song.Artists, Converter={StaticResource ArtistsToString}}" Foreground="{StaticResource SongItemSubtitleBrush}" TextTrimming="CharacterEllipsis" LineStackingStrategy="BlockLineHeight" LineHeight="0" FontSize="14" Margin="0 0 0 0">
</TextBlock>
<TextBlock Grid.Row="2" Text="{Binding Song.Album}" Foreground="{StaticResource SongItemSubtitleBrush}" TextTrimming="CharacterEllipsis" LineStackingStrategy="BlockLineHeight" LineHeight="0" FontSize="14" Margin="0 0 0 0">
</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center" TextAlignment="Right" HorizontalTextAlignment="Right" Foreground="{StaticResource SongItemSubtitleBrush}" TextTrimming="CharacterEllipsis" LineStackingStrategy="BlockLineHeight" LineHeight="0" FontSize="13" Text="{Binding Song.Length.TotalSeconds, Converter={StaticResource SecondsToString}}"></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="{StaticResource SongItemFooterBrush}" TextTrimming="CharacterEllipsis" LineStackingStrategy="BlockLineHeight" LineHeight="0" FontSize="12" Text="{Binding Song.FileType}"></TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="{StaticResource SongItemFooterBrush}" TextTrimming="CharacterEllipsis" LineStackingStrategy="BlockLineHeight" LineHeight="0" FontSize="12">
<Run Text="{Binding Song.BitRate}"></Run><Run Text=" kbps"></Run>
</TextBlock>
</Grid>
</Grid>
</Border>
</DataTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Margin="10 0" HorizontalAlignment="Stretch" PlaceholderText="Filter"></TextBox>
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="10">
<Button Style="{StaticResource FlatRoundButton}" Foreground="#7FD184">
<Button.Content>
<Path Style="{StaticResource FlatButtonPath}" Fill="#7FD184" Data="{StaticResource AddIcon}"></Path>
</Button.Content>
<Button.Flyout>
<MenuFlyout Placement="Bottom">
<MenuFlyoutItem Text="Add Files..." Command="{Binding AddFilesCommand}">
<MenuFlyoutItem.Icon>
<PathIcon Data="{StaticResource CopyIcon}"></PathIcon>
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Add Folder..." Command="{Binding AddFolderCommand}">
<MenuFlyoutItem.Icon>
<PathIcon Data="{StaticResource CopyIcon}"></PathIcon>
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
</MenuFlyout>
</Button.Flyout>
</Button>
<Button Style="{StaticResource FlatRoundButton}">
<Button.Content>
<Path Style="{StaticResource FlatButtonPath}" Data="{StaticResource MoreIcon}"></Path>
</Button.Content>
<Button.Flyout>
<MenuFlyout Placement="Bottom">
<MenuFlyoutItem Text="Add Files..." Command="{Binding AddFilesCommand}">
<MenuFlyoutItem.Icon>
<PathIcon Data="{StaticResource CopyIcon}"></PathIcon>
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Add Folder..." Command="{Binding AddFolderCommand}">
<MenuFlyoutItem.Icon>
<PathIcon Data="{StaticResource CopyIcon}"></PathIcon>
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
</MenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>
</Grid>
<ListView
Grid.Row="1"
Name="PlaylistListView"
ItemsSource="{Binding FilteredPlaylistSongs}"
ItemTemplate="{StaticResource SongTemplate}"
SelectionMode="Extended">
</ListView>
</Grid>
</UserControl>

View File

@@ -0,0 +1,57 @@
using Harmonia.Core.Playlists;
using Harmonia.WinUI.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using System.Threading.Tasks;
namespace Harmonia.WinUI.Views;
public sealed partial class PlaylistView : UserControl
{
private readonly PlaylistViewModel _viewModel;
public PlaylistView()
{
InitializeComponent();
_viewModel = (PlaylistViewModel)DataContext;
}
private void Image_Loaded(object sender, RoutedEventArgs e)
{
//Image? image = sender as Image;
//if (image == null)
// return;
//image.DataContextChanged += Image_DataContextChanged;
//var song = (PlaylistSong)image.DataContext;
//if (song == null)
// return;
//Task.Run(async () => await FetchImage(song.Song, image));
}
private void Image_Unloaded(object sender, RoutedEventArgs e)
{
//Image? image = sender as Image;
//if (image == null)
// return;
//image.DataContextChanged -= Image_DataContextChanged;
}
private async void PlaylistListViewItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
if (sender is not FrameworkElement element)
return;
if (element == null || element.DataContext is not PlaylistSong playlistSong)
return;
await _viewModel.PlaySongAsync(playlistSong);
}
}