333 lines
9.4 KiB
C#
333 lines
9.4 KiB
C#
using Avalonia;
|
|
using Avalonia.Animation;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Presenters;
|
|
using Avalonia.Input;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Media.Imaging;
|
|
using Avalonia.Threading;
|
|
using Avalonia.VisualTree;
|
|
using Harmonia.Core.Playlists;
|
|
using Harmonia.UI.ViewModels;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
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!;
|
|
_viewModel.PropertyChanging += OnViewModelPropertyChanging;
|
|
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
|
|
}
|
|
|
|
private async void OnBeforeFilteredSongsUpdate(object? sender, System.EventArgs e)
|
|
{
|
|
await Dispatcher.UIThread.InvokeAsync(SlideOutSongs);
|
|
}
|
|
|
|
private async void OnAfterFilteredSongsUpdate(object? sender, System.EventArgs e)
|
|
{
|
|
await Dispatcher.UIThread.InvokeAsync(SlideInSongs);
|
|
}
|
|
|
|
private void OnViewModelPropertyChanging(object? sender, PropertyChangingEventArgs e)
|
|
{
|
|
switch (e.PropertyName)
|
|
{
|
|
case nameof(_viewModel.PlayingSong):
|
|
RemovePlayingSongClass();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private async void OnViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
{
|
|
switch (e.PropertyName)
|
|
{
|
|
case nameof(_viewModel.FilteredPlaylistSongs):
|
|
await Dispatcher.UIThread.InvokeAsync(SlideInSongs);
|
|
break;
|
|
case nameof(_viewModel.PlayingSong):
|
|
AddPlayingSongClass();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private async Task SlideOutSongs()
|
|
{
|
|
await Animations.Animations.SlideToRight(100, duration: 300).RunAsync(PlaylistListBox);
|
|
}
|
|
|
|
private async Task SlideInSongs()
|
|
{
|
|
await Animations.Animations.SlideFromRight(100, duration: 300).RunAsync(PlaylistListBox);
|
|
}
|
|
|
|
private void RemovePlayingSongClass()
|
|
{
|
|
PlaylistSong? playingSong = _viewModel.PlayingSong;
|
|
|
|
if (playingSong == null)
|
|
return;
|
|
|
|
Control? control = PlaylistListBox.ContainerFromItem(playingSong);
|
|
|
|
if (control is not ListBoxItem listBoxItem)
|
|
return;
|
|
|
|
if (listBoxItem.GetVisualChildren().FirstOrDefault() is not ContentPresenter contentPresentor)
|
|
return;
|
|
|
|
if (contentPresentor.GetVisualChildren().FirstOrDefault() is not Grid grid)
|
|
return;
|
|
|
|
grid.Classes.Remove("Playing");
|
|
}
|
|
|
|
private void AddPlayingSongClass()
|
|
{
|
|
PlaylistSong? playingSong = _viewModel.PlayingSong;
|
|
|
|
if (playingSong == null)
|
|
return;
|
|
|
|
PlaylistListBox.UpdateLayout();
|
|
PlaylistListBox.ScrollIntoView(playingSong);
|
|
|
|
Control? control = PlaylistListBox.ContainerFromItem(playingSong);
|
|
|
|
if (control is ListBoxItem listBoxItem)
|
|
{
|
|
listBoxItem.BringIntoView();
|
|
}
|
|
else
|
|
{
|
|
PlaylistListBox.ScrollIntoView(playingSong);
|
|
}
|
|
|
|
if (control is not ListBoxItem listBoxItem2)
|
|
return;
|
|
|
|
if (listBoxItem2.GetVisualChildren().FirstOrDefault() is not ContentPresenter contentPresentor)
|
|
return;
|
|
|
|
if (contentPresentor.GetVisualChildren().FirstOrDefault() is not Grid grid)
|
|
return;
|
|
|
|
grid.Classes.Add("Playing");
|
|
}
|
|
|
|
private async void ListBox_DoubleTapped(object? sender, TappedEventArgs e)
|
|
{
|
|
if (sender is ListBox listBox && listBox.SelectedItem is PlaylistSong playlistSong)
|
|
{
|
|
await _viewModel.PlaySongAsync(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)
|
|
return;
|
|
|
|
if (image.Source is Bitmap bitmap)
|
|
{
|
|
bitmap.Dispose();
|
|
}
|
|
|
|
image.Source = null;
|
|
|
|
int hashCode = image.GetHashCode();
|
|
|
|
_imageCancellationTokens.TryGetValue(hashCode, out CancellationTokenSource? cancellationTokenSource);
|
|
cancellationTokenSource?.Cancel();
|
|
}
|
|
|
|
private void Grid_Loaded(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is not Grid grid)
|
|
return;
|
|
|
|
if (grid.Parent is not ListBoxItem listBoxItem)
|
|
return;
|
|
|
|
//SetupDragAndDrop(grid, d => d.Set(DataFormats.Text,
|
|
// $"Text was dragged x times."),
|
|
// DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
|
|
|
|
if (PlaylistListBox.ItemFromContainer(listBoxItem) is not PlaylistSong playlistSong)
|
|
return;
|
|
|
|
if (playlistSong == _viewModel.PlayingSong)
|
|
grid.Classes.Add("Playing");
|
|
|
|
//await Dispatcher.UIThread.InvokeAsync(() => SlideInPlaylistSong(sender));
|
|
}
|
|
|
|
private async Task SlideOutPlaylistSong(object sender)
|
|
{
|
|
if (sender is not Animatable animatable)
|
|
return;
|
|
|
|
await Animations.Animations.SlideToRight(100, duration: 300).RunAsync(animatable);
|
|
}
|
|
|
|
private async Task SlideInPlaylistSong(object sender)
|
|
{
|
|
if (sender is not Animatable animatable)
|
|
return;
|
|
|
|
await Animations.Animations.SlideFromRight(100, duration: 300).RunAsync(animatable);
|
|
}
|
|
|
|
private void OnPlaylistListBoxDrop(object? sender, DragEventArgs e)
|
|
{
|
|
|
|
}
|
|
|
|
//private void SetupDragAndDrop(IInputElement draggable, Action<DataObject> factory, DragDropEffects effects)
|
|
//{
|
|
// draggable.PointerPressed += (sender, e) => StartDrag(sender, factory, e, effects);
|
|
|
|
// AddHandler(DragDrop.DropEvent, Drop);
|
|
// AddHandler(DragDrop.DragOverEvent, DragOver);
|
|
// AddHandler(DragDrop.DragEnterEvent, DragEnter);
|
|
// AddHandler(DragDrop.DragLeaveEvent, DragLeave);
|
|
//}
|
|
|
|
private async void StartDrag(object? sender, Action<DataObject> factory, PointerPressedEventArgs e, DragDropEffects effects)
|
|
{
|
|
if (e.GetCurrentPoint((Visual)sender!).Properties.IsLeftButtonPressed == false)
|
|
{
|
|
// It was a left click
|
|
Console.WriteLine("Left mouse button pressed!");
|
|
e.Handled = true;
|
|
return;
|
|
}
|
|
|
|
var dragData = new DataObject();
|
|
factory(dragData);
|
|
|
|
var result = await DragDrop.DoDragDrop(e, dragData, effects);
|
|
}
|
|
|
|
private void DragOver(object? sender, DragEventArgs e)
|
|
{
|
|
if (e.Source == PlaylistListBox)
|
|
return;
|
|
|
|
//if (e.Source is Animatable animatable)
|
|
//{
|
|
// //await Animations.Animations.SlideToRight(200).RunAsync(animatable);
|
|
// animatable.P
|
|
//}
|
|
|
|
//if (e.Source is Grid grid)
|
|
//{
|
|
// grid.Opacity = 0.5;
|
|
//}
|
|
|
|
//if (Equals(e.Source, MoveTarget))
|
|
//{
|
|
// e.DragEffects &= DragDropEffects.Move;
|
|
//}
|
|
//else
|
|
//{
|
|
// e.DragEffects &= DragDropEffects.Copy;
|
|
//}
|
|
|
|
//// Only allow if the dragged data contains text or filenames.
|
|
//if (!e.Data.Contains(DataFormats.Text)
|
|
// && !e.Data.Contains(DataFormats.Files)
|
|
// && !e.Data.Contains(CustomFormat))
|
|
// e.DragEffects = DragDropEffects.None;
|
|
}
|
|
|
|
private void DragEnter(object? sender, DragEventArgs e)
|
|
{
|
|
if (e.Source is ListBoxItem grid)
|
|
{
|
|
grid.Opacity = 0.5;
|
|
//Animations.Animations.SlideToRight(200).RunAsync(grid);
|
|
}
|
|
}
|
|
|
|
private void DragLeave(object? sender, DragEventArgs e)
|
|
{
|
|
if (e.Source is ListBoxItem grid)
|
|
{
|
|
grid.Opacity = 1;
|
|
//Animations.Animations.SlideToRight(200).RunAsync(grid);
|
|
}
|
|
}
|
|
|
|
private void Drop(object? sender, DragEventArgs e)
|
|
{
|
|
//if (Equals(e.Source, MoveTarget))
|
|
//{
|
|
// e.DragEffects &= DragDropEffects.Move;
|
|
//}
|
|
//else
|
|
//{
|
|
// e.DragEffects &= DragDropEffects.Copy;
|
|
//}
|
|
|
|
//if (e.Data.Contains(DataFormats.Text))
|
|
//{
|
|
// DropState.Text = e.Data.GetText();
|
|
//}
|
|
|
|
//else if (e.Data.Contains(CustomFormat))
|
|
//{
|
|
// DropState.Text = e.Data.Get(CustomFormat)?.ToString();
|
|
//}
|
|
}
|
|
} |