Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db6847b74f | ||
|
|
d510892650 | ||
|
|
a0e51d976e | ||
|
|
80ef86478b | ||
|
|
9736a4c9cc | ||
|
|
d01be4b883 | ||
|
|
b470b91d0e | ||
|
|
61f4e5706b | ||
|
|
6f2d3a3505 | ||
|
|
58b7cd2520 | ||
|
|
b2319e7983 | ||
|
|
a3b250dd46 |
@@ -40,15 +40,16 @@
|
|||||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||||
<AssetTargetFallback>net8.0-windows$(TargetPlatformVersion);$(AssetTargetFallback)</AssetTargetFallback>
|
<AssetTargetFallback>net8.0-windows$(TargetPlatformVersion);$(AssetTargetFallback)</AssetTargetFallback>
|
||||||
<DefaultLanguage>zh-CN</DefaultLanguage>
|
<DefaultLanguage>zh-CN</DefaultLanguage>
|
||||||
<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>
|
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
||||||
<EntryPointProjectUniqueName>..\BetterLyrics.WinUI3\BetterLyrics.WinUI3.csproj</EntryPointProjectUniqueName>
|
<EntryPointProjectUniqueName>..\BetterLyrics.WinUI3\BetterLyrics.WinUI3.csproj</EntryPointProjectUniqueName>
|
||||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||||
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
|
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
|
||||||
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
|
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
|
||||||
<GenerateTestArtifacts>True</GenerateTestArtifacts>
|
<GenerateTestArtifacts>True</GenerateTestArtifacts>
|
||||||
<AppxBundlePlatforms>x86|x64|arm64</AppxBundlePlatforms>
|
<AppxBundlePlatforms>x86|x64</AppxBundlePlatforms>
|
||||||
<GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate>
|
<GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate>
|
||||||
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
|
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
|
||||||
|
<PackageCertificateKeyFile>BetterLyrics.WinUI3 %28Package%29_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<AppxBundle>Always</AppxBundle>
|
<AppxBundle>Always</AppxBundle>
|
||||||
@@ -80,6 +81,7 @@
|
|||||||
</AppxManifest>
|
</AppxManifest>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Include="BetterLyrics.WinUI3 %28Package%29_TemporaryKey.pfx" />
|
||||||
<Content Include="Images\LargeTile.scale-100.png" />
|
<Content Include="Images\LargeTile.scale-100.png" />
|
||||||
<Content Include="Images\LargeTile.scale-125.png" />
|
<Content Include="Images\LargeTile.scale-125.png" />
|
||||||
<Content Include="Images\LargeTile.scale-150.png" />
|
<Content Include="Images\LargeTile.scale-150.png" />
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 824 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 824 B |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 531 B After Width: | Height: | Size: 599 B |
|
Before Width: | Height: | Size: 703 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 46 KiB |
@@ -5,12 +5,13 @@
|
|||||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||||
IgnorableNamespaces="uap rescap">
|
xmlns:uap18="http://schemas.microsoft.com/appx/manifest/uap/windows10/18"
|
||||||
|
IgnorableNamespaces="uap rescap uap18">
|
||||||
|
|
||||||
<Identity
|
<Identity
|
||||||
Name="37412.BetterLyrics"
|
Name="37412.BetterLyrics"
|
||||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||||
Version="1.0.1.0" />
|
Version="1.0.2.0" />
|
||||||
|
|
||||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||||
|
|
||||||
@@ -26,7 +27,9 @@
|
|||||||
</Dependencies>
|
</Dependencies>
|
||||||
|
|
||||||
<Resources>
|
<Resources>
|
||||||
<Resource Language="x-generate"/>
|
<Resource Language="en-US"/>
|
||||||
|
<Resource Language="zh-CN"/>
|
||||||
|
<Resource Language="zh-TW"/>
|
||||||
</Resources>
|
</Resources>
|
||||||
|
|
||||||
<Applications>
|
<Applications>
|
||||||
|
|||||||
@@ -16,12 +16,8 @@
|
|||||||
|
|
||||||
<!-- Theme -->
|
<!-- Theme -->
|
||||||
<ResourceDictionary.ThemeDictionaries>
|
<ResourceDictionary.ThemeDictionaries>
|
||||||
<ResourceDictionary x:Key="Light">
|
<ResourceDictionary x:Key="Light" />
|
||||||
<Color x:Key="SemiTransparentSystemBaseHighColor">#80000000</Color>
|
<ResourceDictionary x:Key="Dark" />
|
||||||
</ResourceDictionary>
|
|
||||||
<ResourceDictionary x:Key="Dark">
|
|
||||||
<Color x:Key="SemiTransparentSystemBaseHighColor">#80FFFFFF</Color>
|
|
||||||
</ResourceDictionary>
|
|
||||||
</ResourceDictionary.ThemeDictionaries>
|
</ResourceDictionary.ThemeDictionaries>
|
||||||
|
|
||||||
<!-- Brush -->
|
<!-- Brush -->
|
||||||
@@ -48,6 +44,7 @@
|
|||||||
<converter:ColorToBrushConverter x:Key="ColorToBrushConverter" />
|
<converter:ColorToBrushConverter x:Key="ColorToBrushConverter" />
|
||||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||||
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||||
|
<converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
|
||||||
|
|
||||||
<x:Double x:Key="SettingsCardSpacing">4</x:Double>
|
<x:Double x:Key="SettingsCardSpacing">4</x:Double>
|
||||||
|
|
||||||
@@ -60,6 +57,34 @@
|
|||||||
<Setter Property="Margin" Value="1,30,0,6" />
|
<Setter Property="Margin" Value="1,30,0,6" />
|
||||||
</Style.Setters>
|
</Style.Setters>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style x:Key="TitleBarButtonStyle" TargetType="Button">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="Padding" Value="16,0" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="GhostButtonStyle" TargetType="Button">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="TitleBarToggleButtonStyle" TargetType="ToggleButton">
|
||||||
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="Padding" Value="16,0" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="GhostToggleButtonStyle" TargetType="ToggleButton">
|
||||||
|
<Setter Property="CornerRadius" Value="4" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Dimensions -->
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
|
|||||||
@@ -1,63 +1,112 @@
|
|||||||
using BetterLyrics.WinUI3.Services.Database;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Services.Database;
|
||||||
using BetterLyrics.WinUI3.Services.Settings;
|
using BetterLyrics.WinUI3.Services.Settings;
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using BetterLyrics.WinUI3.Views;
|
using BetterLyrics.WinUI3.Views;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.Windows.ApplicationModel.Resources;
|
using Microsoft.Windows.ApplicationModel.Resources;
|
||||||
using System.Text;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Windows.ApplicationModel.Core;
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3 {
|
namespace BetterLyrics.WinUI3
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides application-specific behavior to supplement the default Application class.
|
/// Provides application-specific behavior to supplement the default Application class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class App : Application {
|
public partial class App : Application
|
||||||
public static App Current => (App)Application.Current;
|
{
|
||||||
public MainWindow? MainWindow { get; private set; }
|
private readonly ILogger<App> _logger;
|
||||||
public MainWindow? SettingsWindow { get; set; }
|
|
||||||
|
|
||||||
public static ResourceLoader ResourceLoader = new();
|
public static new App Current => (App)Application.Current;
|
||||||
|
public BaseWindow? MainWindow { get; private set; }
|
||||||
|
public BaseWindow? SettingsWindow { get; set; }
|
||||||
|
|
||||||
public static DispatcherQueue DispatcherQueue => DispatcherQueue.GetForCurrentThread();
|
public static ResourceLoader? ResourceLoader { get; private set; }
|
||||||
|
|
||||||
|
public static DispatcherQueue? DispatcherQueue { get; private set; }
|
||||||
|
public static DispatcherQueueTimer? DispatcherQueueTimer { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the singleton application object. This is the first line of authored code
|
/// Initializes the singleton application object. This is the first line of authored code
|
||||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public App() {
|
public App()
|
||||||
|
{
|
||||||
this.InitializeComponent();
|
this.InitializeComponent();
|
||||||
|
|
||||||
|
DispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||||
|
DispatcherQueueTimer = DispatcherQueue.CreateTimer();
|
||||||
|
ResourceLoader = new ResourceLoader();
|
||||||
|
|
||||||
|
Helper.AppInfo.EnsureDirectories();
|
||||||
|
ConfigureServices();
|
||||||
|
|
||||||
|
_logger = Ioc.Default.GetService<ILogger<App>>()!;
|
||||||
|
|
||||||
|
UnhandledException += App_UnhandledException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConfigureServices()
|
||||||
|
{
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
.MinimumLevel.Debug()
|
||||||
|
.WriteTo.File(Helper.AppInfo.LogFilePattern, rollingInterval: RollingInterval.Day)
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
|
// Register services
|
||||||
|
Ioc.Default.ConfigureServices(
|
||||||
|
new ServiceCollection()
|
||||||
|
.AddLogging(loggingBuilder =>
|
||||||
|
{
|
||||||
|
loggingBuilder.ClearProviders();
|
||||||
|
loggingBuilder.AddSerilog();
|
||||||
|
})
|
||||||
|
// Services
|
||||||
|
.AddSingleton<SettingsService>()
|
||||||
|
.AddSingleton<DatabaseService>()
|
||||||
|
// ViewModels
|
||||||
|
.AddSingleton<BaseWindowModel>()
|
||||||
|
.AddSingleton<MainViewModel>()
|
||||||
|
.AddSingleton<SettingsViewModel>()
|
||||||
|
.BuildServiceProvider()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void App_UnhandledException(
|
||||||
|
object sender,
|
||||||
|
Microsoft.UI.Xaml.UnhandledExceptionEventArgs e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_logger.LogError(e.Exception, "App_UnhandledException");
|
||||||
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when the application is launched.
|
/// Invoked when the application is launched.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">Details about the launch request and process.</param>
|
/// <param name="args">Details about the launch request and process.</param>
|
||||||
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) {
|
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
|
||||||
|
{
|
||||||
// Register services
|
|
||||||
Ioc.Default.ConfigureServices(
|
|
||||||
new ServiceCollection()
|
|
||||||
.AddSingleton(DispatcherQueue.GetForCurrentThread())
|
|
||||||
// Services
|
|
||||||
.AddSingleton<SettingsService>()
|
|
||||||
.AddSingleton<DatabaseService>()
|
|
||||||
// ViewModels
|
|
||||||
.AddSingleton<MainViewModel>()
|
|
||||||
.AddSingleton<SettingsViewModel>()
|
|
||||||
.BuildServiceProvider());
|
|
||||||
|
|
||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
|
|
||||||
// Activate the window
|
// Activate the window
|
||||||
MainWindow = new MainWindow();
|
MainWindow = new BaseWindow();
|
||||||
MainWindow!.Navigate(typeof(MainPage));
|
MainWindow!.Navigate(typeof(MainPage));
|
||||||
MainWindow.Activate();
|
MainWindow.Activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/AI - 甜度爆表.mp3
Normal file
|
Before Width: | Height: | Size: 334 B |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
@@ -2,41 +2,56 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
|
<TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
|
||||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
|
||||||
<RootNamespace>BetterLyrics.WinUI3</RootNamespace>
|
<RootNamespace>BetterLyrics.WinUI3</RootNamespace>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
|
||||||
<Platforms>x86;x64;ARM64</Platforms>
|
<Platforms>x86;x64;ARM64</Platforms>
|
||||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||||
<UseWinUI>true</UseWinUI>
|
<UseWinUI>true</UseWinUI>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Logo.ico" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Manifest Include="$(ApplicationManifest)" />
|
<Manifest Include="$(ApplicationManifest)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.MarqueeText" Version="0.1.230830" />
|
<PackageReference Include="CommunityToolkit.Labs.WinUI.MarqueeText" Version="0.1.230830" />
|
||||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.OpacityMaskView" Version="0.1.250513-build.2126" />
|
<PackageReference
|
||||||
|
Include="CommunityToolkit.Labs.WinUI.OpacityMaskView"
|
||||||
|
Version="0.1.250513-build.2126"
|
||||||
|
/>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
|
<PackageReference
|
||||||
|
Include="CommunityToolkit.WinUI.Controls.SettingsControls"
|
||||||
|
Version="8.2.250402"
|
||||||
|
/>
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||||
|
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
||||||
<PackageReference Include="DevWinUI" Version="8.2.0" />
|
<PackageReference Include="DevWinUI" Version="8.3.0" />
|
||||||
|
<PackageReference Include="DevWinUI.Controls" Version="8.3.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
|
||||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
|
||||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
|
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
|
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.5" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.5" />
|
||||||
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
|
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
|
||||||
<PackageReference Include="z440.atl.core" Version="6.24.0" />
|
<PackageReference Include="z440.atl.core" Version="6.24.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="Assets\AI - 甜度爆表.mp3">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
<!-- Publish Properties -->
|
<!-- Publish Properties -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||||
@@ -45,4 +60,9 @@
|
|||||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
||||||
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<ApplicationIcon>Logo.ico</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,22 +1,27 @@
|
|||||||
using Microsoft.UI.Xaml.Data;
|
using System;
|
||||||
using Microsoft.UI.Xaml.Media;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Converter {
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
public class ColorToBrushConverter : IValueConverter {
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, string language) {
|
public class ColorToBrushConverter : IValueConverter
|
||||||
if (value is Color color) {
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is Color color)
|
||||||
|
{
|
||||||
return new SolidColorBrush(color);
|
return new SolidColorBrush(color);
|
||||||
}
|
}
|
||||||
return new SolidColorBrush();
|
return new SolidColorBrush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, string language) {
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
using Microsoft.UI.Xaml;
|
using System;
|
||||||
using Microsoft.UI.Xaml.Data;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Converter {
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
internal class ThemeTypeToElementThemeConverter : IValueConverter {
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, string language) {
|
internal class ThemeTypeToElementThemeConverter : IValueConverter
|
||||||
if (value is int themeType) {
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is int themeType)
|
||||||
|
{
|
||||||
return (ElementTheme)themeType;
|
return (ElementTheme)themeType;
|
||||||
}
|
}
|
||||||
return ElementTheme.Default;
|
return ElementTheme.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, string language) {
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,13 @@
|
|||||||
using Microsoft.UI.Xaml;
|
using System;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Media.Animation;
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper {
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
|
{
|
||||||
/// <summary>
|
public static class AnimationHelper
|
||||||
/// Edited based on: https://stackoverflow.com/a/25236507/11048731
|
{
|
||||||
/// </summary>
|
public const int StackedNotificationsShowingDuration = 3900;
|
||||||
public class AnimationHelper : DependencyObject {
|
public const int StoryboardDefaultDuration = 200;
|
||||||
public static int GetAnimationDuration(DependencyObject obj) {
|
public const int DebounceDefaultDuration = 200;
|
||||||
return (int)obj.GetValue(AnimationDurationProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetAnimationDuration(DependencyObject obj, int value) {
|
|
||||||
obj.SetValue(AnimationDurationProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Using a DependencyProperty as the backing store for AnimationDuration.
|
|
||||||
// This enables animation, styling, binding, etc...
|
|
||||||
public static readonly DependencyProperty AnimationDurationProperty =
|
|
||||||
DependencyProperty.RegisterAttached("AnimationDuration", typeof(int),
|
|
||||||
typeof(AnimationHelper), new PropertyMetadata(0,
|
|
||||||
OnAnimationDurationChanged));
|
|
||||||
|
|
||||||
private static void OnAnimationDurationChanged(DependencyObject d,
|
|
||||||
DependencyPropertyChangedEventArgs e) {
|
|
||||||
FrameworkElement element = d as FrameworkElement;
|
|
||||||
|
|
||||||
var ms = (int)e.NewValue;
|
|
||||||
|
|
||||||
if (ms < 0) return;
|
|
||||||
|
|
||||||
var key = "LyricsLineCharGradientInTextBlock";
|
|
||||||
foreach (var timeline in (element.Resources[key] as Storyboard).Children) {
|
|
||||||
foreach (var keyFrame in (timeline as DoubleAnimationUsingKeyFrames).KeyFrames) {
|
|
||||||
(keyFrame as LinearDoubleKeyFrame).KeyTime =
|
|
||||||
KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(ms));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
59
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/AppInfo.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Windows.ApplicationModel;
|
||||||
|
using Windows.Storage;
|
||||||
|
|
||||||
|
public static class AppInfo
|
||||||
|
{
|
||||||
|
// App Metadata
|
||||||
|
public const string AppName = "BetterLyrics";
|
||||||
|
public const string AppDisplayName = "Better Lyrics";
|
||||||
|
public const string AppAuthor = "Zhe Fang";
|
||||||
|
public const string GithubUrl = "https://github.com/jayfunc/BetterLyrics";
|
||||||
|
public static string AppVersion
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var version = Package.Current.Id.Version;
|
||||||
|
return $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Environment Info
|
||||||
|
public static bool IsDebug =>
|
||||||
|
#if DEBUG
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Base Folders
|
||||||
|
private static string LocalFolder => ApplicationData.Current.LocalFolder.Path;
|
||||||
|
public static string CacheFolder => ApplicationData.Current.LocalCacheFolder.Path;
|
||||||
|
public static string AssetsFolder => Path.Combine(Package.Current.InstalledPath, "Assets");
|
||||||
|
|
||||||
|
// Data Files
|
||||||
|
private static string DatabaseFileName => "database.db";
|
||||||
|
public static string DatabasePath => Path.Combine(LocalFolder, DatabaseFileName);
|
||||||
|
|
||||||
|
public static string LogDirectory => Path.Combine(CacheFolder, "logs");
|
||||||
|
public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt");
|
||||||
|
|
||||||
|
private static string TestMusicFileName => "AI - 甜度爆表.mp3";
|
||||||
|
public static string TestMusicPath => Path.Combine(AssetsFolder, TestMusicFileName);
|
||||||
|
|
||||||
|
public static void EnsureDirectories()
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(LogDirectory);
|
||||||
|
Directory.CreateDirectory(LocalFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,12 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper {
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
public static class CollectionHelper {
|
{
|
||||||
public static T? SafeGet<T>(this IList<T> list, int index) {
|
public static class CollectionHelper
|
||||||
|
{
|
||||||
|
public static T? SafeGet<T>(this IList<T> list, int index)
|
||||||
|
{
|
||||||
if (list == null || index < 0 || index >= list.Count)
|
if (list == null || index < 0 || index >= list.Count)
|
||||||
return default;
|
return default;
|
||||||
return list[index];
|
return list[index];
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
namespace BetterLyrics.WinUI3.Helper {
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
public class ColorHelper {
|
{
|
||||||
public static Windows.UI.Color LerpColor(Windows.UI.Color a, Windows.UI.Color b, double t) {
|
public class ColorHelper
|
||||||
|
{
|
||||||
|
public static Windows.UI.Color LerpColor(Windows.UI.Color a, Windows.UI.Color b, double t)
|
||||||
|
{
|
||||||
byte A = (byte)(a.A + (b.A - a.A) * t);
|
byte A = (byte)(a.A + (b.A - a.A) * t);
|
||||||
byte R = (byte)(a.R + (b.R - a.R) * t);
|
byte R = (byte)(a.R + (b.R - a.R) * t);
|
||||||
byte G = (byte)(a.G + (b.G - a.G) * t);
|
byte G = (byte)(a.G + (b.G - a.G) * t);
|
||||||
byte B = (byte)(a.B + (b.B - a.B) * t);
|
byte B = (byte)(a.B + (b.B - a.B) * t);
|
||||||
return Windows.UI.Color.FromArgb(A, R, G, B);
|
return Windows.UI.Color.FromArgb(A, R, G, B);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
internal class CMap
|
internal class CMap
|
||||||
{
|
{
|
||||||
private readonly List<VBox> vboxes = new List<VBox>();
|
private readonly List<VBox> vboxes = new List<VBox>();
|
||||||
private List<QuantizedColor> palette;
|
private List<QuantizedColor>? palette;
|
||||||
|
|
||||||
public void Push(VBox box)
|
public void Push(VBox box)
|
||||||
{
|
{
|
||||||
@@ -24,10 +24,12 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
if (palette == null)
|
if (palette == null)
|
||||||
{
|
{
|
||||||
palette = (from vBox in vboxes
|
palette = (
|
||||||
|
from vBox in vboxes
|
||||||
let rgb = vBox.Avg(false)
|
let rgb = vBox.Avg(false)
|
||||||
let color = FromRgb(rgb[0], rgb[1], rgb[2])
|
let color = FromRgb(rgb[0], rgb[1], rgb[2])
|
||||||
select new QuantizedColor(color, vBox.Count(false))).ToList();
|
select new QuantizedColor(color, vBox.Count(false))
|
||||||
|
).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return palette;
|
return palette;
|
||||||
@@ -38,7 +40,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return vboxes.Count;
|
return vboxes.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] Map(int[] color)
|
public int[]? Map(int[] color)
|
||||||
{
|
{
|
||||||
foreach (var vbox in vboxes.Where(vbox => vbox.Contains(color)))
|
foreach (var vbox in vboxes.Where(vbox => vbox.Contains(color)))
|
||||||
{
|
{
|
||||||
@@ -47,17 +49,19 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return Nearest(color);
|
return Nearest(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] Nearest(int[] color)
|
public int[]? Nearest(int[] color)
|
||||||
{
|
{
|
||||||
var d1 = double.MaxValue;
|
var d1 = double.MaxValue;
|
||||||
int[] pColor = null;
|
int[]? pColor = null;
|
||||||
|
|
||||||
foreach (var t in vboxes)
|
foreach (var t in vboxes)
|
||||||
{
|
{
|
||||||
var vbColor = t.Avg(false);
|
var vbColor = t.Avg(false);
|
||||||
var d2 = Math.Sqrt(Math.Pow(color[0] - vbColor[0], 2)
|
var d2 = Math.Sqrt(
|
||||||
|
Math.Pow(color[0] - vbColor[0], 2)
|
||||||
+ Math.Pow(color[1] - vbColor[1], 2)
|
+ Math.Pow(color[1] - vbColor[1], 2)
|
||||||
+ Math.Pow(color[2] - vbColor[2], 2));
|
+ Math.Pow(color[2] - vbColor[2], 2)
|
||||||
|
);
|
||||||
if (d2 < d1)
|
if (d2 < d1)
|
||||||
{
|
{
|
||||||
d1 = d2;
|
d1 = d2;
|
||||||
@@ -67,9 +71,16 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return pColor;
|
return pColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VBox FindColor(double targetLuma, double minLuma, double maxLuma, double targetSaturation, double minSaturation, double maxSaturation)
|
public VBox FindColor(
|
||||||
|
double targetLuma,
|
||||||
|
double minLuma,
|
||||||
|
double maxLuma,
|
||||||
|
double targetSaturation,
|
||||||
|
double minSaturation,
|
||||||
|
double maxSaturation
|
||||||
|
)
|
||||||
{
|
{
|
||||||
VBox max = null;
|
VBox? max = null;
|
||||||
double maxValue = 0;
|
double maxValue = 0;
|
||||||
var highestPopulation = vboxes.Select(p => p.Count(false)).Max();
|
var highestPopulation = vboxes.Select(p => p.Count(false)).Max();
|
||||||
|
|
||||||
@@ -80,11 +91,21 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
var sat = hsl.S;
|
var sat = hsl.S;
|
||||||
var luma = hsl.L;
|
var luma = hsl.L;
|
||||||
|
|
||||||
if (sat >= minSaturation && sat <= maxSaturation &&
|
if (
|
||||||
luma >= minLuma && luma <= maxLuma)
|
sat >= minSaturation
|
||||||
|
&& sat <= maxSaturation
|
||||||
|
&& luma >= minLuma
|
||||||
|
&& luma <= maxLuma
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var thisValue = Mmcq.CreateComparisonValue(sat, targetSaturation, luma, targetLuma,
|
var thisValue = Mmcq.CreateComparisonValue(
|
||||||
swatch.Count(false), highestPopulation);
|
sat,
|
||||||
|
targetSaturation,
|
||||||
|
luma,
|
||||||
|
targetLuma,
|
||||||
|
swatch.Count(false),
|
||||||
|
highestPopulation
|
||||||
|
);
|
||||||
|
|
||||||
if (max == null || thisValue > maxValue)
|
if (max == null || thisValue > maxValue)
|
||||||
{
|
{
|
||||||
@@ -97,14 +118,14 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color FromRgb(int red, int green, int blue)
|
public RGB FromRgb(int red, int green, int blue)
|
||||||
{
|
{
|
||||||
var color = new Color
|
var color = new RGB
|
||||||
{
|
{
|
||||||
A = 255,
|
A = 255,
|
||||||
R = (byte)red,
|
R = (byte)red,
|
||||||
G = (byte)green,
|
G = (byte)green,
|
||||||
B = (byte)blue
|
B = (byte)blue,
|
||||||
};
|
};
|
||||||
|
|
||||||
return color;
|
return color;
|
||||||
@@ -114,7 +135,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a color in RGB space.
|
/// Defines a color in RGB space.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct Color
|
public struct RGB
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get or Set the Alpha component value for sRGB.
|
/// Get or Set the Alpha component value for sRGB.
|
||||||
@@ -269,9 +290,12 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
|
|
||||||
private static VBox VboxFromPixels(IList<byte[]> pixels, int[] histo)
|
private static VBox VboxFromPixels(IList<byte[]> pixels, int[] histo)
|
||||||
{
|
{
|
||||||
int rmin = 1000000, rmax = 0;
|
int rmin = 1000000,
|
||||||
int gmin = 1000000, gmax = 0;
|
rmax = 0;
|
||||||
int bmin = 1000000, bmax = 0;
|
int gmin = 1000000,
|
||||||
|
gmax = 0;
|
||||||
|
int bmin = 1000000,
|
||||||
|
bmax = 0;
|
||||||
|
|
||||||
// find min/max
|
// find min/max
|
||||||
var numPixels = pixels.Count;
|
var numPixels = pixels.Count;
|
||||||
@@ -313,7 +337,13 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return new VBox(rmin, rmax, gmin, gmax, bmin, bmax, histo);
|
return new VBox(rmin, rmax, gmin, gmax, bmin, bmax, histo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VBox[] DoCut(char color, VBox vbox, IList<int> partialsum, IList<int> lookaheadsum, int total)
|
private static VBox[] DoCut(
|
||||||
|
char color,
|
||||||
|
VBox vbox,
|
||||||
|
IList<int> partialsum,
|
||||||
|
IList<int> lookaheadsum,
|
||||||
|
int total
|
||||||
|
)
|
||||||
{
|
{
|
||||||
int vboxDim1;
|
int vboxDim1;
|
||||||
int vboxDim2;
|
int vboxDim2;
|
||||||
@@ -344,7 +374,8 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
var left = i - vboxDim1;
|
var left = i - vboxDim1;
|
||||||
var right = vboxDim2 - i;
|
var right = vboxDim2 - i;
|
||||||
|
|
||||||
var d2 = left <= right
|
var d2 =
|
||||||
|
left <= right
|
||||||
? Math.Min(vboxDim2 - 1, Math.Abs(i + right / 2))
|
? Math.Min(vboxDim2 - 1, Math.Abs(i + right / 2))
|
||||||
: Math.Max(vboxDim1, Math.Abs(Convert.ToInt32(i - 1 - left / 2.0)));
|
: Math.Max(vboxDim1, Math.Abs(Convert.ToInt32(i - 1 - left / 2.0)));
|
||||||
|
|
||||||
@@ -383,7 +414,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
throw new Exception("VBox can't be cut");
|
throw new Exception("VBox can't be cut");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VBox[] MedianCutApply(IList<int> histo, VBox vbox)
|
private static VBox?[]? MedianCutApply(IList<int> histo, VBox vbox)
|
||||||
{
|
{
|
||||||
if (vbox.Count(false) == 0)
|
if (vbox.Count(false) == 0)
|
||||||
{
|
{
|
||||||
@@ -391,7 +422,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
}
|
}
|
||||||
if (vbox.Count(false) == 1)
|
if (vbox.Count(false) == 1)
|
||||||
{
|
{
|
||||||
return new[] { vbox.Clone(), null };
|
return [vbox.Clone(), null];
|
||||||
}
|
}
|
||||||
|
|
||||||
// only one pixel, no split
|
// only one pixel, no split
|
||||||
@@ -417,7 +448,11 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
lookaheadsum[l] = -1;
|
lookaheadsum[l] = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i, j, k, sum, index;
|
int i,
|
||||||
|
j,
|
||||||
|
k,
|
||||||
|
sum,
|
||||||
|
index;
|
||||||
|
|
||||||
if (maxw == rw)
|
if (maxw == rw)
|
||||||
{
|
{
|
||||||
@@ -480,8 +515,9 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
}
|
}
|
||||||
|
|
||||||
// determine the cut planes
|
// determine the cut planes
|
||||||
return maxw == rw ? DoCut('r', vbox, partialsum, lookaheadsum, total) : maxw == gw
|
return maxw == rw ? DoCut('r', vbox, partialsum, lookaheadsum, total)
|
||||||
? DoCut('g', vbox, partialsum, lookaheadsum, total) : DoCut('b', vbox, partialsum, lookaheadsum, total);
|
: maxw == gw ? DoCut('g', vbox, partialsum, lookaheadsum, total)
|
||||||
|
: DoCut('b', vbox, partialsum, lookaheadsum, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -492,7 +528,12 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
/// <param name="target">The target.</param>
|
/// <param name="target">The target.</param>
|
||||||
/// <param name="histo">The histo.</param>
|
/// <param name="histo">The histo.</param>
|
||||||
/// <exception cref="System.Exception">vbox1 not defined; shouldn't happen!</exception>
|
/// <exception cref="System.Exception">vbox1 not defined; shouldn't happen!</exception>
|
||||||
private static void Iter(List<VBox> lh, IComparer<VBox> comparator, int target, IList<int> histo)
|
private static void Iter(
|
||||||
|
List<VBox> lh,
|
||||||
|
IComparer<VBox> comparator,
|
||||||
|
int target,
|
||||||
|
IList<int> histo
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var ncolors = 1;
|
var ncolors = 1;
|
||||||
var niters = 0;
|
var niters = 0;
|
||||||
@@ -511,13 +552,12 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
|
|
||||||
// do the cut
|
// do the cut
|
||||||
var vboxes = MedianCutApply(histo, vbox);
|
var vboxes = MedianCutApply(histo, vbox);
|
||||||
var vbox1 = vboxes[0];
|
var vbox1 = vboxes?[0];
|
||||||
var vbox2 = vboxes[1];
|
var vbox2 = vboxes?[1];
|
||||||
|
|
||||||
if (vbox1 == null)
|
if (vbox1 == null)
|
||||||
{
|
{
|
||||||
throw new Exception(
|
throw new Exception("vbox1 not defined; shouldn't happen!");
|
||||||
"vbox1 not defined; shouldn't happen!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lh.Add(vbox1);
|
lh.Add(vbox1);
|
||||||
@@ -579,11 +619,23 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return cmap;
|
return cmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double CreateComparisonValue(double saturation, double targetSaturation, double luma, double targetLuma, int population, int highestPopulation)
|
public static double CreateComparisonValue(
|
||||||
|
double saturation,
|
||||||
|
double targetSaturation,
|
||||||
|
double luma,
|
||||||
|
double targetLuma,
|
||||||
|
int population,
|
||||||
|
int highestPopulation
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return WeightedMean(InvertDiff(saturation, targetSaturation), WeightSaturation,
|
return WeightedMean(
|
||||||
InvertDiff(luma, targetLuma), WeightLuma,
|
InvertDiff(saturation, targetSaturation),
|
||||||
population / (double)highestPopulation, WeightPopulation);
|
WeightSaturation,
|
||||||
|
InvertDiff(luma, targetLuma),
|
||||||
|
WeightLuma,
|
||||||
|
population / (double)highestPopulation,
|
||||||
|
WeightPopulation
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double WeightedMean(params double[] values)
|
private static double WeightedMean(params double[] values)
|
||||||
@@ -611,20 +663,22 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
|
|
||||||
public class QuantizedColor
|
public class QuantizedColor
|
||||||
{
|
{
|
||||||
public QuantizedColor(Color color, int population)
|
public QuantizedColor(RGB color, int population)
|
||||||
{
|
{
|
||||||
Color = color;
|
Color = color;
|
||||||
Population = population;
|
Population = population;
|
||||||
IsDark = CalculateYiqLuma(color) < 128;
|
IsDark = CalculateYiqLuma(color) < 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color Color { get; private set; }
|
public RGB Color { get; private set; }
|
||||||
public int Population { get; private set; }
|
public int Population { get; private set; }
|
||||||
public bool IsDark { get; private set; }
|
public bool IsDark { get; private set; }
|
||||||
|
|
||||||
public int CalculateYiqLuma(Color color)
|
public int CalculateYiqLuma(RGB color)
|
||||||
{
|
{
|
||||||
return Convert.ToInt32(Math.Round((299 * color.R + 587 * color.G + 114 * color.B) / 1000f));
|
return Convert.ToInt32(
|
||||||
|
Math.Round((299 * color.R + 587 * color.G + 114 * color.B) / 1000f)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,8 +786,9 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
avg = new[]
|
avg = new[]
|
||||||
{
|
{
|
||||||
Math.Abs(rsum / ntot), Math.Abs(gsum / ntot),
|
Math.Abs(rsum / ntot),
|
||||||
Math.Abs(bsum / ntot)
|
Math.Abs(gsum / ntot),
|
||||||
|
Math.Abs(bsum / ntot),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -742,7 +797,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
Math.Abs(Mmcq.Mult * (R1 + R2 + 1) / 2),
|
Math.Abs(Mmcq.Mult * (R1 + R2 + 1) / 2),
|
||||||
Math.Abs(Mmcq.Mult * (G1 + G2 + 1) / 2),
|
Math.Abs(Mmcq.Mult * (G1 + G2 + 1) / 2),
|
||||||
Math.Abs(Mmcq.Mult * (B1 + B2 + 1) / 2)
|
Math.Abs(Mmcq.Mult * (B1 + B2 + 1) / 2),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -809,14 +864,16 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
|
|
||||||
private byte[][] ConvertPixels(byte[] pixels, int pixelCount, int quality, bool ignoreWhite)
|
private byte[][] ConvertPixels(byte[] pixels, int pixelCount, int quality, bool ignoreWhite)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
var expectedDataLength = pixelCount * ColorDepth;
|
var expectedDataLength = pixelCount * ColorDepth;
|
||||||
if (expectedDataLength != pixels.Length)
|
if (expectedDataLength != pixels.Length)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("(expectedDataLength = "
|
throw new ArgumentException(
|
||||||
+ expectedDataLength + ") != (pixels.length = "
|
"(expectedDataLength = "
|
||||||
+ pixels.Length + ")");
|
+ expectedDataLength
|
||||||
|
+ ") != (pixels.length = "
|
||||||
|
+ pixels.Length
|
||||||
|
+ ")"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the RGB values in an array format suitable for quantize
|
// Store the RGB values in an array format suitable for quantize
|
||||||
@@ -864,17 +921,24 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="ignoreWhite">if set to <c>true</c> [ignore white].</param>
|
/// <param name="ignoreWhite">if set to <c>true</c> [ignore white].</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<QuantizedColor> GetColor(BitmapDecoder sourceImage, int quality = DefaultQuality, bool ignoreWhite = DefaultIgnoreWhite)
|
public async Task<QuantizedColor> GetColor(
|
||||||
|
BitmapDecoder sourceImage,
|
||||||
|
int quality = DefaultQuality,
|
||||||
|
bool ignoreWhite = DefaultIgnoreWhite
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var palette = await GetPalette(sourceImage, 3, quality, ignoreWhite);
|
var palette = await GetPalette(sourceImage, 3, quality, ignoreWhite);
|
||||||
|
|
||||||
var dominantColor = new QuantizedColor(new Color
|
var dominantColor = new QuantizedColor(
|
||||||
|
new RGB
|
||||||
{
|
{
|
||||||
A = Convert.ToByte(palette.Average(a => a.Color.A)),
|
A = Convert.ToByte(palette.Average(a => a.Color.A)),
|
||||||
R = Convert.ToByte(palette.Average(a => a.Color.R)),
|
R = Convert.ToByte(palette.Average(a => a.Color.R)),
|
||||||
G = Convert.ToByte(palette.Average(a => a.Color.G)),
|
G = Convert.ToByte(palette.Average(a => a.Color.G)),
|
||||||
B = Convert.ToByte(palette.Average(a => a.Color.B))
|
B = Convert.ToByte(palette.Average(a => a.Color.B)),
|
||||||
}, Convert.ToInt32(palette.Average(a => a.Population)));
|
},
|
||||||
|
Convert.ToInt32(palette.Average(a => a.Population))
|
||||||
|
);
|
||||||
|
|
||||||
return dominantColor;
|
return dominantColor;
|
||||||
}
|
}
|
||||||
@@ -893,7 +957,12 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
/// <param name="ignoreWhite">if set to <c>true</c> [ignore white].</param>
|
/// <param name="ignoreWhite">if set to <c>true</c> [ignore white].</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <code>true</code>
|
/// <code>true</code>
|
||||||
public async Task<List<QuantizedColor>> GetPalette(BitmapDecoder sourceImage, int colorCount = DefaultColorCount, int quality = DefaultQuality, bool ignoreWhite = DefaultIgnoreWhite)
|
public async Task<List<QuantizedColor>> GetPalette(
|
||||||
|
BitmapDecoder sourceImage,
|
||||||
|
int colorCount = DefaultColorCount,
|
||||||
|
int quality = DefaultQuality,
|
||||||
|
bool ignoreWhite = DefaultIgnoreWhite
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var pixelArray = await GetPixelsFast(sourceImage, quality, ignoreWhite);
|
var pixelArray = await GetPixelsFast(sourceImage, quality, ignoreWhite);
|
||||||
var cmap = GetColorMap(pixelArray, colorCount);
|
var cmap = GetColorMap(pixelArray, colorCount);
|
||||||
@@ -912,7 +981,11 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return pixels;
|
return pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<byte[][]> GetPixelsFast(BitmapDecoder sourceImage, int quality, bool ignoreWhite)
|
private async Task<byte[][]> GetPixelsFast(
|
||||||
|
BitmapDecoder sourceImage,
|
||||||
|
int quality,
|
||||||
|
bool ignoreWhite
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (quality < 1)
|
if (quality < 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using SQLite;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper {
|
|
||||||
public class DatabaseHelper {
|
|
||||||
private static SQLiteConnection _database;
|
|
||||||
|
|
||||||
public static SQLiteConnection InitializeDatabase() {
|
|
||||||
string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MusicMetadataIndex.db");
|
|
||||||
_database = new SQLiteConnection(dbPath);
|
|
||||||
_database.CreateTable<MetadataIndex>(); // Create table if it doesn't exist
|
|
||||||
return _database;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,9 +4,10 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper {
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
public class EasingHelper {
|
{
|
||||||
|
public class EasingHelper
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// No easing
|
/// No easing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -25,23 +26,24 @@ namespace BetterLyrics.WinUI3.Helper {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Acceleration until halfway then deceleration
|
/// Acceleration until halfway then deceleration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static float EaseInOutQuad(float t) {
|
public static float EaseInOutQuad(float t)
|
||||||
return t < 0.5f
|
{
|
||||||
? 2 * t * t
|
return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
||||||
: -1 + (4 - 2 * t) * t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Smoother transition than linear
|
/// Smoother transition than linear
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static float SmoothStep(float t) {
|
public static float SmoothStep(float t)
|
||||||
|
{
|
||||||
return t * t * (3 - 2 * t);
|
return t * t * (3 - 2 * t);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Even smoother transition with continuous first and second derivatives
|
/// Even smoother transition with continuous first and second derivatives
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static float SmootherStep(float t) {
|
public static float SmootherStep(float t)
|
||||||
|
{
|
||||||
return t * t * t * (t * (6 * t - 15) + 10);
|
return t * t * t * (t * (6 * t - 15) + 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using Microsoft.UI.Xaml.Media;
|
using System;
|
||||||
using Microsoft.UI.Xaml.Media.Imaging;
|
using System.IO;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices.WindowsRuntime;
|
using System.Runtime.InteropServices.WindowsRuntime;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
using Windows.Graphics.Imaging;
|
using Windows.Graphics.Imaging;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
using Windows.Storage.Streams;
|
using Windows.Storage.Streams;
|
||||||
@@ -11,7 +12,9 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
public class ImageHelper
|
public class ImageHelper
|
||||||
{
|
{
|
||||||
public static async Task<InMemoryRandomAccessStream> GetStreamFromBytesAsync(byte[] imageBytes)
|
public static async Task<InMemoryRandomAccessStream> GetStreamFromBytesAsync(
|
||||||
|
byte[] imageBytes
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (imageBytes == null || imageBytes.Length == 0)
|
if (imageBytes == null || imageBytes.Length == 0)
|
||||||
return null;
|
return null;
|
||||||
@@ -22,5 +25,13 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<byte[]> ToByteArrayAsync(IRandomAccessStreamReference streamRef)
|
||||||
|
{
|
||||||
|
using IRandomAccessStream stream = await streamRef.OpenReadAsync();
|
||||||
|
using var memoryStream = new MemoryStream();
|
||||||
|
await stream.AsStreamForRead().CopyToAsync(memoryStream);
|
||||||
|
return memoryStream.ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,18 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper {
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
public class MathHelper {
|
{
|
||||||
public static List<int> GetAllFactors(int n) {
|
public class MathHelper
|
||||||
|
{
|
||||||
|
public static List<int> GetAllFactors(int n)
|
||||||
|
{
|
||||||
var result = new SortedSet<int>();
|
var result = new SortedSet<int>();
|
||||||
|
|
||||||
for (int i = 1; i <= Math.Sqrt(n); i++) {
|
for (int i = 1; i <= Math.Sqrt(n); i++)
|
||||||
if (n % i == 0) {
|
{
|
||||||
|
if (n % i == 0)
|
||||||
|
{
|
||||||
result.Add(i);
|
result.Add(i);
|
||||||
result.Add(n / i);
|
result.Add(n / i);
|
||||||
}
|
}
|
||||||
@@ -18,6 +23,5 @@ namespace BetterLyrics.WinUI3.Helper {
|
|||||||
|
|
||||||
return [.. result];
|
return [.. result];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,10 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
public class SystemBackdropHelper
|
public class SystemBackdropHelper
|
||||||
{
|
{
|
||||||
public static SystemBackdrop? CreateSystemBackdrop(BackdropType backdropType) {
|
public static SystemBackdrop? CreateSystemBackdrop(BackdropType backdropType)
|
||||||
return backdropType switch {
|
{
|
||||||
|
return backdropType switch
|
||||||
|
{
|
||||||
BackdropType.None => null,
|
BackdropType.None => null,
|
||||||
BackdropType.Mica => new MicaSystemBackdrop(MicaKind.Base),
|
BackdropType.Mica => new MicaSystemBackdrop(MicaKind.Base),
|
||||||
BackdropType.MicaAlt => new MicaSystemBackdrop(MicaKind.BaseAlt),
|
BackdropType.MicaAlt => new MicaSystemBackdrop(MicaKind.BaseAlt),
|
||||||
@@ -18,6 +20,5 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,40 @@
|
|||||||
using Microsoft.UI.Xaml.Media;
|
using System;
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper {
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
public class VisualHelper {
|
{
|
||||||
|
public class VisualHelper
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Source: https://stackoverflow.com/a/61626933/11048731
|
/// Source: https://stackoverflow.com/a/61626933/11048731
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="depObj"></param>
|
/// <param name="depObj"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static List<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject {
|
public static List<T> FindVisualChildren<T>(DependencyObject depObj)
|
||||||
List<T> list = new List<T>();
|
where T : DependencyObject
|
||||||
if (depObj != null) {
|
{
|
||||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) {
|
List<T> list = [];
|
||||||
|
if (depObj != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
|
||||||
|
{
|
||||||
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
|
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
|
||||||
if (child != null && child is T) {
|
if (child != null && child is T t)
|
||||||
list.Add((T)child);
|
{
|
||||||
|
list.Add(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<T> childItems = FindVisualChildren<T>(child);
|
List<T> childItems = FindVisualChildren<T>(child);
|
||||||
if (childItems != null && childItems.Count() > 0) {
|
if (childItems != null && childItems.Count > 0)
|
||||||
foreach (var item in childItems) {
|
{
|
||||||
|
foreach (var item in childItems)
|
||||||
|
{
|
||||||
list.Add(item);
|
list.Add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logo.ico
Normal file
|
After Width: | Height: | Size: 201 KiB |
@@ -1,191 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<Window
|
|
||||||
x:Class="BetterLyrics.WinUI3.MainWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
|
||||||
xmlns:local="using:BetterLyrics.WinUI3"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:media="using:CommunityToolkit.WinUI.Media"
|
|
||||||
SizeChanged="Window_SizeChanged"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
|
|
||||||
<Grid x:Name="RootGrid">
|
|
||||||
|
|
||||||
<Frame
|
|
||||||
x:Name="RootFrame"
|
|
||||||
Navigated="RootFrame_Navigated"
|
|
||||||
NavigationFailed="RootFrame_NavigationFailed" />
|
|
||||||
|
|
||||||
<Grid
|
|
||||||
x:Name="TopCommandGrid"
|
|
||||||
Padding="2,0"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
Background="Transparent"
|
|
||||||
Opacity="0">
|
|
||||||
|
|
||||||
<Grid.Resources>
|
|
||||||
|
|
||||||
<Storyboard x:Name="TopCommandGridFadeInStoryboard">
|
|
||||||
<DoubleAnimation
|
|
||||||
Storyboard.TargetName="TopCommandGrid"
|
|
||||||
Storyboard.TargetProperty="Opacity"
|
|
||||||
To="1"
|
|
||||||
Duration="0:0:0.2" />
|
|
||||||
</Storyboard>
|
|
||||||
<Storyboard x:Name="TopCommandGridFadeOutStoryboard">
|
|
||||||
<DoubleAnimation
|
|
||||||
Storyboard.TargetName="TopCommandGrid"
|
|
||||||
Storyboard.TargetProperty="Opacity"
|
|
||||||
To="0"
|
|
||||||
Duration="0:0:0.2" />
|
|
||||||
</Storyboard>
|
|
||||||
|
|
||||||
</Grid.Resources>
|
|
||||||
|
|
||||||
<interactivity:Interaction.Behaviors>
|
|
||||||
|
|
||||||
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
|
||||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource TopCommandGridFadeInStoryboard}" />
|
|
||||||
</interactivity:EventTriggerBehavior>
|
|
||||||
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
|
||||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource TopCommandGridFadeOutStoryboard}" />
|
|
||||||
</interactivity:EventTriggerBehavior>
|
|
||||||
|
|
||||||
</interactivity:Interaction.Behaviors>
|
|
||||||
|
|
||||||
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
|
|
||||||
<!-- Always On Top -->
|
|
||||||
<AppBarButton
|
|
||||||
x:Name="AOTButton"
|
|
||||||
Click="AOTButton_Click"
|
|
||||||
LabelPosition="Collapsed">
|
|
||||||
<Grid>
|
|
||||||
<FontIcon FontFamily="Segoe Fluent Icons" Glyph="" />
|
|
||||||
<FontIcon
|
|
||||||
x:Name="PinnedFontIcon"
|
|
||||||
FontFamily="Segoe Fluent Icons"
|
|
||||||
Glyph=""
|
|
||||||
Opacity="0">
|
|
||||||
<FontIcon.Resources>
|
|
||||||
<Storyboard x:Key="ShowPinnedFontIconStoryboard">
|
|
||||||
<DoubleAnimation
|
|
||||||
Storyboard.TargetName="PinnedFontIcon"
|
|
||||||
Storyboard.TargetProperty="Opacity"
|
|
||||||
To="1"
|
|
||||||
Duration="0:0:0.3" />
|
|
||||||
</Storyboard>
|
|
||||||
<Storyboard x:Key="HidePinnedFontIconStoryboard">
|
|
||||||
<DoubleAnimation
|
|
||||||
Storyboard.TargetName="PinnedFontIcon"
|
|
||||||
Storyboard.TargetProperty="Opacity"
|
|
||||||
To="0"
|
|
||||||
Duration="0:0:0.3" />
|
|
||||||
</Storyboard>
|
|
||||||
</FontIcon.Resources>
|
|
||||||
</FontIcon>
|
|
||||||
</Grid>
|
|
||||||
</AppBarButton>
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
x:Name="AppTitleTextBlock"
|
|
||||||
Margin="0,-4,0,0"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{x:Bind Title}" />
|
|
||||||
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
|
||||||
<!-- Window Mini -->
|
|
||||||
<AppBarButton
|
|
||||||
x:Name="MiniButton"
|
|
||||||
Click="MiniButton_Click"
|
|
||||||
LabelPosition="Collapsed"
|
|
||||||
Visibility="Collapsed">
|
|
||||||
<FontIcon FontFamily="Segoe Fluent Icons" Glyph="" />
|
|
||||||
</AppBarButton>
|
|
||||||
<!-- Window Unmini -->
|
|
||||||
<AppBarButton
|
|
||||||
x:Name="UnminiButton"
|
|
||||||
Click="UnminiButton_Click"
|
|
||||||
LabelPosition="Collapsed"
|
|
||||||
Visibility="Collapsed">
|
|
||||||
<FontIcon FontFamily="Segoe Fluent Icons" Glyph="" />
|
|
||||||
</AppBarButton>
|
|
||||||
<!-- Window Minimise -->
|
|
||||||
<AppBarButton
|
|
||||||
x:Name="MinimiseButton"
|
|
||||||
Click="MinimiseButton_Click"
|
|
||||||
LabelPosition="Collapsed">
|
|
||||||
<FontIcon FontFamily="Segoe Fluent Icons" Glyph="" />
|
|
||||||
</AppBarButton>
|
|
||||||
<!-- Window Maximise -->
|
|
||||||
<AppBarButton
|
|
||||||
x:Name="MaximiseButton"
|
|
||||||
Click="MaximiseButton_Click"
|
|
||||||
LabelPosition="Collapsed">
|
|
||||||
<FontIcon FontFamily="Segoe Fluent Icons" Glyph="" />
|
|
||||||
</AppBarButton>
|
|
||||||
<!-- Window Restore -->
|
|
||||||
<AppBarButton
|
|
||||||
x:Name="RestoreButton"
|
|
||||||
Click="RestoreButton_Click"
|
|
||||||
LabelPosition="Collapsed"
|
|
||||||
Visibility="Collapsed">
|
|
||||||
<FontIcon FontFamily="Segoe Fluent Icons" Glyph="" />
|
|
||||||
</AppBarButton>
|
|
||||||
<!-- Window Close -->
|
|
||||||
<AppBarButton
|
|
||||||
x:Name="CloseButton"
|
|
||||||
Click="CloseButton_Click"
|
|
||||||
LabelPosition="Collapsed">
|
|
||||||
<FontIcon FontFamily="Segoe Fluent Icons" Glyph="" />
|
|
||||||
</AppBarButton>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<InfoBar
|
|
||||||
x:Name="HostInfoBar"
|
|
||||||
Margin="18"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
IsClosable="False"
|
|
||||||
Opacity="0">
|
|
||||||
<InfoBar.RenderTransform>
|
|
||||||
<TranslateTransform x:Name="HostInfoBarTransform" Y="20" />
|
|
||||||
</InfoBar.RenderTransform>
|
|
||||||
<InfoBar.Resources>
|
|
||||||
<Storyboard x:Key="InfoBarShowAndHideStoryboard">
|
|
||||||
<!-- Opacity -->
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="HostInfoBar" Storyboard.TargetProperty="Opacity">
|
|
||||||
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0" />
|
|
||||||
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
|
|
||||||
<EasingDoubleKeyFrame KeyTime="0:0:3.6" Value="1" />
|
|
||||||
<EasingDoubleKeyFrame KeyTime="0:0:3.9" Value="0" />
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
|
|
||||||
<!-- Y -->
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="HostInfoBarTransform" Storyboard.TargetProperty="Y">
|
|
||||||
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="20" />
|
|
||||||
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0" />
|
|
||||||
<EasingDoubleKeyFrame KeyTime="0:0:3.6" Value="0" />
|
|
||||||
<EasingDoubleKeyFrame KeyTime="0:0:3.9" Value="20" />
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</InfoBar.Resources>
|
|
||||||
<interactivity:Interaction.Behaviors>
|
|
||||||
<behaviors:StackedNotificationsBehavior x:Name="NotificationQueue" />
|
|
||||||
<interactivity:DataTriggerBehavior
|
|
||||||
Binding="{Binding ElementName=HostInfoBar, Path=IsOpen, Mode=OneWay}"
|
|
||||||
ComparisonCondition="Equal"
|
|
||||||
Value="True">
|
|
||||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource InfoBarShowAndHideStoryboard}" />
|
|
||||||
</interactivity:DataTriggerBehavior>
|
|
||||||
</interactivity:Interaction.Behaviors>
|
|
||||||
</InfoBar>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using BetterLyrics.WinUI3.Helper;
|
|
||||||
using BetterLyrics.WinUI3.Messages;
|
|
||||||
using BetterLyrics.WinUI3.Services.Settings;
|
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
|
||||||
using BetterLyrics.WinUI3.Views;
|
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
|
||||||
using CommunityToolkit.WinUI.Behaviors;
|
|
||||||
using DevWinUI;
|
|
||||||
using Microsoft.UI;
|
|
||||||
using Microsoft.UI.Composition;
|
|
||||||
using Microsoft.UI.Composition.SystemBackdrops;
|
|
||||||
using Microsoft.UI.Windowing;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using Microsoft.UI.Xaml.Media;
|
|
||||||
using Microsoft.UI.Xaml.Media.Animation;
|
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
|
||||||
using WinRT;
|
|
||||||
using WinRT.Interop;
|
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
|
||||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3 {
|
|
||||||
/// <summary>
|
|
||||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
|
||||||
/// </summary>
|
|
||||||
public sealed partial class MainWindow : Window {
|
|
||||||
|
|
||||||
private readonly OverlappedPresenter _presenter;
|
|
||||||
|
|
||||||
private SettingsService _settingsService;
|
|
||||||
|
|
||||||
public static StackedNotificationsBehavior? StackedNotificationsBehavior { get; private set; }
|
|
||||||
|
|
||||||
public MainWindow() {
|
|
||||||
this.InitializeComponent();
|
|
||||||
|
|
||||||
_settingsService = Ioc.Default.GetService<SettingsService>();
|
|
||||||
|
|
||||||
RootGrid.RequestedTheme = (ElementTheme)_settingsService.Theme;
|
|
||||||
SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop((BackdropType)_settingsService.BackdropType);
|
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.Register<ThemeChangedMessage>(this, (r, m) => {
|
|
||||||
RootGrid.RequestedTheme = m.Value;
|
|
||||||
});
|
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.Register<SystemBackdropChangedMessage>(this, (r, m) => {
|
|
||||||
SystemBackdrop = null;
|
|
||||||
SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(m.Value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// AppWindow.SetIcon("white_round.ico");
|
|
||||||
StackedNotificationsBehavior = NotificationQueue;
|
|
||||||
|
|
||||||
_presenter = (OverlappedPresenter)AppWindow.Presenter;
|
|
||||||
|
|
||||||
ExtendsContentIntoTitleBar = true;
|
|
||||||
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
|
|
||||||
SetTitleBar(TopCommandGrid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Navigate(Type type) {
|
|
||||||
RootFrame.Navigate(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e) {
|
|
||||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BackButton_Click(object sender, RoutedEventArgs e) {
|
|
||||||
if (RootFrame.CanGoBack) {
|
|
||||||
RootFrame.GoBack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CloseButton_Click(object sender, RoutedEventArgs e) {
|
|
||||||
if (RootFrame.CurrentSourcePageType == typeof(MainPage)) {
|
|
||||||
App.Current.Exit();
|
|
||||||
} else if (RootFrame.CurrentSourcePageType == typeof(SettingsPage)) {
|
|
||||||
App.Current.SettingsWindow!.AppWindow.Hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MaximiseButton_Click(object sender, RoutedEventArgs e) {
|
|
||||||
_presenter.Maximize();
|
|
||||||
//MaximiseButton.Visibility = Visibility.Collapsed;
|
|
||||||
//RestoreButton.Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MinimiseButton_Click(object sender, RoutedEventArgs e) {
|
|
||||||
_presenter.Minimize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RestoreButton_Click(object sender, RoutedEventArgs e) {
|
|
||||||
_presenter.Restore();
|
|
||||||
//MaximiseButton.Visibility = Visibility.Visible;
|
|
||||||
//RestoreButton.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Window_SizeChanged(object sender, WindowSizeChangedEventArgs args) {
|
|
||||||
if (_presenter.State == OverlappedPresenterState.Maximized) {
|
|
||||||
MaximiseButton.Visibility = Visibility.Collapsed;
|
|
||||||
RestoreButton.Visibility = Visibility.Visible;
|
|
||||||
} else if (_presenter.State == OverlappedPresenterState.Restored) {
|
|
||||||
MaximiseButton.Visibility = Visibility.Visible;
|
|
||||||
RestoreButton.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MiniButton_Click(object sender, RoutedEventArgs e) {
|
|
||||||
AppWindow.Resize(new Windows.Graphics.SizeInt32(144, 48));
|
|
||||||
MiniButton.Visibility = Visibility.Collapsed;
|
|
||||||
UnminiButton.Visibility = Visibility.Visible;
|
|
||||||
MinimiseButton.Visibility = Visibility.Collapsed;
|
|
||||||
MaximiseButton.Visibility = Visibility.Collapsed;
|
|
||||||
RestoreButton.Visibility = Visibility.Collapsed;
|
|
||||||
CloseButton.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UnminiButton_Click(object sender, RoutedEventArgs e) {
|
|
||||||
AppWindow.Resize(new Windows.Graphics.SizeInt32(800, 600));
|
|
||||||
MiniButton.Visibility = Visibility.Visible;
|
|
||||||
UnminiButton.Visibility = Visibility.Collapsed;
|
|
||||||
MinimiseButton.Visibility = Visibility.Visible;
|
|
||||||
MaximiseButton.Visibility = Visibility.Visible;
|
|
||||||
RestoreButton.Visibility = Visibility.Collapsed;
|
|
||||||
CloseButton.Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RootFrame_Navigated(object sender, NavigationEventArgs e) {
|
|
||||||
AppWindow.Title = Title = App.ResourceLoader.GetString($"{e.SourcePageType.Name}Title");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AOTButton_Click(object sender, RoutedEventArgs e) {
|
|
||||||
_presenter.IsAlwaysOnTop = !_presenter.IsAlwaysOnTop;
|
|
||||||
string prefix;
|
|
||||||
if (_presenter.IsAlwaysOnTop) {
|
|
||||||
prefix = "Show";
|
|
||||||
} else {
|
|
||||||
prefix = "Hide";
|
|
||||||
}
|
|
||||||
(PinnedFontIcon.Resources[$"{prefix}PinnedFontIconStoryboard"] as Storyboard)!.Begin();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Messages
|
||||||
|
{
|
||||||
|
public class ShowNotificatonMessage(Notification value)
|
||||||
|
: ValueChangedMessage<Notification>(value) { }
|
||||||
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
using System;
|
||||||
using DevWinUI;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
using DevWinUI;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Messages {
|
namespace BetterLyrics.WinUI3.Messages
|
||||||
public class SystemBackdropChangedMessage : ValueChangedMessage<BackdropType> {
|
{
|
||||||
public SystemBackdropChangedMessage(BackdropType value) : base(value) {
|
public class SystemBackdropChangedMessage(BackdropType value)
|
||||||
}
|
: ValueChangedMessage<BackdropType>(value) { }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
using System;
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Messages {
|
namespace BetterLyrics.WinUI3.Messages
|
||||||
public class ThemeChangedMessage : ValueChangedMessage<ElementTheme> {
|
{
|
||||||
public ThemeChangedMessage(ElementTheme value) : base(value) {
|
public class ThemeChangedMessage(ElementTheme value)
|
||||||
}
|
: ValueChangedMessage<ElementTheme>(value) { }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models {
|
namespace BetterLyrics.WinUI3.Models
|
||||||
public enum Language {
|
{
|
||||||
|
public enum Language
|
||||||
|
{
|
||||||
FollowSystem,
|
FollowSystem,
|
||||||
English,
|
English,
|
||||||
SimplifiedChinese,
|
SimplifiedChinese,
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models {
|
namespace BetterLyrics.WinUI3.Models
|
||||||
public enum LyricsAlignmentType {
|
{
|
||||||
|
public enum LyricsAlignmentType
|
||||||
|
{
|
||||||
Left,
|
Left,
|
||||||
Center,
|
Center,
|
||||||
Right,
|
Right,
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Models
|
||||||
|
{
|
||||||
|
public enum LyricsFontColorType
|
||||||
|
{
|
||||||
|
Default,
|
||||||
|
Dominant,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using System.Collections.Generic;
|
||||||
using Microsoft.Graphics.Canvas.Text;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Microsoft.Graphics.Canvas.Text;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models {
|
namespace BetterLyrics.WinUI3.Models
|
||||||
public class LyricsLine {
|
{
|
||||||
|
public class LyricsLine
|
||||||
|
{
|
||||||
public List<string> Texts { get; set; } = [];
|
public List<string> Texts { get; set; } = [];
|
||||||
|
|
||||||
public int LanguageIndex { get; set; } = 0;
|
public int LanguageIndex { get; set; } = 0;
|
||||||
@@ -37,6 +38,5 @@ namespace BetterLyrics.WinUI3.Models {
|
|||||||
public float Opacity { get; set; }
|
public float Opacity { get; set; }
|
||||||
|
|
||||||
public CanvasTextLayout TextLayout { get; set; }
|
public CanvasTextLayout TextLayout { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,23 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models {
|
namespace BetterLyrics.WinUI3.Models
|
||||||
public enum LyricsPlayingState {
|
{
|
||||||
|
public enum LyricsPlayingState
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Not played yet, will be playing in the future
|
/// Not played yet, will be playing in the future
|
||||||
/// </summary>
|
/// </summary>
|
||||||
NotPlayed,
|
NotPlayed,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Playing
|
/// Playing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Playing,
|
Playing,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Has already played
|
/// Has already played
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Played
|
Played,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
using SQLite;
|
using SQLite;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models {
|
namespace BetterLyrics.WinUI3.Models
|
||||||
public class MetadataIndex {
|
{
|
||||||
|
public class MetadataIndex
|
||||||
|
{
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
public string Path { get; set; }
|
public string? Path { get; set; }
|
||||||
public string Title { get; set; }
|
public string? Title { get; set; }
|
||||||
public string Artist { get; set; }
|
public string? Artist { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models {
|
|
||||||
public partial class MusicFolder : ObservableObject {
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _path;
|
|
||||||
|
|
||||||
public bool IsValid => Directory.Exists(Path);
|
|
||||||
|
|
||||||
public MusicFolder(string path) {
|
|
||||||
Path = path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Models
|
||||||
|
{
|
||||||
|
public partial class Notification : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty]
|
||||||
|
private InfoBarSeverity _severity;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _message;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isForeverDismissable;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private Visibility _visibility;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _relatedSettingsKeyName;
|
||||||
|
|
||||||
|
public Notification(
|
||||||
|
string? message = null,
|
||||||
|
InfoBarSeverity severity = InfoBarSeverity.Informational,
|
||||||
|
bool isForeverDismissable = false,
|
||||||
|
string? relatedSettingsKeyName = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Severity = severity;
|
||||||
|
IsForeverDismissable = isForeverDismissable;
|
||||||
|
Visibility = IsForeverDismissable ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
RelatedSettingsKeyName = relatedSettingsKeyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Models
|
||||||
|
{
|
||||||
|
public enum TitleBarType
|
||||||
|
{
|
||||||
|
Compact,
|
||||||
|
Extended,
|
||||||
|
}
|
||||||
|
}
|
||||||
116
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Program.cs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.Windows.AppLifecycle;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
WinRT.ComWrappersSupport.InitializeComWrappers();
|
||||||
|
bool isRedirect = DecideRedirection();
|
||||||
|
|
||||||
|
if (!isRedirect)
|
||||||
|
{
|
||||||
|
Application.Start(
|
||||||
|
(p) =>
|
||||||
|
{
|
||||||
|
var context = new DispatcherQueueSynchronizationContext(
|
||||||
|
DispatcherQueue.GetForCurrentThread()
|
||||||
|
);
|
||||||
|
SynchronizationContext.SetSynchronizationContext(context);
|
||||||
|
_ = new App();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool DecideRedirection()
|
||||||
|
{
|
||||||
|
bool isRedirect = false;
|
||||||
|
AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
|
||||||
|
ExtendedActivationKind kind = args.Kind;
|
||||||
|
AppInstance keyInstance = AppInstance.FindOrRegisterForKey(Helper.AppInfo.AppName);
|
||||||
|
|
||||||
|
if (keyInstance.IsCurrent)
|
||||||
|
{
|
||||||
|
keyInstance.Activated += OnActivated;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isRedirect = true;
|
||||||
|
RedirectActivationTo(args, keyInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isRedirect;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
private static extern IntPtr CreateEvent(
|
||||||
|
IntPtr lpEventAttributes,
|
||||||
|
bool bManualReset,
|
||||||
|
bool bInitialState,
|
||||||
|
string lpName
|
||||||
|
);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern bool SetEvent(IntPtr hEvent);
|
||||||
|
|
||||||
|
[DllImport("ole32.dll")]
|
||||||
|
private static extern uint CoWaitForMultipleObjects(
|
||||||
|
uint dwFlags,
|
||||||
|
uint dwMilliseconds,
|
||||||
|
ulong nHandles,
|
||||||
|
IntPtr[] pHandles,
|
||||||
|
out uint dwIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||||
|
|
||||||
|
private static IntPtr redirectEventHandle = IntPtr.Zero;
|
||||||
|
|
||||||
|
// Do the redirection on another thread, and use a non-blocking
|
||||||
|
// wait method to wait for the redirection to complete.
|
||||||
|
public static void RedirectActivationTo(
|
||||||
|
AppActivationArguments args,
|
||||||
|
AppInstance keyInstance
|
||||||
|
)
|
||||||
|
{
|
||||||
|
redirectEventHandle = CreateEvent(IntPtr.Zero, true, false, null);
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
keyInstance.RedirectActivationToAsync(args).AsTask().Wait();
|
||||||
|
SetEvent(redirectEventHandle);
|
||||||
|
});
|
||||||
|
|
||||||
|
uint CWMO_DEFAULT = 0;
|
||||||
|
uint INFINITE = 0xFFFFFFFF;
|
||||||
|
_ = CoWaitForMultipleObjects(
|
||||||
|
CWMO_DEFAULT,
|
||||||
|
INFINITE,
|
||||||
|
1,
|
||||||
|
[redirectEventHandle],
|
||||||
|
out uint handleIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
// Bring the window to the foreground
|
||||||
|
Process process = Process.GetProcessById((int)keyInstance.ProcessId);
|
||||||
|
SetForegroundWindow(process.MainWindowHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnActivated(object sender, AppActivationArguments args)
|
||||||
|
{
|
||||||
|
ExtendedActivationKind kind = args.Kind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BetterLyrics.WinUI3.Services.Settings;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using Microsoft.Graphics.Canvas;
|
||||||
|
using Microsoft.Graphics.Canvas.Effects;
|
||||||
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Windows.Graphics.Imaging;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Rendering
|
||||||
|
{
|
||||||
|
public class CoverBackgroundRenderer
|
||||||
|
{
|
||||||
|
private readonly SettingsService _settingsService;
|
||||||
|
|
||||||
|
public float RotateAngle { get; set; } = 0f;
|
||||||
|
|
||||||
|
private SoftwareBitmap? _lastSoftwareBitmap = null;
|
||||||
|
private SoftwareBitmap? _softwareBitmap = null;
|
||||||
|
public SoftwareBitmap? SoftwareBitmap
|
||||||
|
{
|
||||||
|
get => _softwareBitmap;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_softwareBitmap != null)
|
||||||
|
{
|
||||||
|
_lastSoftwareBitmap = _softwareBitmap;
|
||||||
|
_transitionStartTime = DateTimeOffset.Now;
|
||||||
|
_isTransitioning = true;
|
||||||
|
_transitionAlpha = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
_softwareBitmap = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float _transitionAlpha = 1f;
|
||||||
|
private TimeSpan _transitionDuration = TimeSpan.FromMilliseconds(1000);
|
||||||
|
private DateTimeOffset _transitionStartTime;
|
||||||
|
private bool _isTransitioning = false;
|
||||||
|
|
||||||
|
public CoverBackgroundRenderer()
|
||||||
|
{
|
||||||
|
_settingsService = Ioc.Default.GetService<SettingsService>()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||||
|
{
|
||||||
|
if (!_settingsService.IsCoverOverlayEnabled || SoftwareBitmap == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ds.Transform = Matrix3x2.CreateRotation(RotateAngle, control.Size.ToVector2() * 0.5f);
|
||||||
|
|
||||||
|
var overlappedCovers = new CanvasCommandList(control);
|
||||||
|
using var overlappedCoversDs = overlappedCovers.CreateDrawingSession();
|
||||||
|
|
||||||
|
if (_isTransitioning && _lastSoftwareBitmap != null)
|
||||||
|
{
|
||||||
|
DrawImgae(control, overlappedCoversDs, _lastSoftwareBitmap, 1 - _transitionAlpha);
|
||||||
|
DrawImgae(control, overlappedCoversDs, SoftwareBitmap, _transitionAlpha);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawImgae(control, overlappedCoversDs, SoftwareBitmap, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var coverOverlayEffect = new OpacityEffect
|
||||||
|
{
|
||||||
|
Opacity = _settingsService.CoverOverlayOpacity / 100f,
|
||||||
|
Source = new GaussianBlurEffect
|
||||||
|
{
|
||||||
|
BlurAmount = _settingsService.CoverOverlayBlurAmount,
|
||||||
|
Source = overlappedCovers,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ds.DrawImage(coverOverlayEffect);
|
||||||
|
|
||||||
|
ds.Transform = Matrix3x2.Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawImgae(
|
||||||
|
ICanvasAnimatedControl control,
|
||||||
|
CanvasDrawingSession ds,
|
||||||
|
SoftwareBitmap softwareBitmap,
|
||||||
|
float opacity
|
||||||
|
)
|
||||||
|
{
|
||||||
|
float imageWidth = (float)(softwareBitmap.PixelWidth * 96f / softwareBitmap.DpiX);
|
||||||
|
float imageHeight = (float)(softwareBitmap.PixelHeight * 96f / softwareBitmap.DpiY);
|
||||||
|
var scaleFactor =
|
||||||
|
(float)Math.Sqrt(Math.Pow(control.Size.Width, 2) + Math.Pow(control.Size.Height, 2))
|
||||||
|
/ Math.Min(imageWidth, imageHeight);
|
||||||
|
|
||||||
|
ds.DrawImage(
|
||||||
|
new OpacityEffect
|
||||||
|
{
|
||||||
|
Source = new ScaleEffect
|
||||||
|
{
|
||||||
|
InterpolationMode = CanvasImageInterpolation.HighQualityCubic,
|
||||||
|
BorderMode = EffectBorderMode.Hard,
|
||||||
|
Scale = new Vector2(scaleFactor),
|
||||||
|
Source = CanvasBitmap.CreateFromSoftwareBitmap(control, softwareBitmap),
|
||||||
|
},
|
||||||
|
Opacity = opacity,
|
||||||
|
},
|
||||||
|
(float)control.Size.Width / 2 - imageWidth * scaleFactor / 2,
|
||||||
|
(float)control.Size.Height / 2 - imageHeight * scaleFactor / 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Calculate(ICanvasAnimatedControl control)
|
||||||
|
{
|
||||||
|
if (_isTransitioning)
|
||||||
|
{
|
||||||
|
var elapsed = DateTimeOffset.Now - _transitionStartTime;
|
||||||
|
float progress = (float)(
|
||||||
|
elapsed.TotalMilliseconds / _transitionDuration.TotalMilliseconds
|
||||||
|
);
|
||||||
|
_transitionAlpha = Math.Clamp(progress, 0f, 1f);
|
||||||
|
|
||||||
|
if (_transitionAlpha >= 1f)
|
||||||
|
{
|
||||||
|
_isTransitioning = false;
|
||||||
|
_lastSoftwareBitmap?.Dispose();
|
||||||
|
_lastSoftwareBitmap = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,422 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Services.Settings;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using Microsoft.Graphics.Canvas;
|
||||||
|
using Microsoft.Graphics.Canvas.Brushes;
|
||||||
|
using Microsoft.Graphics.Canvas.Text;
|
||||||
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
|
using Microsoft.UI;
|
||||||
|
using Microsoft.UI.Text;
|
||||||
|
using Windows.UI;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Rendering
|
||||||
|
{
|
||||||
|
public class PureLyricsRenderer
|
||||||
|
{
|
||||||
|
private readonly SettingsService _settingsService;
|
||||||
|
|
||||||
|
private readonly float _defaultOpacity = 0.3f;
|
||||||
|
private readonly float _highlightedOpacity = 1.0f;
|
||||||
|
|
||||||
|
private readonly float _defaultScale = 0.95f;
|
||||||
|
private readonly float _highlightedScale = 1.0f;
|
||||||
|
|
||||||
|
private readonly int _lineEnteringDurationMs = 800;
|
||||||
|
private readonly int _lineExitingDurationMs = 800;
|
||||||
|
private readonly int _lineScrollDurationMs = 800;
|
||||||
|
|
||||||
|
private float _lastTotalYScroll = 0.0f;
|
||||||
|
private float _totalYScroll = 0.0f;
|
||||||
|
|
||||||
|
private int _startVisibleLineIndex = -1;
|
||||||
|
private int _endVisibleLineIndex = -1;
|
||||||
|
|
||||||
|
private bool _forceToScroll = false;
|
||||||
|
|
||||||
|
private readonly double _rightMargin = 36;
|
||||||
|
|
||||||
|
public double LimitedLineWidth { get; set; } = 0;
|
||||||
|
public double CanvasWidth { get; set; } = 0;
|
||||||
|
public double CanvasHeight { get; set; } = 0;
|
||||||
|
|
||||||
|
public TimeSpan CurrentTime { get; set; }
|
||||||
|
|
||||||
|
public List<LyricsLine> LyricsLines { get; set; } = [];
|
||||||
|
|
||||||
|
public PureLyricsRenderer()
|
||||||
|
{
|
||||||
|
_settingsService = Ioc.Default.GetService<SettingsService>()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tuple<int, int> GetVisibleLyricsLineIndexBoundaries()
|
||||||
|
{
|
||||||
|
// _logger.LogDebug($"{_startVisibleLineIndex} {_endVisibleLineIndex}");
|
||||||
|
return new Tuple<int, int>(_startVisibleLineIndex, _endVisibleLineIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tuple<int, int> GetMaxLyricsLineIndexBoundaries()
|
||||||
|
{
|
||||||
|
if (LyricsLines.Count == 0)
|
||||||
|
{
|
||||||
|
return new Tuple<int, int>(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tuple<int, int>(0, LyricsLines.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(
|
||||||
|
ICanvasAnimatedControl control,
|
||||||
|
CanvasDrawingSession ds,
|
||||||
|
byte r,
|
||||||
|
byte g,
|
||||||
|
byte b
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var (displayStartLineIndex, displayEndLineIndex) =
|
||||||
|
GetVisibleLyricsLineIndexBoundaries();
|
||||||
|
|
||||||
|
for (
|
||||||
|
int i = displayStartLineIndex;
|
||||||
|
LyricsLines.Count > 0
|
||||||
|
&& i >= 0
|
||||||
|
&& i < LyricsLines.Count
|
||||||
|
&& i <= displayEndLineIndex;
|
||||||
|
i++
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var line = LyricsLines[i];
|
||||||
|
|
||||||
|
if (line.TextLayout == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float progressPerChar = 1f / line.Text.Length;
|
||||||
|
|
||||||
|
var position = line.Position;
|
||||||
|
|
||||||
|
float centerX = position.X;
|
||||||
|
float centerY = position.Y + (float)line.TextLayout.LayoutBounds.Height / 2;
|
||||||
|
|
||||||
|
switch ((LyricsAlignmentType)_settingsService.LyricsAlignmentType)
|
||||||
|
{
|
||||||
|
case LyricsAlignmentType.Left:
|
||||||
|
line.TextLayout.HorizontalAlignment = CanvasHorizontalAlignment.Left;
|
||||||
|
break;
|
||||||
|
case LyricsAlignmentType.Center:
|
||||||
|
line.TextLayout.HorizontalAlignment = CanvasHorizontalAlignment.Center;
|
||||||
|
centerX += (float)LimitedLineWidth / 2;
|
||||||
|
break;
|
||||||
|
case LyricsAlignmentType.Right:
|
||||||
|
line.TextLayout.HorizontalAlignment = CanvasHorizontalAlignment.Right;
|
||||||
|
centerX += (float)LimitedLineWidth;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIndex = 0;
|
||||||
|
|
||||||
|
// Set brush
|
||||||
|
for (int j = 0; j < line.TextLayout.LineCount; j++)
|
||||||
|
{
|
||||||
|
int count = line.TextLayout.LineMetrics[j].CharacterCount;
|
||||||
|
var regions = line.TextLayout.GetCharacterRegions(startIndex, count);
|
||||||
|
float subLinePlayingProgress = Math.Clamp(
|
||||||
|
(line.PlayingProgress * line.Text.Length - startIndex) / count,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
using var horizontalFillBrush = new CanvasLinearGradientBrush(
|
||||||
|
control,
|
||||||
|
[
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Position = 0,
|
||||||
|
Color = Color.FromArgb((byte)(255 * line.Opacity), r, g, b),
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Position =
|
||||||
|
subLinePlayingProgress * (1 + progressPerChar)
|
||||||
|
- progressPerChar,
|
||||||
|
Color = Color.FromArgb((byte)(255 * line.Opacity), r, g, b),
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Position = subLinePlayingProgress * (1 + progressPerChar),
|
||||||
|
Color = Color.FromArgb((byte)(255 * _defaultOpacity), r, g, b),
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Position = 1.5f,
|
||||||
|
Color = Color.FromArgb((byte)(255 * _defaultOpacity), r, g, b),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
{
|
||||||
|
StartPoint = new Vector2(
|
||||||
|
(float)(regions[0].LayoutBounds.Left + position.X),
|
||||||
|
0
|
||||||
|
),
|
||||||
|
EndPoint = new Vector2(
|
||||||
|
(float)(regions[^1].LayoutBounds.Right + position.X),
|
||||||
|
0
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
line.TextLayout.SetBrush(startIndex, count, horizontalFillBrush);
|
||||||
|
startIndex += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
ds.Transform =
|
||||||
|
Matrix3x2.CreateScale(line.Scale, new Vector2(centerX, centerY))
|
||||||
|
* Matrix3x2.CreateTranslation(0, _totalYScroll);
|
||||||
|
// _logger.LogDebug(_totalYScroll);
|
||||||
|
|
||||||
|
ds.DrawTextLayout(line.TextLayout, position, Colors.Transparent);
|
||||||
|
|
||||||
|
// Reset scale
|
||||||
|
ds.Transform = Matrix3x2.Identity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ForceToScrollToCurrentPlayingLineAsync()
|
||||||
|
{
|
||||||
|
_forceToScroll = true;
|
||||||
|
await Task.Delay(1);
|
||||||
|
_forceToScroll = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ReLayoutAsync(ICanvasAnimatedControl control)
|
||||||
|
{
|
||||||
|
if (control == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float leftMargin = (float)(CanvasWidth - LimitedLineWidth - _rightMargin);
|
||||||
|
|
||||||
|
using CanvasTextFormat textFormat = new()
|
||||||
|
{
|
||||||
|
FontSize = _settingsService.LyricsFontSize,
|
||||||
|
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = CanvasVerticalAlignment.Top,
|
||||||
|
FontWeight = FontWeights.Bold,
|
||||||
|
//FontFamily = "Segoe UI Mono",
|
||||||
|
};
|
||||||
|
float y = (float)CanvasHeight / 2;
|
||||||
|
|
||||||
|
// Init Positions
|
||||||
|
for (int i = 0; i < LyricsLines.Count; i++)
|
||||||
|
{
|
||||||
|
var line = LyricsLines[i];
|
||||||
|
|
||||||
|
// Calculate layout bounds
|
||||||
|
line.TextLayout = new CanvasTextLayout(
|
||||||
|
control.Device,
|
||||||
|
line.Text,
|
||||||
|
textFormat,
|
||||||
|
(float)LimitedLineWidth,
|
||||||
|
(float)CanvasHeight
|
||||||
|
);
|
||||||
|
line.Position = new Vector2(leftMargin, y);
|
||||||
|
|
||||||
|
y +=
|
||||||
|
(float)line.TextLayout.LayoutBounds.Height
|
||||||
|
/ line.TextLayout.LineCount
|
||||||
|
* (line.TextLayout.LineCount + _settingsService.LyricsLineSpacingFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
await ForceToScrollToCurrentPlayingLineAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CalculateScaleAndOpacity(int currentPlayingLineIndex)
|
||||||
|
{
|
||||||
|
var (startLineIndex, endLineIndex) = GetMaxLyricsLineIndexBoundaries();
|
||||||
|
|
||||||
|
for (int i = startLineIndex; LyricsLines.Count > 0 && i <= endLineIndex; i++)
|
||||||
|
{
|
||||||
|
var line = LyricsLines[i];
|
||||||
|
|
||||||
|
bool linePlaying = i == currentPlayingLineIndex;
|
||||||
|
|
||||||
|
var lineEnteringDurationMs = Math.Min(line.DurationMs, _lineEnteringDurationMs);
|
||||||
|
var lineExitingDurationMs = _lineExitingDurationMs;
|
||||||
|
if (i + 1 <= endLineIndex)
|
||||||
|
{
|
||||||
|
lineExitingDurationMs = Math.Min(
|
||||||
|
LyricsLines[i + 1].DurationMs,
|
||||||
|
lineExitingDurationMs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
float lineEnteringProgress = 0.0f;
|
||||||
|
float lineExitingProgress = 0.0f;
|
||||||
|
|
||||||
|
bool lineEntering = false;
|
||||||
|
bool lineExiting = false;
|
||||||
|
|
||||||
|
float scale = _defaultScale;
|
||||||
|
float opacity = _defaultOpacity;
|
||||||
|
|
||||||
|
float playProgress = 0;
|
||||||
|
|
||||||
|
if (linePlaying)
|
||||||
|
{
|
||||||
|
line.PlayingState = LyricsPlayingState.Playing;
|
||||||
|
|
||||||
|
scale = _highlightedScale;
|
||||||
|
opacity = _highlightedOpacity;
|
||||||
|
|
||||||
|
playProgress =
|
||||||
|
((float)CurrentTime.TotalMilliseconds - line.StartPlayingTimestampMs)
|
||||||
|
/ line.DurationMs;
|
||||||
|
|
||||||
|
var durationFromStartMs =
|
||||||
|
CurrentTime.TotalMilliseconds - line.StartPlayingTimestampMs;
|
||||||
|
lineEntering = durationFromStartMs <= lineEnteringDurationMs;
|
||||||
|
if (lineEntering)
|
||||||
|
{
|
||||||
|
lineEnteringProgress = (float)durationFromStartMs / lineEnteringDurationMs;
|
||||||
|
scale =
|
||||||
|
_defaultScale
|
||||||
|
+ (_highlightedScale - _defaultScale) * (float)lineEnteringProgress;
|
||||||
|
opacity =
|
||||||
|
_defaultOpacity
|
||||||
|
+ (_highlightedOpacity - _defaultOpacity) * (float)lineEnteringProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (i < currentPlayingLineIndex)
|
||||||
|
{
|
||||||
|
line.PlayingState = LyricsPlayingState.Played;
|
||||||
|
playProgress = 1;
|
||||||
|
|
||||||
|
var durationToEndMs =
|
||||||
|
CurrentTime.TotalMilliseconds - line.EndPlayingTimestampMs;
|
||||||
|
lineExiting = durationToEndMs <= lineExitingDurationMs;
|
||||||
|
if (lineExiting)
|
||||||
|
{
|
||||||
|
lineExitingProgress = (float)durationToEndMs / lineExitingDurationMs;
|
||||||
|
scale =
|
||||||
|
_highlightedScale
|
||||||
|
- (_highlightedScale - _defaultScale) * (float)lineExitingProgress;
|
||||||
|
opacity =
|
||||||
|
_highlightedOpacity
|
||||||
|
- (_highlightedOpacity - _defaultOpacity)
|
||||||
|
* (float)lineExitingProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line.PlayingState = LyricsPlayingState.NotPlayed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line.EnteringProgress = lineEnteringProgress;
|
||||||
|
line.ExitingProgress = lineExitingProgress;
|
||||||
|
|
||||||
|
line.Scale = scale;
|
||||||
|
line.Opacity = opacity;
|
||||||
|
|
||||||
|
line.PlayingProgress = playProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CalculatePosition(ICanvasAnimatedControl control, int currentPlayingLineIndex)
|
||||||
|
{
|
||||||
|
if (currentPlayingLineIndex < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (startLineIndex, endLineIndex) = GetMaxLyricsLineIndexBoundaries();
|
||||||
|
|
||||||
|
if (startLineIndex < 0 || endLineIndex < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set _scrollOffsetY
|
||||||
|
LyricsLine? currentPlayingLine = LyricsLines?[currentPlayingLineIndex];
|
||||||
|
|
||||||
|
if (currentPlayingLine == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPlayingLine.TextLayout == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lineScrollingProgress =
|
||||||
|
(CurrentTime.TotalMilliseconds - currentPlayingLine.StartPlayingTimestampMs)
|
||||||
|
/ Math.Min(_lineScrollDurationMs, currentPlayingLine.DurationMs);
|
||||||
|
|
||||||
|
var targetYScrollOffset = (float)(
|
||||||
|
-currentPlayingLine.Position.Y
|
||||||
|
+ LyricsLines![0].Position.Y
|
||||||
|
- currentPlayingLine.TextLayout.LayoutBounds.Height / 2
|
||||||
|
- _lastTotalYScroll
|
||||||
|
);
|
||||||
|
|
||||||
|
var yScrollOffset =
|
||||||
|
targetYScrollOffset
|
||||||
|
* EasingHelper.SmootherStep((float)Math.Min(1, lineScrollingProgress));
|
||||||
|
|
||||||
|
bool isScrollingNow = lineScrollingProgress <= 1;
|
||||||
|
|
||||||
|
if (isScrollingNow)
|
||||||
|
{
|
||||||
|
_totalYScroll = _lastTotalYScroll + yScrollOffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_forceToScroll && Math.Abs(targetYScrollOffset) >= 1)
|
||||||
|
{
|
||||||
|
_totalYScroll = _lastTotalYScroll + targetYScrollOffset;
|
||||||
|
}
|
||||||
|
_lastTotalYScroll = _totalYScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
_startVisibleLineIndex = _endVisibleLineIndex = -1;
|
||||||
|
|
||||||
|
// Update Positions
|
||||||
|
for (int i = startLineIndex; i >= 0 && i <= endLineIndex; i++)
|
||||||
|
{
|
||||||
|
var line = LyricsLines[i];
|
||||||
|
|
||||||
|
if (_totalYScroll + line.Position.Y + line.TextLayout.LayoutBounds.Height >= 0)
|
||||||
|
{
|
||||||
|
if (_startVisibleLineIndex == -1)
|
||||||
|
{
|
||||||
|
_startVisibleLineIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
_totalYScroll + line.Position.Y + line.TextLayout.LayoutBounds.Height
|
||||||
|
>= control.Size.Height
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (_endVisibleLineIndex == -1)
|
||||||
|
{
|
||||||
|
_endVisibleLineIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_startVisibleLineIndex != -1 && _endVisibleLineIndex == -1)
|
||||||
|
{
|
||||||
|
_endVisibleLineIndex = endLineIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,76 +1,127 @@
|
|||||||
using ATL;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ATL;
|
||||||
using BetterLyrics.WinUI3.Messages;
|
using BetterLyrics.WinUI3.Messages;
|
||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
using BetterLyrics.WinUI3.Services.Settings;
|
using BetterLyrics.WinUI3.Services.Settings;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Ude;
|
using Ude;
|
||||||
|
using Windows.Media.Control;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Services.Database {
|
namespace BetterLyrics.WinUI3.Services.Database
|
||||||
public class DatabaseService {
|
{
|
||||||
|
public class DatabaseService
|
||||||
|
{
|
||||||
private readonly SQLiteConnection _connection;
|
private readonly SQLiteConnection _connection;
|
||||||
|
|
||||||
private readonly CharsetDetector _charsetDetector = new();
|
private readonly CharsetDetector _charsetDetector = new();
|
||||||
|
|
||||||
public DatabaseService() {
|
public DatabaseService()
|
||||||
string dbPath = Path.Combine(
|
{
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
_connection = new SQLiteConnection(Helper.AppInfo.DatabasePath);
|
||||||
"MusicMetadataIndex.db"
|
if (_connection.GetTableInfo("MetadataIndex").Count == 0)
|
||||||
);
|
{
|
||||||
_connection = new SQLiteConnection(dbPath);
|
|
||||||
_connection.CreateTable<MetadataIndex>();
|
_connection.CreateTable<MetadataIndex>();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task RebuildMusicMetadataIndexDatabaseAsync(IList<MusicFolder> musicFolders) {
|
public async Task RebuildMusicMetadataIndexDatabaseAsync(IList<string> paths)
|
||||||
await Task.Run(() => {
|
{
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
_connection.DeleteAll<MetadataIndex>();
|
_connection.DeleteAll<MetadataIndex>();
|
||||||
foreach (var localMusicFolder in musicFolders) {
|
foreach (var path in paths)
|
||||||
if (localMusicFolder.IsValid) {
|
{
|
||||||
foreach (var file in Directory.GetFiles(localMusicFolder.Path)) {
|
if (Directory.Exists(path))
|
||||||
|
{
|
||||||
|
foreach (var file in Directory.GetFiles(path))
|
||||||
|
{
|
||||||
var fileExtension = Path.GetExtension(file);
|
var fileExtension = Path.GetExtension(file);
|
||||||
var track = new Track(file);
|
var track = new Track(file);
|
||||||
_connection.Insert(new MetadataIndex {
|
_connection.Insert(
|
||||||
|
new MetadataIndex
|
||||||
|
{
|
||||||
Path = file,
|
Path = file,
|
||||||
Title = track.Title,
|
Title = track.Title,
|
||||||
Artist = track.Artist,
|
Artist = track.Artist,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Track? GetMusicMetadata(string? title, string? artist) {
|
public Track? GetMusicMetadata(
|
||||||
var founds = _connection.Table<MetadataIndex>()
|
GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProps
|
||||||
.Where(m => m.Title == title && m.Artist == artist).ToList();
|
)
|
||||||
if (founds == null || founds.Count == 0) {
|
{
|
||||||
|
if (mediaProps == null || mediaProps.Title == null || mediaProps.Artist == null)
|
||||||
return null;
|
return null;
|
||||||
} else {
|
|
||||||
|
var founds = _connection
|
||||||
|
.Table<MetadataIndex>()
|
||||||
|
// Look up by Title and Artist (these two props were fetched by reading metadata in music file befoe) first
|
||||||
|
// then by Path (music file name usually contains song name and artist so this can be a second way to look up for)
|
||||||
|
// Please note for .lrc file, only the second way works for it
|
||||||
|
.Where(m =>
|
||||||
|
(
|
||||||
|
m.Title != null
|
||||||
|
&& m.Artist != null
|
||||||
|
&& m.Title.Contains(mediaProps.Title)
|
||||||
|
&& m.Artist.Contains(mediaProps.Artist)
|
||||||
|
)
|
||||||
|
|| (
|
||||||
|
m.Path != null
|
||||||
|
&& m.Path.Contains(mediaProps.Title)
|
||||||
|
&& m.Path.Contains(mediaProps.Artist)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ToList();
|
||||||
|
if (founds == null || founds.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
var first = new Track(founds[0].Path);
|
var first = new Track(founds[0].Path);
|
||||||
if (founds.Count == 1) {
|
if (founds.Count == 1)
|
||||||
|
{
|
||||||
return first;
|
return first;
|
||||||
} else {
|
}
|
||||||
if (first.Lyrics.Exists()) {
|
else
|
||||||
|
{
|
||||||
|
if (first.Lyrics.Exists())
|
||||||
|
{
|
||||||
return first;
|
return first;
|
||||||
} else {
|
}
|
||||||
foreach (var found in founds) {
|
else
|
||||||
if (found.Path.EndsWith(".lrc")) {
|
{
|
||||||
using (FileStream fs = File.OpenRead(found.Path)) {
|
foreach (var found in founds)
|
||||||
|
{
|
||||||
|
if (found.Path.EndsWith(".lrc"))
|
||||||
|
{
|
||||||
|
using (FileStream fs = File.OpenRead(found.Path))
|
||||||
|
{
|
||||||
_charsetDetector.Feed(fs);
|
_charsetDetector.Feed(fs);
|
||||||
_charsetDetector.DataEnd();
|
_charsetDetector.DataEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
string content;
|
string content;
|
||||||
if (_charsetDetector.Charset != null) {
|
if (_charsetDetector.Charset != null)
|
||||||
Encoding encoding = Encoding.GetEncoding(_charsetDetector.Charset);
|
{
|
||||||
|
Encoding encoding = Encoding.GetEncoding(
|
||||||
|
_charsetDetector.Charset
|
||||||
|
);
|
||||||
content = File.ReadAllText(found.Path, encoding);
|
content = File.ReadAllText(found.Path, encoding);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
content = File.ReadAllText(found.Path, Encoding.UTF8);
|
content = File.ReadAllText(found.Path, Encoding.UTF8);
|
||||||
}
|
}
|
||||||
first.Lyrics.ParseLRC(content);
|
first.Lyrics.ParseLRC(content);
|
||||||
@@ -83,7 +134,5 @@ namespace BetterLyrics.WinUI3.Services.Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,38 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Services.Settings {
|
namespace BetterLyrics.WinUI3.Services.Settings
|
||||||
public static class SettingsDefaultValues {
|
{
|
||||||
|
public static class SettingsDefaultValues
|
||||||
|
{
|
||||||
public const bool IsFirstRun = true;
|
public const bool IsFirstRun = true;
|
||||||
|
|
||||||
// Theme
|
// Theme
|
||||||
public const int ThemeType = 0; // Follow system
|
public const int ThemeType = 0; // Follow system
|
||||||
|
|
||||||
// Language
|
// Language
|
||||||
public const int Language = 0; // Default
|
public const int Language = 0; // Default
|
||||||
|
|
||||||
// Music
|
// Music
|
||||||
public const string MusicLibraries = "[]";
|
public const string MusicLibraries = "[]";
|
||||||
|
|
||||||
// Backdrop
|
// Backdrop
|
||||||
public const int BackdropType = 5; // Acrylic Base
|
public const int BackdropType = 5; // Acrylic Base
|
||||||
public const bool IsCoverOverlayEnabled = true;
|
public const bool IsCoverOverlayEnabled = true;
|
||||||
public const bool IsDynamicCoverOverlay = true;
|
public const bool IsDynamicCoverOverlay = true;
|
||||||
public const int CoverOverlayOpacity = 100; // 1.0
|
public const int CoverOverlayOpacity = 100; // 1.0
|
||||||
public const int CoverOverlayBlurAmount = 200;
|
public const int CoverOverlayBlurAmount = 200;
|
||||||
|
|
||||||
|
// Title bar
|
||||||
|
public const int TitleBarType = 0;
|
||||||
|
|
||||||
|
// Album art
|
||||||
|
public const int CoverImageRadius = 24;
|
||||||
|
|
||||||
// Lyrics
|
// Lyrics
|
||||||
public const int LyricsAlignmentType = 1; // Center
|
public const int LyricsAlignmentType = 1; // Center
|
||||||
public const int LyricsBlurAmount = 0;
|
public const int LyricsBlurAmount = 0;
|
||||||
@@ -29,5 +41,11 @@ namespace BetterLyrics.WinUI3.Services.Settings {
|
|||||||
public const int LyricsFontSize = 28;
|
public const int LyricsFontSize = 28;
|
||||||
public const bool IsLyricsGlowEffectEnabled = false;
|
public const bool IsLyricsGlowEffectEnabled = false;
|
||||||
public const bool IsLyricsDynamicGlowEffectEnabled = false;
|
public const bool IsLyricsDynamicGlowEffectEnabled = false;
|
||||||
|
public const int LyricsFontColorType = 0; // Default
|
||||||
|
public const int LyricsFontSelectedAccentColorIndex = 0;
|
||||||
|
|
||||||
|
// Notification
|
||||||
|
public const bool NeverShowEnterFullScreenMessage = false;
|
||||||
|
public const bool NeverShowEnterImmersiveModeMessage = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,22 +4,34 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Services.Settings {
|
namespace BetterLyrics.WinUI3.Services.Settings
|
||||||
public static class SettingsKeys {
|
{
|
||||||
|
public static class SettingsKeys
|
||||||
|
{
|
||||||
public const string IsFirstRun = "IsFirstRun";
|
public const string IsFirstRun = "IsFirstRun";
|
||||||
|
|
||||||
// Theme
|
// Theme
|
||||||
public const string ThemeType = "ThemeType";
|
public const string ThemeType = "ThemeType";
|
||||||
|
|
||||||
// Language
|
// Language
|
||||||
public const string Language = "Language";
|
public const string Language = "Language";
|
||||||
|
|
||||||
// Music
|
// Music
|
||||||
public const string MusicLibraries = "MusicLibraries";
|
public const string MusicLibraries = "MusicLibraries";
|
||||||
|
|
||||||
// Backdrop
|
// Backdrop
|
||||||
public const string BackdropType = "BackdropType";
|
public const string BackdropType = "BackdropType";
|
||||||
public const string IsCoverOverlayEnabled = "IsCoverOverlayEnabled";
|
public const string IsCoverOverlayEnabled = "IsCoverOverlayEnabled";
|
||||||
public const string IsDynamicCoverOverlay = "IsDynamicCoverOverlay";
|
public const string IsDynamicCoverOverlay = "IsDynamicCoverOverlay";
|
||||||
public const string CoverOverlayOpacity = "CoverOverlayOpacity";
|
public const string CoverOverlayOpacity = "CoverOverlayOpacity";
|
||||||
public const string CoverOverlayBlurAmount = "CoverOverlayBlurAmount";
|
public const string CoverOverlayBlurAmount = "CoverOverlayBlurAmount";
|
||||||
|
|
||||||
|
// Title bar
|
||||||
|
public const string TitleBarType = "TitleBarType";
|
||||||
|
|
||||||
|
// Album art
|
||||||
|
public const string CoverImageRadius = "CoverImageRadius";
|
||||||
|
|
||||||
// Lyrics
|
// Lyrics
|
||||||
public const string LyricsAlignmentType = "LyricsAlignmentType";
|
public const string LyricsAlignmentType = "LyricsAlignmentType";
|
||||||
public const string LyricsBlurAmount = "LyricsBlurAmount";
|
public const string LyricsBlurAmount = "LyricsBlurAmount";
|
||||||
@@ -28,6 +40,13 @@ namespace BetterLyrics.WinUI3.Services.Settings {
|
|||||||
public const string LyricsFontSize = "LyricsFontSize";
|
public const string LyricsFontSize = "LyricsFontSize";
|
||||||
public const string IsLyricsGlowEffectEnabled = "IsLyricsGlowEffectEnabled";
|
public const string IsLyricsGlowEffectEnabled = "IsLyricsGlowEffectEnabled";
|
||||||
public const string IsLyricsDynamicGlowEffectEnabled = "IsLyricsDynamicGlowEffectEnabled";
|
public const string IsLyricsDynamicGlowEffectEnabled = "IsLyricsDynamicGlowEffectEnabled";
|
||||||
}
|
public const string LyricsFontColorType = "LyricsFontColorType";
|
||||||
|
public const string LyricsFontSelectedAccentColorIndex =
|
||||||
|
"LyricsFontSelectedAccentColorIndex";
|
||||||
|
|
||||||
|
// Notification
|
||||||
|
public const string NeverShowEnterFullScreenMessage = "NeverShowEnterFullScreenMessage";
|
||||||
|
public const string NeverShowEnterImmersiveModeMessage =
|
||||||
|
"NeverShowEnterImmersiveModeMessage";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,71 @@
|
|||||||
using ATL;
|
using System;
|
||||||
using BetterLyrics.WinUI3.Helper;
|
|
||||||
using BetterLyrics.WinUI3.Messages;
|
|
||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using BetterLyrics.WinUI3.Services.Database;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
|
||||||
using DevWinUI;
|
|
||||||
using Microsoft.UI.Composition.SystemBackdrops;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
|
||||||
using Microsoft.UI.Xaml.Media;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Data.Common;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using BetterLyrics.WinUI3.Messages;
|
||||||
using System.Threading.Tasks;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using DevWinUI;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Windows.Globalization;
|
using Windows.Globalization;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
using Windows.System;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Services.Settings {
|
namespace BetterLyrics.WinUI3.Services.Settings
|
||||||
public partial class SettingsService : ObservableObject {
|
{
|
||||||
|
public partial class SettingsService : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly ApplicationDataContainer _localSettings;
|
||||||
|
|
||||||
public bool IsFirstRun {
|
public SettingsService()
|
||||||
|
{
|
||||||
|
_localSettings = ApplicationData.Current.LocalSettings;
|
||||||
|
|
||||||
|
_musicLibraries =
|
||||||
|
[
|
||||||
|
.. JsonConvert.DeserializeObject<List<string>>(
|
||||||
|
Get(SettingsKeys.MusicLibraries, SettingsDefaultValues.MusicLibraries)!
|
||||||
|
)!,
|
||||||
|
];
|
||||||
|
|
||||||
|
_musicLibraries.CollectionChanged += (_, _) => SaveMusicLibraries();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFirstRun
|
||||||
|
{
|
||||||
get => Get(SettingsKeys.IsFirstRun, SettingsDefaultValues.IsFirstRun);
|
get => Get(SettingsKeys.IsFirstRun, SettingsDefaultValues.IsFirstRun);
|
||||||
set => Set(SettingsKeys.IsFirstRun, value);
|
set => Set(SettingsKeys.IsFirstRun, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isRebuildingLyricsIndexDatabase;
|
private bool _isRebuildingLyricsIndexDatabase = false;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isImmersiveMode = false;
|
||||||
|
|
||||||
// Theme
|
// Theme
|
||||||
public int Theme {
|
public int Theme
|
||||||
|
{
|
||||||
get => Get(SettingsKeys.ThemeType, SettingsDefaultValues.ThemeType);
|
get => Get(SettingsKeys.ThemeType, SettingsDefaultValues.ThemeType);
|
||||||
set {
|
set
|
||||||
|
{
|
||||||
Set(SettingsKeys.ThemeType, value);
|
Set(SettingsKeys.ThemeType, value);
|
||||||
WeakReferenceMessenger.Default.Send(new ThemeChangedMessage((ElementTheme)value));
|
WeakReferenceMessenger.Default.Send(new ThemeChangedMessage((ElementTheme)value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Music
|
// Music
|
||||||
private ObservableCollection<MusicFolder> _musicLibraries;
|
private ObservableCollection<string> _musicLibraries;
|
||||||
|
|
||||||
public ObservableCollection<MusicFolder> MusicLibraries {
|
public ObservableCollection<string> MusicLibraries
|
||||||
get {
|
{
|
||||||
if (_musicLibraries == null) {
|
get { return _musicLibraries; }
|
||||||
var list = JsonConvert.DeserializeObject<List<MusicFolder>>(
|
set
|
||||||
Get(SettingsKeys.MusicLibraries, SettingsDefaultValues.MusicLibraries)
|
{
|
||||||
);
|
if (_musicLibraries != null)
|
||||||
|
{
|
||||||
_musicLibraries = new ObservableCollection<MusicFolder>(list);
|
|
||||||
_musicLibraries.CollectionChanged += (_, _) => SaveMusicLibraries();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _musicLibraries;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
if (_musicLibraries != null) {
|
|
||||||
_musicLibraries.CollectionChanged -= (_, _) => SaveMusicLibraries();
|
_musicLibraries.CollectionChanged -= (_, _) => SaveMusicLibraries();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,17 +76,20 @@ namespace BetterLyrics.WinUI3.Services.Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveMusicLibraries() {
|
private void SaveMusicLibraries()
|
||||||
|
{
|
||||||
Set(SettingsKeys.MusicLibraries, JsonConvert.SerializeObject(MusicLibraries.ToList()));
|
Set(SettingsKeys.MusicLibraries, JsonConvert.SerializeObject(MusicLibraries.ToList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Language
|
// Language
|
||||||
public int Language {
|
public int Language
|
||||||
|
{
|
||||||
get => Get(SettingsKeys.Language, SettingsDefaultValues.Language);
|
get => Get(SettingsKeys.Language, SettingsDefaultValues.Language);
|
||||||
set {
|
set
|
||||||
|
{
|
||||||
Set(SettingsKeys.Language, value);
|
Set(SettingsKeys.Language, value);
|
||||||
switch ((Models.Language)Language) {
|
switch ((Models.Language)Language)
|
||||||
|
{
|
||||||
case Models.Language.FollowSystem:
|
case Models.Language.FollowSystem:
|
||||||
ApplicationLanguages.PrimaryLanguageOverride = "";
|
ApplicationLanguages.PrimaryLanguageOverride = "";
|
||||||
break;
|
break;
|
||||||
@@ -103,81 +109,171 @@ namespace BetterLyrics.WinUI3.Services.Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Backdrop
|
// Backdrop
|
||||||
public int BackdropType {
|
public int BackdropType
|
||||||
|
{
|
||||||
get => Get(SettingsKeys.BackdropType, SettingsDefaultValues.BackdropType);
|
get => Get(SettingsKeys.BackdropType, SettingsDefaultValues.BackdropType);
|
||||||
set {
|
set
|
||||||
|
{
|
||||||
Set(SettingsKeys.BackdropType, value);
|
Set(SettingsKeys.BackdropType, value);
|
||||||
WeakReferenceMessenger.Default.Send(new SystemBackdropChangedMessage((BackdropType)value));
|
WeakReferenceMessenger.Default.Send(
|
||||||
|
new SystemBackdropChangedMessage((BackdropType)value)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public bool IsCoverOverlayEnabled {
|
public bool IsCoverOverlayEnabled
|
||||||
get => Get(SettingsKeys.IsCoverOverlayEnabled, SettingsDefaultValues.IsCoverOverlayEnabled);
|
{
|
||||||
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.IsCoverOverlayEnabled,
|
||||||
|
SettingsDefaultValues.IsCoverOverlayEnabled
|
||||||
|
);
|
||||||
set => Set(SettingsKeys.IsCoverOverlayEnabled, value);
|
set => Set(SettingsKeys.IsCoverOverlayEnabled, value);
|
||||||
}
|
}
|
||||||
public bool IsDynamicCoverOverlay {
|
public bool IsDynamicCoverOverlay
|
||||||
get => Get(SettingsKeys.IsDynamicCoverOverlay, SettingsDefaultValues.IsDynamicCoverOverlay);
|
{
|
||||||
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.IsDynamicCoverOverlay,
|
||||||
|
SettingsDefaultValues.IsDynamicCoverOverlay
|
||||||
|
);
|
||||||
set => Set(SettingsKeys.IsDynamicCoverOverlay, value);
|
set => Set(SettingsKeys.IsDynamicCoverOverlay, value);
|
||||||
}
|
}
|
||||||
public int CoverOverlayOpacity {
|
public int CoverOverlayOpacity
|
||||||
|
{
|
||||||
get => Get(SettingsKeys.CoverOverlayOpacity, SettingsDefaultValues.CoverOverlayOpacity);
|
get => Get(SettingsKeys.CoverOverlayOpacity, SettingsDefaultValues.CoverOverlayOpacity);
|
||||||
set => Set(SettingsKeys.CoverOverlayOpacity, value);
|
set => Set(SettingsKeys.CoverOverlayOpacity, value);
|
||||||
}
|
}
|
||||||
public int CoverOverlayBlurAmount {
|
public int CoverOverlayBlurAmount
|
||||||
get => Get(SettingsKeys.CoverOverlayBlurAmount, SettingsDefaultValues.CoverOverlayBlurAmount);
|
{
|
||||||
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.CoverOverlayBlurAmount,
|
||||||
|
SettingsDefaultValues.CoverOverlayBlurAmount
|
||||||
|
);
|
||||||
set => Set(SettingsKeys.CoverOverlayBlurAmount, value);
|
set => Set(SettingsKeys.CoverOverlayBlurAmount, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Title bar
|
||||||
|
public int TitleBarType
|
||||||
|
{
|
||||||
|
get => Get(SettingsKeys.TitleBarType, SettingsDefaultValues.TitleBarType);
|
||||||
|
set => Set(SettingsKeys.TitleBarType, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Album art
|
||||||
|
public int CoverImageRadius
|
||||||
|
{
|
||||||
|
get => Get(SettingsKeys.CoverImageRadius, SettingsDefaultValues.CoverImageRadius);
|
||||||
|
set => Set(SettingsKeys.CoverImageRadius, value);
|
||||||
|
}
|
||||||
|
|
||||||
// Lyrics
|
// Lyrics
|
||||||
public int LyricsAlignmentType {
|
public int LyricsAlignmentType
|
||||||
|
{
|
||||||
get => Get(SettingsKeys.LyricsAlignmentType, SettingsDefaultValues.LyricsAlignmentType);
|
get => Get(SettingsKeys.LyricsAlignmentType, SettingsDefaultValues.LyricsAlignmentType);
|
||||||
set => Set(SettingsKeys.LyricsAlignmentType, value);
|
set => Set(SettingsKeys.LyricsAlignmentType, value);
|
||||||
}
|
}
|
||||||
public int LyricsBlurAmount {
|
public int LyricsBlurAmount
|
||||||
|
{
|
||||||
get => Get(SettingsKeys.LyricsBlurAmount, SettingsDefaultValues.LyricsBlurAmount);
|
get => Get(SettingsKeys.LyricsBlurAmount, SettingsDefaultValues.LyricsBlurAmount);
|
||||||
set => Set(SettingsKeys.LyricsBlurAmount, value);
|
set => Set(SettingsKeys.LyricsBlurAmount, value);
|
||||||
}
|
}
|
||||||
public int LyricsVerticalEdgeOpacity {
|
public int LyricsVerticalEdgeOpacity
|
||||||
get => Get(SettingsKeys.LyricsVerticalEdgeOpacity, SettingsDefaultValues.LyricsVerticalEdgeOpacity);
|
{
|
||||||
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.LyricsVerticalEdgeOpacity,
|
||||||
|
SettingsDefaultValues.LyricsVerticalEdgeOpacity
|
||||||
|
);
|
||||||
set => Set(SettingsKeys.LyricsVerticalEdgeOpacity, value);
|
set => Set(SettingsKeys.LyricsVerticalEdgeOpacity, value);
|
||||||
}
|
}
|
||||||
public float LyricsLineSpacingFactor {
|
public float LyricsLineSpacingFactor
|
||||||
get => Get(SettingsKeys.LyricsLineSpacingFactor, SettingsDefaultValues.LyricsLineSpacingFactor);
|
{
|
||||||
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.LyricsLineSpacingFactor,
|
||||||
|
SettingsDefaultValues.LyricsLineSpacingFactor
|
||||||
|
);
|
||||||
set => Set(SettingsKeys.LyricsLineSpacingFactor, value);
|
set => Set(SettingsKeys.LyricsLineSpacingFactor, value);
|
||||||
}
|
}
|
||||||
public int LyricsFontSize {
|
public int LyricsFontSize
|
||||||
|
{
|
||||||
get => Get(SettingsKeys.LyricsFontSize, SettingsDefaultValues.LyricsFontSize);
|
get => Get(SettingsKeys.LyricsFontSize, SettingsDefaultValues.LyricsFontSize);
|
||||||
set => Set(SettingsKeys.LyricsFontSize, value);
|
set => Set(SettingsKeys.LyricsFontSize, value);
|
||||||
}
|
}
|
||||||
public bool IsLyricsGlowEffectEnabled {
|
public bool IsLyricsGlowEffectEnabled
|
||||||
get => Get(SettingsKeys.IsLyricsGlowEffectEnabled, SettingsDefaultValues.IsLyricsGlowEffectEnabled);
|
{
|
||||||
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.IsLyricsGlowEffectEnabled,
|
||||||
|
SettingsDefaultValues.IsLyricsGlowEffectEnabled
|
||||||
|
);
|
||||||
set => Set(SettingsKeys.IsLyricsGlowEffectEnabled, value);
|
set => Set(SettingsKeys.IsLyricsGlowEffectEnabled, value);
|
||||||
}
|
}
|
||||||
public bool IsLyricsDynamicGlowEffectEnabled {
|
public bool IsLyricsDynamicGlowEffectEnabled
|
||||||
get => Get(SettingsKeys.IsLyricsDynamicGlowEffectEnabled, SettingsDefaultValues.IsLyricsDynamicGlowEffectEnabled);
|
{
|
||||||
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.IsLyricsDynamicGlowEffectEnabled,
|
||||||
|
SettingsDefaultValues.IsLyricsDynamicGlowEffectEnabled
|
||||||
|
);
|
||||||
set => Set(SettingsKeys.IsLyricsDynamicGlowEffectEnabled, value);
|
set => Set(SettingsKeys.IsLyricsDynamicGlowEffectEnabled, value);
|
||||||
}
|
}
|
||||||
|
public int LyricsFontColorType
|
||||||
|
{
|
||||||
private readonly ApplicationDataContainer _localSettings;
|
get => Get(SettingsKeys.LyricsFontColorType, SettingsDefaultValues.LyricsFontColorType);
|
||||||
private readonly DatabaseService _databaseService;
|
set => Set(SettingsKeys.LyricsFontColorType, value);
|
||||||
|
}
|
||||||
public SettingsService(DatabaseService databaseService) {
|
public int LyricsFontSelectedAccentColorIndex
|
||||||
_localSettings = ApplicationData.Current.LocalSettings;
|
{
|
||||||
_databaseService = databaseService;
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.LyricsFontSelectedAccentColorIndex,
|
||||||
|
SettingsDefaultValues.LyricsFontSelectedAccentColorIndex
|
||||||
|
);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value >= 0)
|
||||||
|
Set(SettingsKeys.LyricsFontSelectedAccentColorIndex, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private T Get<T>(string key, T defaultValue = default) {
|
//Notification
|
||||||
if (_localSettings.Values.TryGetValue(key, out object value)) {
|
public bool NeverShowEnterFullScreenMessage
|
||||||
|
{
|
||||||
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.NeverShowEnterFullScreenMessage,
|
||||||
|
SettingsDefaultValues.NeverShowEnterFullScreenMessage
|
||||||
|
);
|
||||||
|
set => Set(SettingsKeys.NeverShowEnterFullScreenMessage, value);
|
||||||
|
}
|
||||||
|
public bool NeverShowEnterImmersiveModeMessage
|
||||||
|
{
|
||||||
|
get =>
|
||||||
|
Get(
|
||||||
|
SettingsKeys.NeverShowEnterImmersiveModeMessage,
|
||||||
|
SettingsDefaultValues.NeverShowEnterImmersiveModeMessage
|
||||||
|
);
|
||||||
|
set => Set(SettingsKeys.NeverShowEnterImmersiveModeMessage, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
|
||||||
|
private T? Get<T>(string key, T? defaultValue = default)
|
||||||
|
{
|
||||||
|
if (_localSettings.Values.TryGetValue(key, out object? value))
|
||||||
|
{
|
||||||
return (T)Convert.ChangeType(value, typeof(T));
|
return (T)Convert.ChangeType(value, typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Set<T>(string key, T value, [CallerMemberName] string propertyName = null) {
|
private void Set<T>(string key, T value, [CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
_localSettings.Values[key] = value;
|
_localSettings.Values[key] = value;
|
||||||
OnPropertyChanged(propertyName);
|
OnPropertyChanged(propertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,6 +126,9 @@
|
|||||||
<data name="SettingsPageOpenPath.Content" xml:space="preserve">
|
<data name="SettingsPageOpenPath.Content" xml:space="preserve">
|
||||||
<value>Open in file explorer</value>
|
<value>Open in file explorer</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SettingsPageOpenLogFolderButton.Content" xml:space="preserve">
|
||||||
|
<value>Open in file explorer</value>
|
||||||
|
</data>
|
||||||
<data name="SettingsPageRemovePath.Content" xml:space="preserve">
|
<data name="SettingsPageRemovePath.Content" xml:space="preserve">
|
||||||
<value>Remove from app</value>
|
<value>Remove from app</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -273,10 +276,13 @@
|
|||||||
<data name="MainWindowLyricsOnly.ToolTipService.ToolTip" xml:space="preserve">
|
<data name="MainWindowLyricsOnly.ToolTipService.ToolTip" xml:space="preserve">
|
||||||
<value>Show lyrics only</value>
|
<value>Show lyrics only</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="MainWindowImmersiveMode.ToolTipService.ToolTip" xml:space="preserve">
|
||||||
|
<value>Immersive mode</value>
|
||||||
|
</data>
|
||||||
<data name="SettingsPageLyricsEffect.Text" xml:space="preserve">
|
<data name="SettingsPageLyricsEffect.Text" xml:space="preserve">
|
||||||
<value>Lyrics effect</value>
|
<value>Lyrics effect</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsPageAlbum.Text" xml:space="preserve">
|
<data name="SettingsPageAlbumOverlay.Text" xml:space="preserve">
|
||||||
<value>Album background</value>
|
<value>Album background</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsPageAbout.Text" xml:space="preserve">
|
<data name="SettingsPageAbout.Text" xml:space="preserve">
|
||||||
@@ -306,37 +312,64 @@
|
|||||||
<data name="SettingsPageRebuildDatabaseDesc.Text" xml:space="preserve">
|
<data name="SettingsPageRebuildDatabaseDesc.Text" xml:space="preserve">
|
||||||
<value>Rebuilding the database, please wait...</value>
|
<value>Rebuilding the database, please wait...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>Let's get started now</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
||||||
<value>Welcome to BetterLyrics</value>
|
<value>Welcome to BetterLyrics</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MainPageTitleBarTeachingTip.Title" xml:space="preserve">
|
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||||
<value>The top area is the title bar</value>
|
<value>Let's setup lyrics database now</value>
|
||||||
</data>
|
|
||||||
<data name="MainPageTitleBarTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>Hover on the top area to show it</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageInitDatabaseTeachingTip.Title" xml:space="preserve">
|
|
||||||
<value>Setup lyrics database now</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageInitDatabaseTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>Hover on the bottom left corner and then click to open settings page</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageBottomCommandTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>Hover on the bottom area to show it</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageBottomCommandTeachingTip.Title" xml:space="preserve">
|
|
||||||
<value>The bottom area is the command area</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageLyricsOnlyTeachingTip.Title" xml:space="preserve">
|
|
||||||
<value>Toggle "show lyrics only" here</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageLyricsOnlyTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>When lyrics exist, you can switch them in the lower right corner</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||||
<value>No music playing now</value>
|
<value>No music playing now</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SettingsPageDev.Text" xml:space="preserve">
|
||||||
|
<value>Developer options</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
|
||||||
|
<value>Play test music</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||||
|
<value>Play using system player</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||||
|
<value>Log</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLyricsFontColor.Header" xml:space="preserve">
|
||||||
|
<value>Font color</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLyricsFontColorDefault.Content" xml:space="preserve">
|
||||||
|
<value>Default</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLyricsFontColorDominant.Content" xml:space="preserve">
|
||||||
|
<value>Album art accent color</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageAlbumStyle.Text" xml:space="preserve">
|
||||||
|
<value>Album art style</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||||
|
<value>Corner radius</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageTitleBarType.Header" xml:space="preserve">
|
||||||
|
<value>Title bar size</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
|
||||||
|
<value>Compact</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageExtendedTitleBar.Content" xml:space="preserve">
|
||||||
|
<value>Extended</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowAOTFlyoutItem.Text" xml:space="preserve">
|
||||||
|
<value>Always on top</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowFullScreenFlyoutItem.Text" xml:space="preserve">
|
||||||
|
<value>Full screen</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowEnterFullScreenHint" xml:space="preserve">
|
||||||
|
<value>Press Esc to exit full screen mode</value>
|
||||||
|
</data>
|
||||||
|
<data name="MainPageEnterImmersiveModeHint" xml:space="preserve">
|
||||||
|
<value>Hover back again to show the toggle button</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowHostInfoBarCheckBox.Content" xml:space="preserve">
|
||||||
|
<value>Do not show this message again</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -126,6 +126,9 @@
|
|||||||
<data name="SettingsPageOpenPath.Content" xml:space="preserve">
|
<data name="SettingsPageOpenPath.Content" xml:space="preserve">
|
||||||
<value>在文件资源管理器中打开</value>
|
<value>在文件资源管理器中打开</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SettingsPageOpenLogFolderButton.Content" xml:space="preserve">
|
||||||
|
<value>在文件资源管理器中打开</value>
|
||||||
|
</data>
|
||||||
<data name="SettingsPageRemovePath.Content" xml:space="preserve">
|
<data name="SettingsPageRemovePath.Content" xml:space="preserve">
|
||||||
<value>从应用中移除</value>
|
<value>从应用中移除</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -273,10 +276,13 @@
|
|||||||
<data name="MainWindowLyricsOnly.ToolTipService.ToolTip" xml:space="preserve">
|
<data name="MainWindowLyricsOnly.ToolTipService.ToolTip" xml:space="preserve">
|
||||||
<value>仅展示歌词</value>
|
<value>仅展示歌词</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="MainWindowImmersiveMode.ToolTipService.ToolTip" xml:space="preserve">
|
||||||
|
<value>沉浸模式</value>
|
||||||
|
</data>
|
||||||
<data name="SettingsPageLyricsEffect.Text" xml:space="preserve">
|
<data name="SettingsPageLyricsEffect.Text" xml:space="preserve">
|
||||||
<value>歌词效果</value>
|
<value>歌词效果</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsPageAlbum.Text" xml:space="preserve">
|
<data name="SettingsPageAlbumOverlay.Text" xml:space="preserve">
|
||||||
<value>专辑背景</value>
|
<value>专辑背景</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsPageAbout.Text" xml:space="preserve">
|
<data name="SettingsPageAbout.Text" xml:space="preserve">
|
||||||
@@ -306,37 +312,64 @@
|
|||||||
<data name="SettingsPageRebuildDatabaseDesc.Text" xml:space="preserve">
|
<data name="SettingsPageRebuildDatabaseDesc.Text" xml:space="preserve">
|
||||||
<value>重构数据库中,请稍候...</value>
|
<value>重构数据库中,请稍候...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>来看看怎么使用这款应用吧</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
||||||
<value>欢迎使用 BetterLyrics</value>
|
<value>欢迎使用 BetterLyrics</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MainPageTitleBarTeachingTip.Title" xml:space="preserve">
|
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||||
<value>顶部区域是标题栏</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageTitleBarTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>悬停在顶部区域以显示</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageInitDatabaseTeachingTip.Title" xml:space="preserve">
|
|
||||||
<value>现在就来初始化歌词数据库吧</value>
|
<value>现在就来初始化歌词数据库吧</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MainPageInitDatabaseTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>悬停在左下角后单击以进入设置页面</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageBottomCommandTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>悬停在底部区域以显示</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageBottomCommandTeachingTip.Title" xml:space="preserve">
|
|
||||||
<value>底部区域是命令栏</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageLyricsOnlyTeachingTip.Title" xml:space="preserve">
|
|
||||||
<value>在此处切换“仅展示歌词”</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageLyricsOnlyTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>当歌词存在时可在右下角进行切换</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||||
<value>当前没有正在播放的音乐</value>
|
<value>当前没有正在播放的音乐</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SettingsPageDev.Text" xml:space="preserve">
|
||||||
|
<value>开发者选项</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
|
||||||
|
<value>播放测试音乐</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||||
|
<value>使用系统播放器播放</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||||
|
<value>日志</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLyricsFontColor.Header" xml:space="preserve">
|
||||||
|
<value>字体颜色</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLyricsFontColorDefault.Content" xml:space="preserve">
|
||||||
|
<value>默认</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLyricsFontColorDominant.Content" xml:space="preserve">
|
||||||
|
<value>专辑强调色</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageAlbumStyle.Text" xml:space="preserve">
|
||||||
|
<value>专辑封面样式</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||||
|
<value>圆角半径</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageTitleBarType.Header" xml:space="preserve">
|
||||||
|
<value>标题栏大小</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
|
||||||
|
<value>紧凑</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageExtendedTitleBar.Content" xml:space="preserve">
|
||||||
|
<value>扩展</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowAOTFlyoutItem.Text" xml:space="preserve">
|
||||||
|
<value>将应用置于顶层</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowFullScreenFlyoutItem.Text" xml:space="preserve">
|
||||||
|
<value>全屏</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowEnterFullScreenHint" xml:space="preserve">
|
||||||
|
<value>按 Esc 退出全屏模式</value>
|
||||||
|
</data>
|
||||||
|
<data name="MainPageEnterImmersiveModeHint" xml:space="preserve">
|
||||||
|
<value>再次悬停以显示切换按钮</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowHostInfoBarCheckBox.Content" xml:space="preserve">
|
||||||
|
<value> 不再显示此消息</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -126,6 +126,9 @@
|
|||||||
<data name="SettingsPageOpenPath.Content" xml:space="preserve">
|
<data name="SettingsPageOpenPath.Content" xml:space="preserve">
|
||||||
<value>在檔案總管中開啟</value>
|
<value>在檔案總管中開啟</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SettingsPageOpenLogFolderButton.Content" xml:space="preserve">
|
||||||
|
<value>在檔案總管中開啟</value>
|
||||||
|
</data>
|
||||||
<data name="SettingsPageRemovePath.Content" xml:space="preserve">
|
<data name="SettingsPageRemovePath.Content" xml:space="preserve">
|
||||||
<value>從應用程式中移除</value>
|
<value>從應用程式中移除</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -273,10 +276,13 @@
|
|||||||
<data name="MainWindowLyricsOnly.ToolTipService.ToolTip" xml:space="preserve">
|
<data name="MainWindowLyricsOnly.ToolTipService.ToolTip" xml:space="preserve">
|
||||||
<value>僅展示歌詞</value>
|
<value>僅展示歌詞</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="MainWindowImmersiveMode.ToolTipService.ToolTip" xml:space="preserve">
|
||||||
|
<value>沉浸模式</value>
|
||||||
|
</data>
|
||||||
<data name="SettingsPageLyricsEffect.Text" xml:space="preserve">
|
<data name="SettingsPageLyricsEffect.Text" xml:space="preserve">
|
||||||
<value>歌詞效果</value>
|
<value>歌詞效果</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsPageAlbum.Text" xml:space="preserve">
|
<data name="SettingsPageAlbumOverlay.Text" xml:space="preserve">
|
||||||
<value>專輯背景</value>
|
<value>專輯背景</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsPageAbout.Text" xml:space="preserve">
|
<data name="SettingsPageAbout.Text" xml:space="preserve">
|
||||||
@@ -306,37 +312,64 @@
|
|||||||
<data name="SettingsPageRebuildDatabaseDesc.Text" xml:space="preserve">
|
<data name="SettingsPageRebuildDatabaseDesc.Text" xml:space="preserve">
|
||||||
<value>重構資料庫中,請稍候...</value>
|
<value>重構資料庫中,請稍候...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>來看看怎麼使用這款應用程式吧</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
||||||
<value>歡迎使用 BetterLyrics</value>
|
<value>歡迎使用 BetterLyrics</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MainPageTitleBarTeachingTip.Title" xml:space="preserve">
|
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||||
<value>頂部區域是標題欄</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageTitleBarTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>懸停在頂部區域以顯示</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageInitDatabaseTeachingTip.Title" xml:space="preserve">
|
|
||||||
<value>現在就來初始化歌詞資料庫吧</value>
|
<value>現在就來初始化歌詞資料庫吧</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MainPageInitDatabaseTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>懸停在左下角後點擊以進入設定頁面</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageBottomCommandTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>懸停在底部區域以顯示</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageBottomCommandTeachingTip.Title" xml:space="preserve">
|
|
||||||
<value>底部區域是命令列</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageLyricsOnlyTeachingTip.Title" xml:space="preserve">
|
|
||||||
<value>在此切換“僅展示歌詞”</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageLyricsOnlyTeachingTip.Subtitle" xml:space="preserve">
|
|
||||||
<value>當歌詞存在時可在右下角進行切換</value>
|
|
||||||
</data>
|
|
||||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||||
<value>目前沒有正在播放的音樂</value>
|
<value>目前沒有正在播放的音樂</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SettingsPageDev.Text" xml:space="preserve">
|
||||||
|
<value>開發者選項</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
|
||||||
|
<value>播放測試音樂</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||||
|
<value>使用系統播放器播放</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||||
|
<value>紀錄</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLyricsFontColor.Header" xml:space="preserve">
|
||||||
|
<value>字體顏色</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLyricsFontColorDefault.Content" xml:space="preserve">
|
||||||
|
<value>預設</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageLyricsFontColorDominant.Content" xml:space="preserve">
|
||||||
|
<value>專輯強調色</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageAlbumStyle.Text" xml:space="preserve">
|
||||||
|
<value>專輯封面樣式</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||||
|
<value>圓角半徑</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageTitleBarType.Header" xml:space="preserve">
|
||||||
|
<value>標題列大小</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
|
||||||
|
<value>緊湊</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPageExtendedTitleBar.Content" xml:space="preserve">
|
||||||
|
<value>擴充</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowAOTFlyoutItem.Text" xml:space="preserve">
|
||||||
|
<value>將應用置於頂層</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowFullScreenFlyoutItem.Text" xml:space="preserve">
|
||||||
|
<value>全螢幕</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowEnterFullScreenHint" xml:space="preserve">
|
||||||
|
<value>按 Esc 退出全螢幕模式</value>
|
||||||
|
</data>
|
||||||
|
<data name="MainPageEnterImmersiveModeHint" xml:space="preserve">
|
||||||
|
<value>再次懸停以顯示切換按鈕</value>
|
||||||
|
</data>
|
||||||
|
<data name="BaseWindowHostInfoBarCheckBox.Content" xml:space="preserve">
|
||||||
|
<value>不再顯示此訊息</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Messages;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Services.Settings;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.ViewModels
|
||||||
|
{
|
||||||
|
public partial class BaseWindowModel : ObservableObject
|
||||||
|
{
|
||||||
|
public SettingsService SettingsService { get; private set; }
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private int _titleBarFontSize = 11;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private Notification _notification = new();
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _showInfoBar = false;
|
||||||
|
|
||||||
|
public BaseWindowModel(SettingsService settingsService)
|
||||||
|
{
|
||||||
|
SettingsService = settingsService;
|
||||||
|
|
||||||
|
WeakReferenceMessenger.Default.Register<ShowNotificatonMessage>(
|
||||||
|
this,
|
||||||
|
async (r, m) =>
|
||||||
|
{
|
||||||
|
Notification = m.Value;
|
||||||
|
if (
|
||||||
|
!Notification.IsForeverDismissable
|
||||||
|
|| AlreadyForeverDismissedThisMessage() == false
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Notification.Visibility = Notification.IsForeverDismissable
|
||||||
|
? Visibility.Visible
|
||||||
|
: Visibility.Collapsed;
|
||||||
|
ShowInfoBar = true;
|
||||||
|
await Task.Delay(AnimationHelper.StackedNotificationsShowingDuration);
|
||||||
|
ShowInfoBar = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void SwitchInfoBarNeverShowItAgainCheckBox(bool value)
|
||||||
|
{
|
||||||
|
switch (Notification.RelatedSettingsKeyName)
|
||||||
|
{
|
||||||
|
case SettingsKeys.NeverShowEnterFullScreenMessage:
|
||||||
|
SettingsService.NeverShowEnterFullScreenMessage = value;
|
||||||
|
break;
|
||||||
|
case SettingsKeys.NeverShowEnterImmersiveModeMessage:
|
||||||
|
SettingsService.NeverShowEnterImmersiveModeMessage = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool? AlreadyForeverDismissedThisMessage() =>
|
||||||
|
Notification.RelatedSettingsKeyName switch
|
||||||
|
{
|
||||||
|
SettingsKeys.NeverShowEnterFullScreenMessage =>
|
||||||
|
SettingsService.NeverShowEnterFullScreenMessage,
|
||||||
|
SettingsKeys.NeverShowEnterImmersiveModeMessage =>
|
||||||
|
SettingsService.NeverShowEnterImmersiveModeMessage,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,35 @@
|
|||||||
using ATL;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ATL;
|
||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
using BetterLyrics.WinUI3.Services.Database;
|
using BetterLyrics.WinUI3.Services.Database;
|
||||||
|
using BetterLyrics.WinUI3.Services.Settings;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using DevWinUI;
|
||||||
using Microsoft.Graphics.Canvas;
|
using Microsoft.Graphics.Canvas;
|
||||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
using Microsoft.UI;
|
using Microsoft.UI;
|
||||||
using Microsoft.UI.Xaml.Media.Imaging;
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
using System;
|
using Ude;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Windows.Graphics.Imaging;
|
using Windows.Graphics.Imaging;
|
||||||
using Windows.Media.Control;
|
using Windows.Media.Control;
|
||||||
using Windows.Storage.Streams;
|
using Windows.Storage.Streams;
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
|
using static System.Net.Mime.MediaTypeNames;
|
||||||
using static ATL.LyricsInfo;
|
using static ATL.LyricsInfo;
|
||||||
|
using static CommunityToolkit.WinUI.Animations.Expressions.ExpressionValues;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.ViewModels {
|
namespace BetterLyrics.WinUI3.ViewModels
|
||||||
public partial class MainViewModel : ObservableObject {
|
{
|
||||||
|
public partial class MainViewModel : ObservableObject
|
||||||
|
{
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isAnyMusicSessionExisted = false;
|
private bool _isAnyMusicSessionExisted = false;
|
||||||
|
|
||||||
@@ -28,8 +39,8 @@ namespace BetterLyrics.WinUI3.ViewModels {
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _artist;
|
private string? _artist;
|
||||||
|
|
||||||
public List<Color> CoverImageDominantColors { get; set; } =
|
[ObservableProperty]
|
||||||
[Colors.Transparent, Colors.Transparent, Colors.Transparent];
|
private ObservableCollection<Color> _coverImageDominantColors;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private BitmapImage? _coverImage;
|
private BitmapImage? _coverImage;
|
||||||
@@ -46,20 +57,33 @@ namespace BetterLyrics.WinUI3.ViewModels {
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _lyricsExisted = false;
|
private bool _lyricsExisted = false;
|
||||||
|
|
||||||
private Helper.ColorThief _colorThief = new();
|
private readonly ColorThief _colorThief = new();
|
||||||
|
|
||||||
|
private readonly SettingsService _settingsService;
|
||||||
private readonly DatabaseService _databaseService;
|
private readonly DatabaseService _databaseService;
|
||||||
|
|
||||||
public MainViewModel(DatabaseService databaseService) {
|
private readonly int _accentColorCount = 3;
|
||||||
|
|
||||||
|
public MainViewModel(SettingsService settingsService, DatabaseService databaseService)
|
||||||
|
{
|
||||||
|
_settingsService = settingsService;
|
||||||
_databaseService = databaseService;
|
_databaseService = databaseService;
|
||||||
|
CoverImageDominantColors =
|
||||||
|
[
|
||||||
|
.. Enumerable.Repeat(Colors.Transparent, _accentColorCount),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<LyricsLine> GetLyrics(Track? track) {
|
public List<LyricsLine> GetLyrics(Track? track)
|
||||||
|
{
|
||||||
List<LyricsLine> result = [];
|
List<LyricsLine> result = [];
|
||||||
var lyricsPhrases = track?.Lyrics.SynchronizedLyrics;
|
|
||||||
|
|
||||||
if (lyricsPhrases?.Count > 0) {
|
var lyricsPhrases = track?.Lyrics?.SynchronizedLyrics;
|
||||||
if (lyricsPhrases[0].TimestampMs > 0) {
|
|
||||||
|
if (lyricsPhrases?.Count > 0)
|
||||||
|
{
|
||||||
|
if (lyricsPhrases[0].TimestampMs > 0)
|
||||||
|
{
|
||||||
var placeholder = new LyricsPhrase(0, " ");
|
var placeholder = new LyricsPhrase(0, " ");
|
||||||
lyricsPhrases.Insert(0, placeholder);
|
lyricsPhrases.Insert(0, placeholder);
|
||||||
lyricsPhrases.Insert(0, placeholder);
|
lyricsPhrases.Insert(0, placeholder);
|
||||||
@@ -68,40 +92,50 @@ namespace BetterLyrics.WinUI3.ViewModels {
|
|||||||
|
|
||||||
LyricsLine? lyricsLine = null;
|
LyricsLine? lyricsLine = null;
|
||||||
|
|
||||||
for (int i = 0; i < lyricsPhrases?.Count; i++) {
|
for (int i = 0; i < lyricsPhrases?.Count; i++)
|
||||||
|
{
|
||||||
var lyricsPhrase = lyricsPhrases[i];
|
var lyricsPhrase = lyricsPhrases[i];
|
||||||
int startTimestampMs = lyricsPhrase.TimestampMs;
|
int startTimestampMs = lyricsPhrase.TimestampMs;
|
||||||
int endTimestampMs;
|
int endTimestampMs;
|
||||||
|
|
||||||
if (i + 1 < lyricsPhrases.Count) {
|
if (i + 1 < lyricsPhrases.Count)
|
||||||
|
{
|
||||||
endTimestampMs = lyricsPhrases[i + 1].TimestampMs;
|
endTimestampMs = lyricsPhrases[i + 1].TimestampMs;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
endTimestampMs = (int)track.DurationMs;
|
endTimestampMs = (int)track.DurationMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
lyricsLine ??= new LyricsLine {
|
lyricsLine ??= new LyricsLine { StartPlayingTimestampMs = startTimestampMs };
|
||||||
StartPlayingTimestampMs = startTimestampMs,
|
|
||||||
};
|
|
||||||
|
|
||||||
lyricsLine.Texts.Add(lyricsPhrase.Text);
|
lyricsLine.Texts.Add(lyricsPhrase.Text);
|
||||||
|
|
||||||
if (endTimestampMs == startTimestampMs) {
|
if (endTimestampMs == startTimestampMs)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
lyricsLine.EndPlayingTimestampMs = endTimestampMs;
|
lyricsLine.EndPlayingTimestampMs = endTimestampMs;
|
||||||
result.Add(lyricsLine);
|
result.Add(lyricsLine);
|
||||||
lyricsLine = null;
|
lyricsLine = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LyricsExisted = result.Count != 0;
|
LyricsExisted = result.Count != 0;
|
||||||
|
if (!LyricsExisted)
|
||||||
|
{
|
||||||
|
ShowLyricsOnly = false;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(List<LyricsLine>, SoftwareBitmap?, uint, uint)> SetSongInfoAsync(GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProps, ICanvasAnimatedControl control) {
|
public async Task<(List<LyricsLine>, SoftwareBitmap?)> SetSongInfoAsync(
|
||||||
|
GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProps
|
||||||
|
)
|
||||||
|
{
|
||||||
SoftwareBitmap? coverSoftwareBitmap = null;
|
SoftwareBitmap? coverSoftwareBitmap = null;
|
||||||
uint coverImagePixelWidth = 0;
|
uint coverImagePixelWidth = 0;
|
||||||
uint coverImagePixelHeight = 0;
|
uint coverImagePixelHeight = 0;
|
||||||
@@ -111,26 +145,37 @@ namespace BetterLyrics.WinUI3.ViewModels {
|
|||||||
|
|
||||||
IRandomAccessStream? stream = null;
|
IRandomAccessStream? stream = null;
|
||||||
|
|
||||||
var track = _databaseService.GetMusicMetadata(Title, Artist);
|
var track = _databaseService.GetMusicMetadata(mediaProps);
|
||||||
|
|
||||||
if (mediaProps?.Thumbnail is IRandomAccessStreamReference reference) {
|
if (mediaProps?.Thumbnail is IRandomAccessStreamReference reference)
|
||||||
|
{
|
||||||
stream = await reference.OpenReadAsync();
|
stream = await reference.OpenReadAsync();
|
||||||
} else {
|
}
|
||||||
if (track?.EmbeddedPictures.Count > 0) {
|
else
|
||||||
|
{
|
||||||
|
if (track?.EmbeddedPictures.Count > 0)
|
||||||
|
{
|
||||||
var bytes = track.EmbeddedPictures[0].PictureData;
|
var bytes = track.EmbeddedPictures[0].PictureData;
|
||||||
if (bytes != null) {
|
if (bytes != null)
|
||||||
|
{
|
||||||
stream = await Helper.ImageHelper.GetStreamFromBytesAsync(bytes);
|
stream = await Helper.ImageHelper.GetStreamFromBytesAsync(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set cover image and dominant colors
|
// Set cover image and dominant colors
|
||||||
if (stream == null) {
|
if (stream == null)
|
||||||
|
{
|
||||||
CoverImage = null;
|
CoverImage = null;
|
||||||
for (int i = 0; i < 3; i++) {
|
CoverImageDominantColors =
|
||||||
CoverImageDominantColors[i] = Colors.Transparent;
|
[
|
||||||
|
.. Enumerable.Repeat(Colors.Transparent, _accentColorCount),
|
||||||
|
];
|
||||||
|
_settingsService.LyricsFontSelectedAccentColorIndex =
|
||||||
|
_settingsService.LyricsFontSelectedAccentColorIndex;
|
||||||
}
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
CoverImage = new BitmapImage();
|
CoverImage = new BitmapImage();
|
||||||
await CoverImage.SetSourceAsync(stream);
|
await CoverImage.SetSourceAsync(stream);
|
||||||
stream.Seek(0);
|
stream.Seek(0);
|
||||||
@@ -144,19 +189,19 @@ namespace BetterLyrics.WinUI3.ViewModels {
|
|||||||
BitmapAlphaMode.Premultiplied
|
BitmapAlphaMode.Premultiplied
|
||||||
);
|
);
|
||||||
|
|
||||||
var quantizedColors = await _colorThief.GetPalette(decoder, 3);
|
CoverImageDominantColors =
|
||||||
for (int i = 0; i < 3; i++) {
|
[
|
||||||
Helper.QuantizedColor quantizedColor = quantizedColors[i];
|
.. (await _colorThief.GetPalette(decoder, _accentColorCount)).Select(color =>
|
||||||
CoverImageDominantColors[i] = Color.FromArgb(
|
Color.FromArgb(color.Color.A, color.Color.R, color.Color.G, color.Color.B)
|
||||||
quantizedColor.Color.A, quantizedColor.Color.R, quantizedColor.Color.G, quantizedColor.Color.B);
|
),
|
||||||
}
|
];
|
||||||
|
_settingsService.LyricsFontSelectedAccentColorIndex =
|
||||||
|
_settingsService.LyricsFontSelectedAccentColorIndex;
|
||||||
|
|
||||||
stream.Dispose();
|
stream.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (GetLyrics(track), coverSoftwareBitmap, coverImagePixelWidth, coverImagePixelHeight);
|
return (GetLyrics(track), coverSoftwareBitmap);
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,60 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using System;
|
||||||
using BetterLyrics.WinUI3.Services.Database;
|
|
||||||
using BetterLyrics.WinUI3.Services.Settings;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Windows.ApplicationModel;
|
using BetterLyrics.WinUI3.Messages;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Services.Database;
|
||||||
|
using BetterLyrics.WinUI3.Services.Settings;
|
||||||
|
using BetterLyrics.WinUI3.Views;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Windows.ApplicationModel.Core;
|
using Windows.ApplicationModel.Core;
|
||||||
|
using Windows.Media;
|
||||||
|
using Windows.Media.Playback;
|
||||||
using Windows.Storage.Pickers;
|
using Windows.Storage.Pickers;
|
||||||
using Windows.System;
|
using Windows.System;
|
||||||
using WinRT.Interop;
|
using WinRT.Interop;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.ViewModels {
|
namespace BetterLyrics.WinUI3.ViewModels
|
||||||
public partial class SettingsViewModel : ObservableObject {
|
{
|
||||||
|
public partial class SettingsViewModel(
|
||||||
|
DatabaseService databaseService,
|
||||||
|
SettingsService settingsService,
|
||||||
|
MainViewModel mainViewModel
|
||||||
|
) : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly MediaPlayer _mediaPlayer = new();
|
||||||
|
|
||||||
private readonly DatabaseService _databaseService;
|
private readonly DatabaseService _databaseService = databaseService;
|
||||||
|
|
||||||
[ObservableProperty]
|
public SettingsService SettingsService => settingsService;
|
||||||
private SettingsService _settingsService;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
public MainViewModel MainViewModel => mainViewModel;
|
||||||
private string _version;
|
|
||||||
|
|
||||||
public SettingsViewModel(DatabaseService databaseService, SettingsService settingsService) {
|
public string Version => Helper.AppInfo.AppVersion;
|
||||||
_databaseService = databaseService;
|
|
||||||
_settingsService = settingsService;
|
|
||||||
|
|
||||||
var version = Package.Current.Id.Version;
|
|
||||||
Version = $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task RebuildLyricsIndexDatabaseAsync() {
|
private async Task RebuildLyricsIndexDatabaseAsync()
|
||||||
|
{
|
||||||
SettingsService.IsRebuildingLyricsIndexDatabase = true;
|
SettingsService.IsRebuildingLyricsIndexDatabase = true;
|
||||||
await _databaseService.RebuildMusicMetadataIndexDatabaseAsync(SettingsService.MusicLibraries);
|
await _databaseService.RebuildMusicMetadataIndexDatabaseAsync(
|
||||||
|
SettingsService.MusicLibraries
|
||||||
|
);
|
||||||
SettingsService.IsRebuildingLyricsIndexDatabase = false;
|
SettingsService.IsRebuildingLyricsIndexDatabase = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
public async Task RemoveFolderAsync(string path)
|
||||||
private async Task RemoveFolderAsync(MusicFolder musicFolder) {
|
{
|
||||||
SettingsService.MusicLibraries.Remove(musicFolder);
|
SettingsService.MusicLibraries.Remove(path);
|
||||||
await RebuildLyricsIndexDatabaseAsync();
|
await RebuildLyricsIndexDatabaseAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task AddFolderAsync() {
|
private async Task SelectAndAddFolderAsync()
|
||||||
|
{
|
||||||
var picker = new FolderPicker();
|
var picker = new FolderPicker();
|
||||||
|
|
||||||
picker.FileTypeFilter.Add("*");
|
picker.FileTypeFilter.Add("*");
|
||||||
@@ -58,41 +64,67 @@ namespace BetterLyrics.WinUI3.ViewModels {
|
|||||||
|
|
||||||
var folder = await picker.PickSingleFolderAsync();
|
var folder = await picker.PickSingleFolderAsync();
|
||||||
|
|
||||||
if (folder != null) {
|
App.Current.SettingsWindow!.AppWindow.MoveInZOrderAtTop();
|
||||||
string path = folder.Path;
|
|
||||||
bool existed = SettingsService.MusicLibraries.Count((x) => x.Path == path) > 0;
|
if (folder != null)
|
||||||
if (existed) {
|
{
|
||||||
MainWindow.StackedNotificationsBehavior?.Show(App.ResourceLoader.GetString("SettingsPagePathExistedInfo"), 3900);
|
await AddFolderAsync(folder.Path);
|
||||||
} else {
|
}
|
||||||
SettingsService.MusicLibraries.Add(new MusicFolder(path));
|
}
|
||||||
|
|
||||||
|
private async Task AddFolderAsync(string path)
|
||||||
|
{
|
||||||
|
bool existed = SettingsService.MusicLibraries.Any((x) => x == path);
|
||||||
|
if (existed)
|
||||||
|
{
|
||||||
|
WeakReferenceMessenger.Default.Send(
|
||||||
|
new ShowNotificatonMessage(
|
||||||
|
new Notification(
|
||||||
|
App.ResourceLoader!.GetString("SettingsPagePathExistedInfo")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SettingsService.MusicLibraries.Add(path);
|
||||||
await RebuildLyricsIndexDatabaseAsync();
|
await RebuildLyricsIndexDatabaseAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private static async Task LaunchProjectGitHubPageAsync()
|
||||||
|
{
|
||||||
|
await Launcher.LaunchUriAsync(new Uri(Helper.AppInfo.GithubUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
private static void OpenFolderInFileExplorer(string path)
|
||||||
private async Task LaunchProjectGitHubPageAsync() {
|
{
|
||||||
await Launcher.LaunchUriAsync(new Uri("https://github.com/jayfunc/BetterLyrics"));
|
Process.Start(
|
||||||
}
|
new ProcessStartInfo
|
||||||
|
{
|
||||||
[RelayCommand]
|
|
||||||
private void OpenFolderInFileExplorer(MusicFolder musicFolder) {
|
|
||||||
Process.Start(new ProcessStartInfo {
|
|
||||||
FileName = "explorer.exe",
|
FileName = "explorer.exe",
|
||||||
Arguments = musicFolder.Path,
|
Arguments = path,
|
||||||
UseShellExecute = true
|
UseShellExecute = true,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenMusicFolder(string path)
|
||||||
|
{
|
||||||
|
OpenFolderInFileExplorer(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void RestartApp() {
|
private static void RestartApp()
|
||||||
|
{
|
||||||
// The restart will be executed immediately.
|
// The restart will be executed immediately.
|
||||||
AppRestartFailureReason failureReason =
|
AppRestartFailureReason failureReason =
|
||||||
Microsoft.Windows.AppLifecycle.AppInstance.Restart("");
|
Microsoft.Windows.AppLifecycle.AppInstance.Restart("");
|
||||||
|
|
||||||
// If the restart fails, handle it here.
|
// If the restart fails, handle it here.
|
||||||
switch (failureReason) {
|
switch (failureReason)
|
||||||
|
{
|
||||||
case AppRestartFailureReason.RestartPending:
|
case AppRestartFailureReason.RestartPending:
|
||||||
break;
|
break;
|
||||||
case AppRestartFailureReason.NotInForeground:
|
case AppRestartFailureReason.NotInForeground:
|
||||||
@@ -104,5 +136,18 @@ namespace BetterLyrics.WinUI3.ViewModels {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task PlayTestingMusicTask()
|
||||||
|
{
|
||||||
|
await AddFolderAsync(Helper.AppInfo.AssetsFolder);
|
||||||
|
_mediaPlayer.SetUriSource(new Uri(Helper.AppInfo.TestMusicPath));
|
||||||
|
_mediaPlayer.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private static void OpenLogFolder()
|
||||||
|
{
|
||||||
|
OpenFolderInFileExplorer(Helper.AppInfo.LogDirectory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
187
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/BaseWindow.xaml
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Window
|
||||||
|
x:Class="BetterLyrics.WinUI3.Views.BaseWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Views"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:media="using:CommunityToolkit.WinUI.Media"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid x:Name="RootGrid" KeyDown="RootGrid_KeyDown">
|
||||||
|
|
||||||
|
<Frame
|
||||||
|
x:Name="RootFrame"
|
||||||
|
Navigated="RootFrame_Navigated"
|
||||||
|
NavigationFailed="RootFrame_NavigationFailed" />
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
x:Name="TopCommandGrid"
|
||||||
|
Height="{StaticResource TitleBarCompactHeight}"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</Grid.OpacityTransition>
|
||||||
|
|
||||||
|
<interactivity:Interaction.Behaviors>
|
||||||
|
|
||||||
|
<interactivity:DataTriggerBehavior
|
||||||
|
Binding="{x:Bind WindowModel.SettingsService.IsImmersiveMode, Mode=OneWay}"
|
||||||
|
ComparisonCondition="Equal"
|
||||||
|
Value="False">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||||
|
</interactivity:DataTriggerBehavior>
|
||||||
|
<interactivity:DataTriggerBehavior
|
||||||
|
Binding="{x:Bind WindowModel.SettingsService.IsImmersiveMode, Mode=OneWay}"
|
||||||
|
ComparisonCondition="Equal"
|
||||||
|
Value="True">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||||
|
</interactivity:DataTriggerBehavior>
|
||||||
|
|
||||||
|
</interactivity:Interaction.Behaviors>
|
||||||
|
|
||||||
|
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
|
||||||
|
|
||||||
|
<ImageIcon
|
||||||
|
x:Name="AppLogoImageIcon"
|
||||||
|
Height="18"
|
||||||
|
Margin="16,0"
|
||||||
|
Source="ms-appx:///Assets/Logo.png" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
x:Name="AppTitleTextBlock"
|
||||||
|
FontSize="{x:Bind WindowModel.TitleBarFontSize, Mode=OneWay}"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Opacity=".5"
|
||||||
|
Text="{x:Bind Title, Mode=OneWay}" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Opacity=".5"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
|
||||||
|
<Button Style="{StaticResource TitleBarButtonStyle}">
|
||||||
|
<FontIcon
|
||||||
|
FontFamily="Segoe Fluent Icons"
|
||||||
|
FontSize="{x:Bind WindowModel.TitleBarFontSize, Mode=OneWay}"
|
||||||
|
FontWeight="ExtraBold"
|
||||||
|
Glyph="" />
|
||||||
|
<Button.Flyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<ToggleMenuFlyoutItem
|
||||||
|
x:Name="AOTFlyoutItem"
|
||||||
|
x:Uid="BaseWindowAOTFlyoutItem"
|
||||||
|
Click="AOTFlyoutItem_Click" />
|
||||||
|
<ToggleMenuFlyoutItem
|
||||||
|
x:Name="FullScreenFlyoutItem"
|
||||||
|
x:Uid="BaseWindowFullScreenFlyoutItem"
|
||||||
|
Click="FullScreenFlyoutItem_Click" />
|
||||||
|
</MenuFlyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<!-- Window Minimise -->
|
||||||
|
<Button
|
||||||
|
x:Name="MinimiseButton"
|
||||||
|
Click="MinimiseButton_Click"
|
||||||
|
Style="{StaticResource TitleBarButtonStyle}">
|
||||||
|
<FontIcon
|
||||||
|
FontFamily="Segoe Fluent Icons"
|
||||||
|
FontSize="{x:Bind WindowModel.TitleBarFontSize, Mode=OneWay}"
|
||||||
|
Glyph="" />
|
||||||
|
</Button>
|
||||||
|
<!-- Window Maximise -->
|
||||||
|
<Button
|
||||||
|
x:Name="MaximiseButton"
|
||||||
|
Click="MaximiseButton_Click"
|
||||||
|
Style="{StaticResource TitleBarButtonStyle}">
|
||||||
|
<FontIcon
|
||||||
|
FontFamily="Segoe Fluent Icons"
|
||||||
|
FontSize="{x:Bind WindowModel.TitleBarFontSize, Mode=OneWay}"
|
||||||
|
Glyph="" />
|
||||||
|
</Button>
|
||||||
|
<!-- Window Restore -->
|
||||||
|
<Button
|
||||||
|
x:Name="RestoreButton"
|
||||||
|
Click="RestoreButton_Click"
|
||||||
|
Style="{StaticResource TitleBarButtonStyle}"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
<FontIcon
|
||||||
|
FontFamily="Segoe Fluent Icons"
|
||||||
|
FontSize="{x:Bind WindowModel.TitleBarFontSize, Mode=OneWay}"
|
||||||
|
Glyph="" />
|
||||||
|
</Button>
|
||||||
|
<!-- Window Close -->
|
||||||
|
<Button
|
||||||
|
x:Name="CloseButton"
|
||||||
|
Click="CloseButton_Click"
|
||||||
|
Style="{StaticResource TitleBarButtonStyle}">
|
||||||
|
<FontIcon
|
||||||
|
FontFamily="Segoe Fluent Icons"
|
||||||
|
FontSize="{x:Bind WindowModel.TitleBarFontSize, Mode=OneWay}"
|
||||||
|
Glyph="" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<InfoBar
|
||||||
|
x:Name="HostInfoBar"
|
||||||
|
Margin="36"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Background="{ThemeResource SystemFillColorSolidAttentionBackgroundBrush}"
|
||||||
|
IsClosable="False"
|
||||||
|
IsOpen="{x:Bind WindowModel.ShowInfoBar, Mode=OneWay}"
|
||||||
|
Message="{x:Bind WindowModel.Notification.Message, Mode=OneWay}"
|
||||||
|
Opacity="0"
|
||||||
|
Severity="{x:Bind WindowModel.Notification.Severity, Mode=OneWay}">
|
||||||
|
<InfoBar.RenderTransform>
|
||||||
|
<TranslateTransform x:Name="HostInfoBarTransform" Y="20" />
|
||||||
|
</InfoBar.RenderTransform>
|
||||||
|
<InfoBar.ActionButton>
|
||||||
|
<CheckBox
|
||||||
|
x:Name="HostInfoBarCheckBox"
|
||||||
|
x:Uid="BaseWindowHostInfoBarCheckBox"
|
||||||
|
Command="{x:Bind WindowModel.SwitchInfoBarNeverShowItAgainCheckBoxCommand}"
|
||||||
|
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsChecked, Mode=OneWay}"
|
||||||
|
Visibility="{x:Bind WindowModel.Notification.Visibility, Mode=OneWay}" />
|
||||||
|
</InfoBar.ActionButton>
|
||||||
|
<InfoBar.Resources>
|
||||||
|
<Storyboard x:Key="InfoBarShowAndHideStoryboard">
|
||||||
|
<!-- Opacity -->
|
||||||
|
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="HostInfoBar" Storyboard.TargetProperty="Opacity">
|
||||||
|
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:3.6" Value="1" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:3.9" Value="0" />
|
||||||
|
</DoubleAnimationUsingKeyFrames>
|
||||||
|
|
||||||
|
<!-- Y -->
|
||||||
|
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="HostInfoBarTransform" Storyboard.TargetProperty="Y">
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="20" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:3.6" Value="0" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:3.9" Value="20" />
|
||||||
|
</DoubleAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</InfoBar.Resources>
|
||||||
|
<interactivity:Interaction.Behaviors>
|
||||||
|
<interactivity:DataTriggerBehavior
|
||||||
|
Binding="{Binding ElementName=HostInfoBar, Path=IsOpen, Mode=OneWay}"
|
||||||
|
ComparisonCondition="Equal"
|
||||||
|
Value="True">
|
||||||
|
<interactivity:ControlStoryboardAction Storyboard="{StaticResource InfoBarShowAndHideStoryboard}" />
|
||||||
|
</interactivity:DataTriggerBehavior>
|
||||||
|
</interactivity:Interaction.Behaviors>
|
||||||
|
</InfoBar>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||