Added AvaloniaUI project.
This commit is contained in:
18
Harmonia.UI/App.axaml
Normal file
18
Harmonia.UI/App.axaml
Normal file
@@ -0,0 +1,18 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="clr-namespace:Harmonia.UI.ViewModels"
|
||||
x:Class="Harmonia.UI.App"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
</Application.Styles>
|
||||
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<vm:ViewModelLocator x:Key="Locator" />
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
|
||||
</Application>
|
||||
62
Harmonia.UI/App.axaml.cs
Normal file
62
Harmonia.UI/App.axaml.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Data.Core.Plugins;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Harmonia.Core.Extensions;
|
||||
using Harmonia.UI.ViewModels;
|
||||
using Harmonia.UI.Views;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
namespace Harmonia.UI;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public static IServiceProvider ServiceProvider { get; private set; }
|
||||
|
||||
static App()
|
||||
{
|
||||
ServiceCollection services = new();
|
||||
|
||||
services.AddSingleton<MainViewModel>();
|
||||
services.AddSingleton<MainWindow>();
|
||||
services.AddSingleton<PlaybackBarViewModel>();
|
||||
|
||||
services.AddHarmonia();
|
||||
|
||||
ServiceProvider = services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
// Line below is needed to remove Avalonia data validation.
|
||||
// Without this line you will get duplicate validations from both Avalonia and CT
|
||||
BindingPlugins.DataValidators.RemoveAt(0);
|
||||
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
//desktop.MainWindow = new MainWindow
|
||||
//{
|
||||
// DataContext = new MainViewModel()
|
||||
//};
|
||||
|
||||
desktop.MainWindow = ServiceProvider.GetRequiredService<MainWindow>();
|
||||
}
|
||||
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
|
||||
{
|
||||
//singleViewPlatform.MainView = new MainView
|
||||
//{
|
||||
// DataContext = new MainViewModel()
|
||||
//};
|
||||
|
||||
singleViewPlatform.MainView = ServiceProvider.GetRequiredService<MainWindow>();
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
}
|
||||
BIN
Harmonia.UI/Assets/avalonia-logo.ico
Normal file
BIN
Harmonia.UI/Assets/avalonia-logo.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
33
Harmonia.UI/Harmonia.UI.csproj
Normal file
33
Harmonia.UI/Harmonia.UI.csproj
Normal file
@@ -0,0 +1,33 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.0" />
|
||||
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Harmonia.Core\Harmonia.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Views\PlaybackBar.axaml.cs">
|
||||
<DependentUpon>PlaybackBar.axaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
6
Harmonia.UI/ViewModels/MainViewModel.cs
Normal file
6
Harmonia.UI/ViewModels/MainViewModel.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Harmonia.UI.ViewModels;
|
||||
|
||||
public partial class MainViewModel : ViewModelBase
|
||||
{
|
||||
public string Greeting => "Welcome to Avalonia!";
|
||||
}
|
||||
178
Harmonia.UI/ViewModels/PlaybackBarViewModel.cs
Normal file
178
Harmonia.UI/ViewModels/PlaybackBarViewModel.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using Harmonia.Core.Caching;
|
||||
using Harmonia.Core.Imaging;
|
||||
using Harmonia.Core.Models;
|
||||
using Harmonia.Core.Player;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Harmonia.UI.ViewModels;
|
||||
|
||||
public partial class PlaybackBarViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IAudioPlayer _audioPlayer;
|
||||
private readonly IAudioImageCache _audioImageCache;
|
||||
|
||||
private bool _isPositionChangeInProgress;
|
||||
private CancellationTokenSource? _audioImageCancellationTokenSource;
|
||||
|
||||
private Song? _song;
|
||||
public Song? Song
|
||||
{
|
||||
get
|
||||
{
|
||||
return _song;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_song = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
CurrentPosition = 0; // What if player is being loaded and returning to save state?
|
||||
Position = 0;
|
||||
MaxPosition = value?.Length.TotalSeconds ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap? _songImageSource;
|
||||
public Bitmap? SongImageSource
|
||||
{
|
||||
get
|
||||
{
|
||||
return _songImageSource;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_songImageSource = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private double _currentPosition;
|
||||
public double CurrentPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentPosition;
|
||||
}
|
||||
set
|
||||
{
|
||||
_currentPosition = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
if (_isPositionChangeInProgress)
|
||||
_audioPlayer.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
private double _position;
|
||||
public double Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return _position;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_position = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private double _maxPosition;
|
||||
public double MaxPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return _maxPosition;
|
||||
}
|
||||
set
|
||||
{
|
||||
_maxPosition = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRandom
|
||||
{
|
||||
get
|
||||
{
|
||||
return _audioPlayer.IsRandom;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_audioPlayer.IsRandom = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Greeting => "Welcome to Harmonia!";
|
||||
|
||||
public PlaybackBarViewModel(IAudioPlayer audioPlayer, IAudioImageCache audioImageCache)
|
||||
{
|
||||
_audioPlayer = audioPlayer;
|
||||
_audioPlayer.PlayingSongChanged += OnAudioPlayerPlayingSongChanged;
|
||||
|
||||
_audioImageCache = audioImageCache;
|
||||
}
|
||||
|
||||
private void OnAudioPlayerPlayingSongChanged(object? sender, EventArgs e)
|
||||
{
|
||||
Song = _audioPlayer.PlayingSong?.Song;
|
||||
Task.Run(UpdateImage);
|
||||
}
|
||||
|
||||
private async Task UpdateImage()
|
||||
{
|
||||
// TODO: Show default picture
|
||||
if (Song == null)
|
||||
return;
|
||||
|
||||
if (_audioImageCancellationTokenSource != null)
|
||||
await _audioImageCancellationTokenSource.CancelAsync();
|
||||
|
||||
_audioImageCancellationTokenSource = new();
|
||||
CancellationToken cancellationToken = _audioImageCancellationTokenSource.Token;
|
||||
|
||||
SongPictureInfo? songPictureInfo = await _audioImageCache.GetAsync(Song, cancellationToken);
|
||||
|
||||
if (songPictureInfo == null)
|
||||
return;
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() => SetSongImageSource(songPictureInfo));
|
||||
}
|
||||
|
||||
private void SetSongImageSource(SongPictureInfo songPictureInfo)
|
||||
{
|
||||
SongImageSource = new(songPictureInfo.Stream);
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
_audioPlayer.Play();
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
_audioPlayer.Pause();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_audioPlayer.Stop();
|
||||
CurrentPosition = 0;
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
public void Previous()
|
||||
{
|
||||
_audioPlayer.PreviousAsync();
|
||||
}
|
||||
|
||||
public void Next()
|
||||
{
|
||||
_audioPlayer.NextAsync();
|
||||
}
|
||||
}
|
||||
7
Harmonia.UI/ViewModels/ViewModelBase.cs
Normal file
7
Harmonia.UI/ViewModels/ViewModelBase.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Harmonia.UI.ViewModels;
|
||||
|
||||
public class ViewModelBase : ObservableObject
|
||||
{
|
||||
}
|
||||
12
Harmonia.UI/ViewModels/ViewModelLocator.cs
Normal file
12
Harmonia.UI/ViewModels/ViewModelLocator.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Harmonia.UI.ViewModels;
|
||||
|
||||
public class ViewModelLocator
|
||||
{
|
||||
public static MainViewModel MainViewModel
|
||||
=> App.ServiceProvider.GetRequiredService<MainViewModel>();
|
||||
|
||||
public static PlaybackBarViewModel PlaybackBarViewModel
|
||||
=> App.ServiceProvider.GetRequiredService<PlaybackBarViewModel>();
|
||||
}
|
||||
22
Harmonia.UI/Views/MainView.axaml
Normal file
22
Harmonia.UI/Views/MainView.axaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<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:views="clr-namespace:Harmonia.UI.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
DataContext="{x:Static vm:ViewModelLocator.MainViewModel}"
|
||||
x:Class="Harmonia.UI.Views.MainView"
|
||||
x:DataType="vm:MainViewModel">
|
||||
<Design.DataContext>
|
||||
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||
<vm:MainViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<views:PlaybackBar></views:PlaybackBar>
|
||||
</StackPanel>
|
||||
|
||||
</UserControl>
|
||||
11
Harmonia.UI/Views/MainView.axaml.cs
Normal file
11
Harmonia.UI/Views/MainView.axaml.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Harmonia.UI.Views;
|
||||
|
||||
public partial class MainView : UserControl
|
||||
{
|
||||
public MainView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
12
Harmonia.UI/Views/MainWindow.axaml
Normal file
12
Harmonia.UI/Views/MainWindow.axaml
Normal file
@@ -0,0 +1,12 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:Harmonia.UI.ViewModels"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:views="clr-namespace:Harmonia.UI.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Harmonia.UI.Views.MainWindow"
|
||||
Icon="/Assets/avalonia-logo.ico"
|
||||
Title="Harmonia.UI">
|
||||
<views:MainView />
|
||||
</Window>
|
||||
11
Harmonia.UI/Views/MainWindow.axaml.cs
Normal file
11
Harmonia.UI/Views/MainWindow.axaml.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Harmonia.UI.Views;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
17
Harmonia.UI/Views/PlaybackBar.axaml
Normal file
17
Harmonia.UI/Views/PlaybackBar.axaml
Normal file
@@ -0,0 +1,17 @@
|
||||
<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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
DataContext="{x:Static vm:ViewModelLocator.PlaybackBarViewModel}"
|
||||
x:Class="Harmonia.UI.Views.PlaybackBar"
|
||||
x:DataType="vm:PlaybackBarViewModel">
|
||||
<Design.DataContext>
|
||||
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||
<vm:MainViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</UserControl>
|
||||
11
Harmonia.UI/Views/PlaybackBar.axaml.cs
Normal file
11
Harmonia.UI/Views/PlaybackBar.axaml.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Harmonia.UI.Views;
|
||||
|
||||
public partial class PlaybackBar : UserControl
|
||||
{
|
||||
public PlaybackBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user