Redesigned Track Info

This commit is contained in:
Royce551 2021-06-26 23:01:42 -05:00
parent 697ae24fab
commit d6da528e14
7 changed files with 338 additions and 30 deletions

View file

@ -0,0 +1,63 @@
using Avalonia;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Skia;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FRESHMusicPlayer.Controls.BlurryImage
{
// Code taken from https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Rendering/BlurImageRender.cs;
// I just didn't want to depend on everything else Aura.UI has
public class BlurImageRender : DrawOperationBase, ICustomDrawOperation
{
public BlurImageRender(MemoryStream data, Rect src, Rect dest, float levelX, float levelY, IFormattedTextImpl noSkia) : base(src, noSkia)
{
_data = data;
_levelx = levelX;
_levely = levelY;
_dest = dest;
}
private MemoryStream _data;
private Rect _dest;
private float _levelx;
private float _levely;
public override void Render(IDrawingContextImpl drw_context)
{
base.Render(drw_context);
if (drw_context is ISkiaDrawingContextImpl context)
{
var canvas = context.SkCanvas;
if (_data == null)
return;
using (var bitmap = SKBitmap.Decode(_data.ToArray()))
using (var paint = new SKPaint())
{
if (bitmap == null)
{
Debug.WriteLine("bitmap is null");
return;
}
var img = SKImage.FromBitmap(bitmap);
paint.ImageFilter = SKImageFilter.CreateBlur(_levelx, _levely);
canvas.DrawImage(img, _dest.ToSKRect(), Bounds.ToSKRect(), paint);
}
}
}
}
}

View file

@ -0,0 +1,120 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FRESHMusicPlayer.Controls.BlurryImage
{
// Code taken from https://github.com/PieroCastillo/Aura.UI/blob/master/src/Aura.UI/Controls/BlurryImage/BlurryImage.cs;
// I just didn't want to depend on everything else Aura.UI has
public class BlurryImage : Control
{
static BlurryImage()
{
AffectsRender<BlurryImage>(BlurLevelProperty, SourceProperty, StretchDirectionProperty, StretchProperty);
AffectsMeasure<BlurryImage>(BlurLevelProperty, SourceProperty, StretchDirectionProperty, StretchProperty);
AffectsArrange<BlurryImage>(BlurLevelProperty, SourceProperty, StretchDirectionProperty, StretchProperty);
ClipToBoundsProperty.OverrideDefaultValue<BlurryImage>(true);
}
public override void Render(DrawingContext context)
{
var source = Source;
var mem = new MemoryStream();
Source.Save(mem);
if (source != null && mem.Length > 0 && Bounds.Width > 0 && Bounds.Height > 0)
{
Rect viewPort = new Rect(Bounds.Size);
Size sourceSize = source.Size;
Vector scale = Stretch.CalculateScaling(Bounds.Size, sourceSize, StretchDirection);
Size scaledSize = sourceSize * scale;
Rect destRect = viewPort
.CenterRect(new Rect(scaledSize))
.Intersect(viewPort);
Rect sourceRect = new Rect(sourceSize)
.CenterRect(new Rect(destRect.Size / scale));
var interpolationMode = RenderOptions.GetBitmapInterpolationMode(this);
context.Custom(new BlurImageRender(mem, destRect, sourceRect, BlurLevel, BlurLevel, null));
// Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
}
}
///<inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
var source = Source;
var result = new Size();
if (source != null)
{
result = Stretch.CalculateSize(availableSize, source.Size, StretchDirection);
}
return result;
}
/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
var source = Source;
if (source != null)
{
var sourceSize = source.Size;
var result = Stretch.CalculateSize(finalSize, sourceSize);
return result;
}
else
{
return new Size();
}
}
public float BlurLevel
{
get => GetValue(BlurLevelProperty);
set => SetValue(BlurLevelProperty, value);
}
public IBitmap Source
{
get => GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
public Stretch Stretch
{
get => GetValue(StretchProperty);
set => SetValue(StretchProperty, value);
}
public StretchDirection StretchDirection
{
get => GetValue(StretchDirectionProperty);
set => SetValue(StretchDirectionProperty, value);
}
public readonly static StyledProperty<IBitmap> SourceProperty =
AvaloniaProperty.Register<BlurryImage, IBitmap>(nameof(Source));
public readonly static StyledProperty<Stretch> StretchProperty =
Image.StretchProperty.AddOwner<BlurryImage>();
public readonly static StyledProperty<StretchDirection> StretchDirectionProperty =
Image.StretchDirectionProperty.AddOwner<BlurryImage>();
public readonly static StyledProperty<float> BlurLevelProperty =
AvaloniaProperty.Register<BlurryImage, float>(nameof(BlurLevel), 16);
}
}

View file

@ -0,0 +1,49 @@
using Avalonia;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Skia;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FRESHMusicPlayer.Controls.BlurryImage
{
public abstract class DrawOperationBase : ICustomDrawOperation
{
public DrawOperationBase(Rect bounds, IFormattedTextImpl noSkia)
{
Bounds = bounds;
NoSkia = noSkia;
}
public virtual Rect Bounds { get; private set; }
public virtual IFormattedTextImpl NoSkia { get; private set; }
public virtual void Dispose()
{
// do nothing
}
public virtual bool Equals(ICustomDrawOperation other) => false;
public virtual bool HitTest(Point p) => true;
public virtual void Render(IDrawingContextImpl context)
{
}
protected virtual bool CheckSkia(IDrawingContextImpl context)
{
if (context is ISkiaDrawingContextImpl)
{
return true;
}
else
{
return false;
}
}
}
}

View file

@ -21,6 +21,8 @@ namespace FRESHMusicPlayer.ViewModels
public ObservableCollection<DisplayLanguage> AvailableLanguages { get; } = new()
{
new("Automatic", "automatic"),
new("English", "en"),
new("German", "de"),
new("Vietnamese", "vi")
};

View file

@ -26,30 +26,58 @@ namespace FRESHMusicPlayer.ViewModels
get => bitrateText;
set => this.RaiseAndSetIfChanged(ref bitrateText, value);
}
private string discNumberText;
public string DiscNumberText
{
get => discNumberText;
set => this.RaiseAndSetIfChanged(ref discNumberText, value);
}
private bool discNumberShouldBeVisible = true;
public bool DiscNumberShouldBeVisible
{
get => discNumberShouldBeVisible;
set => this.RaiseAndSetIfChanged(ref discNumberShouldBeVisible, value);
}
private string trackNumberText;
public string TrackNumberText
{
get => trackNumberText;
set => this.RaiseAndSetIfChanged(ref trackNumberText, value);
}
private bool trackNumberShouldBeVisible = true;
public bool TrackNumberShouldBeVisible
{
get => trackNumberShouldBeVisible;
set => this.RaiseAndSetIfChanged(ref trackNumberShouldBeVisible, value);
}
private string yearText;
public string YearText
{
get => yearText;
set => this.RaiseAndSetIfChanged(ref yearText, value);
}
private bool yearShouldBeVisible = true;
public bool YearShouldBeVisible
{
get => yearShouldBeVisible;
set => this.RaiseAndSetIfChanged(ref yearShouldBeVisible, value);
}
private string albumText;
public string AlbumText
{
get => albumText;
set => this.RaiseAndSetIfChanged(ref albumText, value);
}
private bool albumShouldBeVisible = true;
public bool AlbumShouldBeVisible
{
get => albumShouldBeVisible;
set => this.RaiseAndSetIfChanged(ref albumShouldBeVisible, value);
}
public TrackInfoViewModel()
{
@ -70,20 +98,26 @@ namespace FRESHMusicPlayer.ViewModels
{
var track = new Track(Player.FilePath);
if (track.EmbeddedPictures.Count > 0)
CoverArt = new Bitmap(new MemoryStream(track.EmbeddedPictures[0].PictureData));
else
CoverArt = null;
CoverArt = track.EmbeddedPictures.Count <= 0 ? null : new Bitmap(new MemoryStream(track.EmbeddedPictures[0].PictureData));
BitrateText = track.Bitrate == 0 ? null : $"Bitrate - {track.Bitrate}kbps {track.SampleRate / 1000}kHz";
DiscNumberShouldBeVisible = true;
TrackNumberShouldBeVisible = true;
YearShouldBeVisible = true;
AlbumShouldBeVisible = true;
DiscNumberText = track.DiscNumber == 0 ? null : $"Disc {track.DiscNumber}";
if (track.DiscTotal > 0) DiscNumberText = $"Disc {track.DiscNumber}/{track.DiscTotal}";
TrackNumberText = track.TrackNumber == 0 ? null : $"Track {track.TrackNumber}";
if (track.TrackTotal > 0) TrackNumberText = $"Track {track.TrackNumber}/{track.TrackTotal}";
BitrateText = $"{track.Bitrate}kbps {track.SampleRate / 1000}kHz";
YearText = track.Year == 0 ? null : $"Year {track.Year}";
AlbumText = track.Album is null ? null : $"Album - {track.Album}";
if (track.DiscNumber == 0) DiscNumberShouldBeVisible = false;
else DiscNumberText = track.DiscTotal <= 0 ? track.DiscNumber.ToString() : $"{track.DiscNumber}/{track.DiscTotal}";
if (track.TrackNumber == 0) TrackNumberShouldBeVisible = false;
else TrackNumberText = track.TrackTotal <= 0 ? track.TrackNumber.ToString() : $"{track.TrackNumber}/{track.TrackTotal}";
if (track.Year == 0) YearShouldBeVisible = false;
else YearText = track.Year.ToString();
if (track.Album is null) AlbumShouldBeVisible = false;
else AlbumText = track.Album;
}
private void Player_SongChanged(object sender, EventArgs e) => Update();

View file

@ -9,6 +9,7 @@ using System;
using System.Linq;
using System.ComponentModel;
using System.Runtime.InteropServices;
using FRESHMusicPlayer.Utilities;
namespace FRESHMusicPlayer.Views
{
@ -122,6 +123,20 @@ namespace FRESHMusicPlayer.Views
ViewModel.PlayPauseCommand();
break;
}
switch (e.Key)
{
case Key.F1:
InterfaceUtils.OpenURL("https://royce551.github.io/FRESHMusicPlayer/docs/index.html");
break;
case Key.F2:
GC.Collect(2);
break;
case Key.F3:
throw new Exception("Exception for debugging");
case Key.F4:
Topmost = !Topmost;
break;
}
}
private void OnDragEnter(object sender, DragEventArgs e)

View file

@ -2,6 +2,7 @@
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:controls="clr-namespace:FRESHMusicPlayer.Controls.BlurryImage"
xmlns:vm="using:FRESHMusicPlayer.ViewModels"
mc:Ignorable="d" Width="500" Height="300"
x:Class="FRESHMusicPlayer.Views.TrackInfo" WindowStartupLocation="CenterOwner" Closing="OnClosing"
@ -10,26 +11,50 @@
<vm:TrackInfoViewModel/>
</Window.DataContext>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<Panel>
<controls:BlurryImage ZIndex="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Source="{Binding CoverArt}" BlurLevel="10" Stretch="UniformToFill" Margin="-10"/>
<Rectangle ZIndex="1" Fill="{StaticResource BackgroundColor}" Opacity="0.55"/>
<Grid ZIndex="2" Margin="10" HorizontalAlignment="Left" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.ColumnSpan="2" IsChecked="{Binding $parent[Window].Topmost}" Content="Pin to top"/>
<CheckBox Grid.ColumnSpan="3" IsChecked="{Binding $parent[Window].Topmost}" Content="Pin to top"/>
<Image Grid.Row="1" Grid.Column="0" Source="{Binding CoverArt}"/>
<Image Grid.Row="1" Grid.Column="0" Source="{Binding CoverArt}" HorizontalAlignment="Right">
</Image>
<StackPanel Grid.Row="1" Grid.Column="1" Margin="10,0,0,0" VerticalAlignment="Center" Spacing="5">
<TextBlock Text="{Binding BitrateText}" TextWrapping="Wrap"/>
<TextBlock Text="{Binding DiscNumberText}" TextWrapping="Wrap"/>
<TextBlock Text="{Binding TrackNumberText}" TextWrapping="Wrap"/>
<TextBlock Text="{Binding YearText}" TextWrapping="Wrap"/>
<TextBlock Text="{Binding AlbumText}" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
<Grid Grid.Row="1" Grid.Column="1" Margin="10,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Margin="0,2" Text="Bitrate" Foreground="{StaticResource SecondaryTextColor}"/>
<TextBlock Grid.Column="0" Grid.Row="1" Margin="0,2" Text="Disc" Foreground="{StaticResource SecondaryTextColor}" IsVisible="{Binding DiscNumberShouldBeVisible}"/>
<TextBlock Grid.Column="0" Grid.Row="2" Margin="0,2" Text="Track" Foreground="{StaticResource SecondaryTextColor}" IsVisible="{Binding TrackNumberShouldBeVisible}"/>
<TextBlock Grid.Column="0" Grid.Row="3" Margin="0,2" Text="Year" Foreground="{StaticResource SecondaryTextColor}" IsVisible="{Binding YearShouldBeVisible}"/>
<TextBlock Grid.Column="0" Grid.Row="4" Margin="0,2" Text="Album" Foreground="{StaticResource SecondaryTextColor}" IsVisible="{Binding AlbumShouldBeVisible}"/>
<TextBlock Grid.Column="1" Grid.Row="0" Margin="10,2,0,2" Text="{Binding BitrateText}"/>
<TextBlock Grid.Column="1" Grid.Row="1" Margin="10,2,0,2" Text="{Binding DiscNumberText}" IsVisible="{Binding DiscNumberShouldBeVisible}"/>
<TextBlock Grid.Column="1" Grid.Row="2" Margin="10,2,0,2" Text="{Binding TrackNumberText}" IsVisible="{Binding TrackNumberShouldBeVisible}"/>
<TextBlock Grid.Column="1" Grid.Row="3" Margin="10,2,0,2" Text="{Binding YearText}" IsVisible="{Binding YearShouldBeVisible}"/>
<TextBlock Grid.Column="1" Grid.Row="4" Margin="10,2,0,2" Text="{Binding AlbumText}" IsVisible="{Binding AlbumShouldBeVisible}"/>
</Grid>
</Grid>
</Panel>
</Window>