Added playlist view and initial playlist view logic.
This commit is contained in:
@@ -23,6 +23,12 @@
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Style>
|
||||
|
||||
<!-- Flat Button ViewBox -->
|
||||
<!--<Style Selector="Viewbox.FlatButtonViewbox">
|
||||
<Setter Property="Width" Value="18"/>
|
||||
<Setter Property="Height" Value="18"/>
|
||||
</Style>-->
|
||||
|
||||
<!-- Flat Button Path Icon -->
|
||||
<Style Selector="PathIcon.FlatButtonIcon">
|
||||
<Setter Property="Padding" Value="10"/>
|
||||
@@ -31,6 +37,12 @@
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Style>
|
||||
|
||||
<!-- Flat Button Path Icon (Large) -->
|
||||
<Style Selector="PathIcon.FlatButtonIcon.Medium">
|
||||
<Setter Property="Width" Value="24"/>
|
||||
<Setter Property="Height" Value="24"/>
|
||||
</Style>
|
||||
|
||||
<!-- Flat Button Path Icon (Large) -->
|
||||
<Style Selector="PathIcon.FlatButtonIcon.Large">
|
||||
<Setter Property="Width" Value="36"/>
|
||||
@@ -106,14 +118,36 @@
|
||||
<!-- Action Buttons -->
|
||||
<Grid Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Right">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Classes="Flat" Command="{Binding ToggleMuteCommand}">
|
||||
<Grid>
|
||||
<!--<PathIcon Classes="FlatButtonIcon Medium" Data="{StaticResource VolumeHighIcon}"></PathIcon>-->
|
||||
<PathIcon Classes="FlatButtonIcon Medium" Data="{Binding VolumeState, Converter={StaticResource VolumeStateToIconConverter}}"></PathIcon>
|
||||
</Grid>
|
||||
</Button>
|
||||
<Slider Value="{Binding Volume, Mode=TwoWay}" Minimum="0" Maximum="1" PointerWheelChanged="VolumeSlider_PointerWheelChanged" Width="200" VerticalAlignment="Center" />
|
||||
<Button Classes="Flat" Command="{Binding ToggleRandomizerCommand}">
|
||||
<PathIcon Classes="FlatButtonIcon Medium" Classes.ShuffleOn="{Binding IsRandom}" Data="{StaticResource ShuffleIcon}">
|
||||
<PathIcon.Styles>
|
||||
<Style Selector="PathIcon.ShuffleOn">
|
||||
<Setter Property="Foreground" Value="#76b9ed"/>
|
||||
</Style>
|
||||
</PathIcon.Styles>
|
||||
</PathIcon>
|
||||
</Button>
|
||||
<Button Classes="Flat" Command="{Binding ToggleRepeatCommand}">
|
||||
<PathIcon Classes="FlatButtonIcon Medium" Data="{Binding RepeatState, Converter={StaticResource RepeatStateToIconConverter}}"></PathIcon>
|
||||
</Button>
|
||||
<Button Classes="Flat">
|
||||
<PathIcon Classes="FlatButtonIcon" Data="{StaticResource SemiIconSetting}"></PathIcon>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout Placement="Top">
|
||||
<RadioButton Content="Compact" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" GroupName="Density" />
|
||||
<RadioButton Content="Comfortable" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" GroupName="Density" />
|
||||
<RadioButton Content="Cozy" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" GroupName="Density" />
|
||||
</MenuFlyout>
|
||||
<Flyout Placement="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Density"></TextBlock>
|
||||
<RadioButton Content="Compact" Margin="10" HorizontalAlignment="Stretch" GroupName="Density" />
|
||||
<RadioButton Content="Comfortable" Margin="10" HorizontalAlignment="Stretch" GroupName="Density" />
|
||||
<RadioButton Content="Cozy" Margin="10" HorizontalAlignment="Stretch" GroupName="Density" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
@@ -45,4 +45,16 @@ public partial class PlaybackBar : UserControl
|
||||
_viewModel.CurrentPosition = slider.Value;
|
||||
_viewModel.IsPositionChangeInProgress = false;
|
||||
}
|
||||
|
||||
private void VolumeSlider_PointerWheelChanged(object? sender, PointerWheelEventArgs e)
|
||||
{
|
||||
double mouseWheelDelta = e.Delta.Y;
|
||||
|
||||
if (mouseWheelDelta == 0)
|
||||
return;
|
||||
|
||||
double delta = mouseWheelDelta > 0 ? .02 : -.02;
|
||||
|
||||
_viewModel.Volume += delta;
|
||||
}
|
||||
}
|
||||
@@ -3,25 +3,36 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:Harmonia.UI.ViewModels"
|
||||
xmlns:views="clr-namespace:Harmonia.UI.Views"
|
||||
xmlns:converter="clr-namespace:Harmonia.UI.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
DataContext="{x:Static vm:ViewModelLocator.PlayingSongInfoViewModel}"
|
||||
x:Class="Harmonia.UI.Views.PlayingSongInfo"
|
||||
x:DataType="vm:PlayingSongInfoViewModel">
|
||||
<Grid>
|
||||
<Image Source="{Binding SongImageSource, Mode=OneWay}" Stretch="UniformToFill" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Image.Effect>
|
||||
<BlurEffect Radius="20" />
|
||||
</Image.Effect>
|
||||
<Image.RenderTransform>
|
||||
<ScaleTransform ScaleX="1.05" ScaleY="1.05" />
|
||||
</Image.RenderTransform>
|
||||
</Image>
|
||||
<Canvas Background="#99000000"></Canvas>
|
||||
<Image Source="{Binding SongImageSource}" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20">
|
||||
<Image.Effect>
|
||||
<DropShadowEffect BlurRadius="10" Opacity=".4" OffsetX="3" OffsetY="3" />
|
||||
</Image.Effect>
|
||||
</Image>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
<ColumnDefinition></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.ColumnSpan="2">
|
||||
<Image Source="{Binding SongImageSource, Mode=OneWay}" Stretch="UniformToFill" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Image.Effect>
|
||||
<BlurEffect Radius="20" />
|
||||
</Image.Effect>
|
||||
<Image.RenderTransform>
|
||||
<ScaleTransform ScaleX="1.05" ScaleY="1.05" />
|
||||
</Image.RenderTransform>
|
||||
</Image>
|
||||
<Canvas Background="#99000000"></Canvas>
|
||||
</Grid>
|
||||
<Grid Grid.Column="0">
|
||||
<Image Source="{Binding SongImageSource}" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20">
|
||||
<Image.Effect>
|
||||
<DropShadowEffect BlurRadius="10" Opacity=".4" OffsetX="3" OffsetY="3" />
|
||||
</Image.Effect>
|
||||
</Image>
|
||||
</Grid>
|
||||
<views:PlaylistView Grid.Column="1"></views:PlaylistView>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
|
||||
93
Harmonia.UI/Views/PlaylistView.axaml
Normal file
93
Harmonia.UI/Views/PlaylistView.axaml
Normal file
@@ -0,0 +1,93 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:Harmonia.UI.ViewModels"
|
||||
xmlns:converter="clr-namespace:Harmonia.UI.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
DataContext="{x:Static vm:ViewModelLocator.PlaylistViewModel}"
|
||||
x:Class="Harmonia.UI.Views.PlaylistView"
|
||||
x:DataType="vm:PlaylistViewModel">
|
||||
<UserControl.Resources>
|
||||
<SolidColorBrush x:Key="SongItemTitleBrush" Color="#dddddd"/>
|
||||
<SolidColorBrush x:Key="SongItemSubtitleBrush" Color="#aaaaaa"/>
|
||||
</UserControl.Resources>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="Border.SongImage">
|
||||
<Setter Property="Width" Value="60"/>
|
||||
<Setter Property="Height" Value="60"/>
|
||||
<Setter Property="Margin" Value="0 0 10 0"/>
|
||||
</Style>
|
||||
<Style Selector="TextBlock.SongTitle">
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource SongItemTitleBrush}"/>
|
||||
</Style>
|
||||
<Style Selector="TextBlock.SongSubtitle">
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource SongItemSubtitleBrush}"/>
|
||||
</Style>
|
||||
<Style Selector="TextBlock.SongMetaData">
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource SongItemTitleBrush}"/>
|
||||
</Style>
|
||||
<Style Selector="TextBlock.SongMetaData.Subtitle">
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource SongItemSubtitleBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Grid.Playing TextBlock">
|
||||
<Setter Property="Foreground" Value="#76b9ed"/>
|
||||
<!-- Light Blue for Highlight -->
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<Grid Margin="0">
|
||||
<Canvas Background="#99000000"></Canvas> <!-- Was 99 !-->
|
||||
<ListBox ItemsSource="{Binding PlaylistSongs}" DoubleTapped="ListBox_DoubleTapped">
|
||||
<!--<ListBox.Styles>
|
||||
<Style Selector="ListBoxItem:nth-child(odd):not(:pointerover):not(:selected)">
|
||||
<Setter Property="Background" Value="#00000000"/>
|
||||
--><!-- Light Gray --><!--
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:nth-child(even):not(:pointerover):not(:selected)">
|
||||
<Setter Property="Background" Value="#22AAAAAA"/>
|
||||
--><!-- Slightly Darker Gray --><!--
|
||||
</Style>
|
||||
</ListBox.Styles>-->
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Classes.Playing="{Binding $parent[ListBox].((vm:PlaylistViewModel)DataContext).PlayingSong, Converter={StaticResource PlaylistSongEquality}, ConverterParameter={Binding .}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="70"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Classes="SongImage">
|
||||
<Grid>
|
||||
<Image Loaded="Image_Loaded" Unloaded="Image_Unloaded"></Image>
|
||||
<Canvas Background="#19000000"></Canvas>
|
||||
</Grid>
|
||||
</Border>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Song.Title, Mode=OneWay}" Classes="SongTitle" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Song.Artists, Mode=OneWay, Converter={StaticResource ArtistsToString}}" Classes="SongSubtitle" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Song.Album, Mode=OneWay}" Classes="SongSubtitle" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Song.Length.TotalSeconds, Mode=OneWay, Converter={StaticResource SecondsToString}}" Classes="SongMetaData" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding Song.FileType, Mode=OneWay}" Classes="SongMetaData Subtitle" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="2" Classes="SongMetaData Subtitle">
|
||||
<Run Text="{Binding Song.BitRate, Mode=OneWay}"></Run><Run Text=" kbps"></Run>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
91
Harmonia.UI/Views/PlaylistView.axaml.cs
Normal file
91
Harmonia.UI/Views/PlaylistView.axaml.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using Harmonia.Core.Caching;
|
||||
using Harmonia.Core.Imaging;
|
||||
using Harmonia.Core.Playlists;
|
||||
using Harmonia.UI.ViewModels;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Harmonia.UI.Views;
|
||||
|
||||
public partial class PlaylistView : UserControl
|
||||
{
|
||||
private readonly PlaylistViewModel _viewModel;
|
||||
private readonly ConcurrentDictionary<int, CancellationTokenSource> _imageCancellationTokens = [];
|
||||
|
||||
public PlaylistView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_viewModel = (PlaylistViewModel)DataContext!;
|
||||
}
|
||||
|
||||
private void ListBox_DoubleTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
if (sender is ListBox listBox && listBox.SelectedItem is PlaylistSong playlistSong)
|
||||
{
|
||||
_viewModel.PlaySong(playlistSong);
|
||||
}
|
||||
}
|
||||
|
||||
private void Image_Loaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not Image image)
|
||||
return;
|
||||
|
||||
if (image.DataContext is not PlaylistSong playlistSong)
|
||||
return;
|
||||
|
||||
Task.Run(() => DoSomethingAsync(image, playlistSong));
|
||||
}
|
||||
|
||||
private async Task DoSomethingAsync(Image image, PlaylistSong playlistSong)
|
||||
{
|
||||
int hashCode = image.GetHashCode();
|
||||
|
||||
_imageCancellationTokens.TryGetValue(hashCode, out CancellationTokenSource? cancellationTokenSource);
|
||||
cancellationTokenSource?.Cancel();
|
||||
|
||||
cancellationTokenSource = new();
|
||||
|
||||
Bitmap? bitmap = await _viewModel.GetBitmapAsync(playlistSong, cancellationTokenSource.Token);
|
||||
|
||||
if (bitmap == null)
|
||||
return;
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() => SetSongImageSource(image, bitmap));
|
||||
}
|
||||
|
||||
private static void SetSongImageSource(Image image, Bitmap bitmap)
|
||||
{
|
||||
image.Source = bitmap;
|
||||
}
|
||||
|
||||
private void Image_Unloaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not Image image)
|
||||
return;
|
||||
|
||||
if (image.DataContext is not PlaylistSong playlistSong)
|
||||
return;
|
||||
|
||||
if (image.Source is Bitmap bitmap)
|
||||
{
|
||||
bitmap.Dispose();
|
||||
}
|
||||
|
||||
image.Source = null;
|
||||
|
||||
int hashCode = image.GetHashCode();
|
||||
|
||||
_imageCancellationTokens.TryGetValue(hashCode, out CancellationTokenSource? cancellationTokenSource);
|
||||
cancellationTokenSource?.Cancel();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user