mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 19:24:55 +08:00
Compare commits
88 Commits
v1.0.135.0
...
v1.1.167.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7238713ff5 | ||
|
|
66c42f81f2 | ||
|
|
3e6ba725d2 | ||
|
|
ffb0c58a58 | ||
|
|
3b61f568d0 | ||
|
|
b925a10d69 | ||
|
|
720f4b311e | ||
|
|
6c03002051 | ||
|
|
9cebd56bd5 | ||
|
|
f0a4c1251d | ||
|
|
a8418d4234 | ||
|
|
53abc4526c | ||
|
|
4d3b982904 | ||
|
|
5faace562d | ||
|
|
290b7f38b4 | ||
|
|
6f02a1a46c | ||
|
|
4f74e48cfb | ||
|
|
06f08558cc | ||
|
|
a2b21ed3d5 | ||
|
|
4cb1ca0bb3 | ||
|
|
81ed341e47 | ||
|
|
a9f92f4cb7 | ||
|
|
62719ed513 | ||
|
|
c9bd7725d0 | ||
|
|
b60952916c | ||
|
|
ec0917b6c8 | ||
|
|
583fa106ce | ||
|
|
88488e4813 | ||
|
|
6e65310b6d | ||
|
|
22bd7c2252 | ||
|
|
5c50bd569a | ||
|
|
401c33003c | ||
|
|
664451c530 | ||
|
|
657c81add5 | ||
|
|
1dd63ab9ba | ||
|
|
0a9b9bf484 | ||
|
|
794079f20b | ||
|
|
be9a67f57d | ||
|
|
5b5d62d688 | ||
|
|
dfe428645e | ||
|
|
8e2c977a44 | ||
|
|
47806c924d | ||
|
|
bbda1cd797 | ||
|
|
2099332f02 | ||
|
|
016d9a626f | ||
|
|
cff6f202d6 | ||
|
|
f643f86567 | ||
|
|
e9b50c84ac | ||
|
|
4c590bcf6f | ||
|
|
68a1c6a465 | ||
|
|
f46364a491 | ||
|
|
75f047e389 | ||
|
|
1cb21b1373 | ||
|
|
4592be10e8 | ||
|
|
c0217150c1 | ||
|
|
0705bde0e2 | ||
|
|
3f5122c93f | ||
|
|
97f061d887 | ||
|
|
77525a62ff | ||
|
|
ad473bbd1d | ||
|
|
b1126026c2 | ||
|
|
2f84155e6e | ||
|
|
1769167811 | ||
|
|
551da8c0b0 | ||
|
|
a5b3671ce3 | ||
|
|
181a06c932 | ||
|
|
93d567e21d | ||
|
|
9da8510de6 | ||
|
|
3ed9e599be | ||
|
|
e277faea9e | ||
|
|
21dcd7de4b | ||
|
|
31540beaa0 | ||
|
|
63ffe6b661 | ||
|
|
5cb880021c | ||
|
|
b00f2b5865 | ||
|
|
90a75f1b96 | ||
|
|
735f03542f | ||
|
|
b7853ded26 | ||
|
|
88fc0adbec | ||
|
|
a3366422a2 | ||
|
|
8f6e106282 | ||
|
|
08d4f4ce90 | ||
|
|
212041a509 | ||
|
|
c0f2b4106a | ||
|
|
c704edde19 | ||
|
|
35de1ef7db | ||
|
|
1c8840e053 | ||
|
|
2d0839d777 |
@@ -143,7 +143,7 @@
|
|||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
|
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<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.135.0" />
|
Version="1.1.167.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"/>
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,11 @@
|
|||||||
<converter:IndexToDisplayConverter x:Key="IndexToDisplayConverter" />
|
<converter:IndexToDisplayConverter x:Key="IndexToDisplayConverter" />
|
||||||
<converter:IntToDoubleConverter x:Key="IntToDoubleConverter" />
|
<converter:IntToDoubleConverter x:Key="IntToDoubleConverter" />
|
||||||
<converter:MillisecondsToSecondsConverter x:Key="MillisecondsToSecondsConverter" />
|
<converter:MillisecondsToSecondsConverter x:Key="MillisecondsToSecondsConverter" />
|
||||||
|
<converter:PictureInfosToImageSourceConverter x:Key="PictureInfosToImageSourceConverter" />
|
||||||
|
<converter:LyricsFontWeightToFontWeightConverter x:Key="LyricsFontWeightToFontWeightConverter" />
|
||||||
|
<converter:TextAlignmentTypeToHorizontalAlignmentConverter x:Key="TextAlignmentTypeToHorizontalAlignmentConverter" />
|
||||||
|
<converter:LyricsLayoutOrientationToOrientationConverter x:Key="LyricsLayoutOrientationToOrientationConverter" />
|
||||||
|
<converter:LyricsLayoutOrientationNegationToOrientationConverter x:Key="LyricsLayoutOrientationNegationToOrientationConverter" />
|
||||||
|
|
||||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||||
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ using BetterLyrics.WinUI3.Services.ResourceService;
|
|||||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
using BetterLyrics.WinUI3.Services.TranslateService;
|
using BetterLyrics.WinUI3.Services.TranslateService;
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel;
|
|
||||||
using BetterLyrics.WinUI3.Views;
|
using BetterLyrics.WinUI3.Views;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -26,6 +25,7 @@ using System.Text;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Vanara.PInvoke;
|
using Vanara.PInvoke;
|
||||||
|
using WinUIEx;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3
|
namespace BetterLyrics.WinUI3
|
||||||
{
|
{
|
||||||
@@ -69,7 +69,11 @@ namespace BetterLyrics.WinUI3
|
|||||||
|
|
||||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||||
{
|
{
|
||||||
WindowHook.OpenOrShowWindow<LyricsWindow>();
|
// 设置托盘
|
||||||
|
WindowHook.OpenOrShowWindow<SystemTrayWindow>();
|
||||||
|
WindowHook.HideWindow<SystemTrayWindow>();
|
||||||
|
|
||||||
|
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
|
||||||
if (Ioc.Default.GetRequiredService<ISettingsService>().AppSettings.MusicGallerySettings.AutoOpen)
|
if (Ioc.Default.GetRequiredService<ISettingsService>().AppSettings.MusicGallerySettings.AutoOpen)
|
||||||
{
|
{
|
||||||
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
|
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
|
||||||
@@ -110,13 +114,14 @@ namespace BetterLyrics.WinUI3
|
|||||||
.AddSingleton<LyricsWindowSettingsControlViewModel>()
|
.AddSingleton<LyricsWindowSettingsControlViewModel>()
|
||||||
.AddSingleton<LyricsWindowSwitchControlViewModel>()
|
.AddSingleton<LyricsWindowSwitchControlViewModel>()
|
||||||
.AddSingleton<LyricsWindowSwitchWindowViewModel>()
|
.AddSingleton<LyricsWindowSwitchWindowViewModel>()
|
||||||
.AddSingleton<LyricsWindowViewModel>()
|
|
||||||
|
.AddSingleton<NowPlayingWindowViewModel>()
|
||||||
|
.AddSingleton<NowPlayingPageViewModel>()
|
||||||
|
|
||||||
.AddSingleton<SettingsWindowViewModel>()
|
.AddSingleton<SettingsWindowViewModel>()
|
||||||
.AddSingleton<SystemTrayViewModel>()
|
.AddSingleton<SystemTrayViewModel>()
|
||||||
.AddSingleton<SettingsPageViewModel>()
|
.AddSingleton<SettingsPageViewModel>()
|
||||||
.AddSingleton<LyricsPageViewModel>()
|
|
||||||
.AddSingleton<MusicGalleryViewModel>()
|
.AddSingleton<MusicGalleryViewModel>()
|
||||||
.AddSingleton<LyricsRendererViewModel>()
|
|
||||||
.AddSingleton<AboutControlViewModel>()
|
.AddSingleton<AboutControlViewModel>()
|
||||||
.BuildServiceProvider()
|
.BuildServiceProvider()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,24 +23,28 @@
|
|||||||
<None Remove="Assets\Segoe Fluent Icons.ttf" />
|
<None Remove="Assets\Segoe Fluent Icons.ttf" />
|
||||||
<None Remove="Assets\Wiki82.profile.xml" />
|
<None Remove="Assets\Wiki82.profile.xml" />
|
||||||
<None Remove="Controls\AboutControl.xaml" />
|
<None Remove="Controls\AboutControl.xaml" />
|
||||||
<None Remove="Controls\AlbumArtLayoutSettingsControl.xaml" />
|
<None Remove="Controls\AlbumArtAreaEffectSettingsControl.xaml" />
|
||||||
<None Remove="Controls\AppSettingsControl.xaml" />
|
<None Remove="Controls\AppSettingsControl.xaml" />
|
||||||
<None Remove="Controls\DemoWindowGrid.xaml" />
|
<None Remove="Controls\DemoWindowGrid.xaml" />
|
||||||
<None Remove="Controls\ExtendedSlider.xaml" />
|
<None Remove="Controls\ExtendedSlider.xaml" />
|
||||||
<None Remove="Controls\FontFamilyAutoSuggestBox.xaml" />
|
<None Remove="Controls\FontFamilyAutoSuggestBox.xaml" />
|
||||||
|
<None Remove="Controls\ImageSwitcher.xaml" />
|
||||||
<None Remove="Controls\LyricsSearchControl.xaml" />
|
<None Remove="Controls\LyricsSearchControl.xaml" />
|
||||||
<None Remove="Controls\LyricsStyleSettingsControl.xaml" />
|
<None Remove="Controls\LyricsStyleSettingsControl.xaml" />
|
||||||
<None Remove="Controls\LyricsWindowSettingsControl.xaml" />
|
<None Remove="Controls\LyricsWindowSettingsControl.xaml" />
|
||||||
<None Remove="Controls\LyricsWindowSwitchControl.xaml" />
|
<None Remove="Controls\LyricsWindowSwitchControl.xaml" />
|
||||||
<None Remove="Controls\MediaSettingsControl.xaml" />
|
<None Remove="Controls\MediaSettingsControl.xaml" />
|
||||||
<None Remove="Controls\PlaybackSettingsControl.xaml" />
|
<None Remove="Controls\PlaybackSettingsControl.xaml" />
|
||||||
|
<None Remove="Controls\PropertyRow.xaml" />
|
||||||
<None Remove="Controls\ShortcutTextBox.xaml" />
|
<None Remove="Controls\ShortcutTextBox.xaml" />
|
||||||
<None Remove="Controls\SystemTray.xaml" />
|
<None Remove="Controls\SystemTray.xaml" />
|
||||||
|
<None Remove="Controls\WindowSettingsControl.xaml" />
|
||||||
<None Remove="Views\LyricsSearchWindow.xaml" />
|
<None Remove="Views\LyricsSearchWindow.xaml" />
|
||||||
<None Remove="Views\LyricsWindowSwitchWindow.xaml" />
|
<None Remove="Views\LyricsWindowSwitchWindow.xaml" />
|
||||||
<None Remove="Views\MusicGalleryPage.xaml" />
|
<None Remove="Views\MusicGalleryPage.xaml" />
|
||||||
<None Remove="Views\MusicGalleryWindow.xaml" />
|
<None Remove="Views\MusicGalleryWindow.xaml" />
|
||||||
<None Remove="Views\SettingsWindow.xaml" />
|
<None Remove="Views\SettingsWindow.xaml" />
|
||||||
|
<None Remove="Views\SystemTrayWindow.xaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Logo.ico" />
|
<Content Include="Logo.ico" />
|
||||||
@@ -64,23 +68,22 @@
|
|||||||
<PackageReference Include="ComputeSharp.D2D1.WinUI" Version="3.2.0" />
|
<PackageReference Include="ComputeSharp.D2D1.WinUI" Version="3.2.0" />
|
||||||
<PackageReference Include="csharp-kana" Version="1.0.2" />
|
<PackageReference Include="csharp-kana" Version="1.0.2" />
|
||||||
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
|
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
|
||||||
<PackageReference Include="DevWinUI.Controls" Version="9.5.0" />
|
<PackageReference Include="DevWinUI.Controls" Version="9.7.0" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.6.1.70" />
|
|
||||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||||
<PackageReference Include="F23.StringSimilarity" Version="7.0.0" />
|
<PackageReference Include="F23.StringSimilarity" Version="7.0.1" />
|
||||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.2" />
|
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.4.1" />
|
||||||
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
|
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
|
||||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0" />
|
||||||
<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.6901" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||||
<PackageReference Include="NAudio.Wasapi" Version="2.2.1" />
|
<PackageReference Include="NAudio.Wasapi" Version="2.2.1" />
|
||||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||||
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||||
<PackageReference Include="NTextCat" Version="0.3.65" />
|
<PackageReference Include="NTextCat" Version="0.3.65" />
|
||||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.3-dev-02320" />
|
<PackageReference Include="Serilog.Extensions.Logging" Version="10.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="10.0.0" />
|
<PackageReference Include="System.Drawing.Common" Version="10.0.0" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.0" />
|
||||||
@@ -91,8 +94,9 @@
|
|||||||
<PackageReference Include="Vanara.PInvoke.Shell32" Version="4.2.1" />
|
<PackageReference Include="Vanara.PInvoke.Shell32" Version="4.2.1" />
|
||||||
<PackageReference Include="Vanara.PInvoke.User32" Version="4.2.1" />
|
<PackageReference Include="Vanara.PInvoke.User32" Version="4.2.1" />
|
||||||
<PackageReference Include="Vanara.Windows.Shell" Version="4.2.1" />
|
<PackageReference Include="Vanara.Windows.Shell" Version="4.2.1" />
|
||||||
|
<PackageReference Include="VCollab.DiscordRichPresence" Version="1.7.0" />
|
||||||
<PackageReference Include="WinUIEx" Version="2.9.0" />
|
<PackageReference Include="WinUIEx" Version="2.9.0" />
|
||||||
<PackageReference Include="z440.atl.core" Version="7.8.0" />
|
<PackageReference Include="z440.atl.core" Version="7.9.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\ColorThief.WinUI3\ColorThief.WinUI3.csproj" />
|
<ProjectReference Include="..\..\ColorThief.WinUI3\ColorThief.WinUI3.csproj" />
|
||||||
@@ -291,7 +295,7 @@
|
|||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="Controls\AlbumArtLayoutSettingsControl.xaml">
|
<Page Update="Controls\AlbumArtAreaStyleSettingsControl.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -333,6 +337,36 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="TemplateSelector\" />
|
<Folder Include="TemplateSelector\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Views\SystemTrayWindow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Controls\WindowSettingsControl.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Controls\AlbumArtAreaEffectSettingsControl.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Controls\ImageSwitcher.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Controls\ShadowImage.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Controls\PropertyRow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="Controls\AboutControl.xaml">
|
<Page Update="Controls\AboutControl.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
public class ExtendedGenreFiled
|
public class ExtendedGenreFiled
|
||||||
{
|
{
|
||||||
public const string NetEaseCloudMusicTrackID = "NCM-";
|
public const string NetEaseCloudMusicTrackID = "NCM-";
|
||||||
|
public const string QQMusicTrackID = "QQ-";
|
||||||
public const string FileName = "FILENAME-";
|
public const string FileName = "FILENAME-";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
{
|
{
|
||||||
public static class Link
|
public static class Link
|
||||||
{
|
{
|
||||||
public const string MicrosoftStoreUrl = "https://apps.microsoft.com/detail/9p1wcd1p597r";
|
public const string MicrosoftStore = "https://apps.microsoft.com/detail/9p1wcd1p597r";
|
||||||
public const string GitHubUrl = "https://github.com/jayfunc/BetterLyrics";
|
|
||||||
public const string ShareHubUrl = $"{GitHubUrl}/blob/dev/ShareHub/index.md";
|
public const string GitHub = "https://github.com/jayfunc/BetterLyrics";
|
||||||
public const string TermsOfServiceUrl = $"{GitHubUrl}/blob/dev/TermsofService.md";
|
public const string ShareHub = $"{GitHub}/blob/dev/ShareHub/index.md";
|
||||||
public const string PrivacyPolicy = $"{GitHubUrl}/blob/dev/PrivacyPolicy.md";
|
public const string TermsOfService = $"{GitHub}/blob/dev/TermsofService.md";
|
||||||
public const string WikiUrl = "https://jayfunc.blog/work/betterlyrics";
|
public const string PrivacyPolicy = $"{GitHub}/blob/dev/PrivacyPolicy.md";
|
||||||
public const string AppleMusicCfgUrl = $"{WikiUrl}#lyrics-sources-configuration";
|
public const string UserGuide = $"{GitHub}/wiki/User-Guide";
|
||||||
public const string FAQUrl = $"{WikiUrl}#faq";
|
public const string AppleMusicCfg = $"{UserGuide}#lyrics-source-configuration";
|
||||||
public const string QQGroupUrl = "https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info";
|
|
||||||
public const string DiscordUrl = "https://discord.gg/5yAQPnyCKv";
|
public const string QQGroup = "https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info";
|
||||||
public const string TelegramUrl = "https://t.me/+svhSLZ7awPsxNGY1";
|
public const string Discord = "https://discord.gg/5yAQPnyCKv";
|
||||||
|
public const string Telegram = "https://t.me/+svhSLZ7awPsxNGY1";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
public const string Edge = "MSEdge";
|
public const string Edge = "MSEdge";
|
||||||
public const string BetterLyrics = "37412.BetterLyrics_rd1g0rsrrtxw8!App";
|
public const string BetterLyrics = "37412.BetterLyrics_rd1g0rsrrtxw8!App";
|
||||||
public const string BetterLyricsDebug = "37412.BetterLyrics_c8mj3v9sysxb4!App";
|
public const string BetterLyricsDebug = "37412.BetterLyrics_c8mj3v9sysxb4!App";
|
||||||
public const string SaltPlayerForWindows = "Sakawish.SaltPlayerforWindows_q65q631pyh094!SaltPlayerforWindows";
|
public const string SaltPlayerForWindowsMS = "Sakawish.SaltPlayerforWindows_q65q631pyh094!SaltPlayerforWindows";
|
||||||
|
public const string SaltPlayerForWindowsSteam = "Salt Player for Windows.exe";
|
||||||
public const string MoeKoeMusic = "cn.MoeKoe.Music";
|
public const string MoeKoeMusic = "cn.MoeKoe.Music";
|
||||||
public const string MoeKoeMusicAlternative = "electron.app.MoeKoe Music";
|
public const string MoeKoeMusicAlternative = "electron.app.MoeKoe Music";
|
||||||
public const string Listen1 = "com.listen1.listen1";
|
public const string Listen1 = "com.listen1.listen1";
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
using System;
|
namespace BetterLyrics.WinUI3.Constants
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Constants
|
|
||||||
{
|
{
|
||||||
public class PlayerName
|
public class PlayerName
|
||||||
{
|
{
|
||||||
@@ -24,7 +20,8 @@ namespace BetterLyrics.WinUI3.Constants
|
|||||||
public const string Edge = "Microsoft Edge";
|
public const string Edge = "Microsoft Edge";
|
||||||
public const string BetterLyrics = "BetterLyrics";
|
public const string BetterLyrics = "BetterLyrics";
|
||||||
public const string BetterLyricsDebug = "BetterLyrics (Debug)";
|
public const string BetterLyricsDebug = "BetterLyrics (Debug)";
|
||||||
public const string SaltPlayerForWindows = "Salt Player for Windows";
|
public const string SaltPlayerForWindowsMS = "Salt Player for Windows (Microsoft Store)";
|
||||||
|
public const string SaltPlayerForWindowsSteam = "Salt Player for Windows (Steam)";
|
||||||
public const string MoeKoeMusic = "MoeKoe Music";
|
public const string MoeKoeMusic = "MoeKoe Music";
|
||||||
public const string Listen1 = "Listen 1";
|
public const string Listen1 = "Listen 1";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace BetterLyrics.WinUI3.Constants
|
|||||||
{
|
{
|
||||||
public static class Time
|
public static class Time
|
||||||
{
|
{
|
||||||
public static readonly TimeSpan DebounceTimeout = TimeSpan.FromMilliseconds(300);
|
public static readonly TimeSpan DebounceTimeout = TimeSpan.FromMilliseconds(250);
|
||||||
|
public static readonly TimeSpan AnimationDuration = TimeSpan.FromMilliseconds(350);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,11 +51,10 @@
|
|||||||
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||||
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.GitHubUrl}" />
|
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.GitHub}" />
|
||||||
<HyperlinkButton Content="Wiki" NavigateUri="{x:Bind const:Link.WikiUrl}" />
|
<HyperlinkButton x:Uid="UserGuide" NavigateUri="{x:Bind const:Link.UserGuide}" />
|
||||||
<HyperlinkButton Content="FAQ" NavigateUri="{x:Bind const:Link.FAQUrl}" />
|
|
||||||
<HyperlinkButton x:Uid="PrivacyPolicy" NavigateUri="{x:Bind const:Link.PrivacyPolicy}" />
|
<HyperlinkButton x:Uid="PrivacyPolicy" NavigateUri="{x:Bind const:Link.PrivacyPolicy}" />
|
||||||
<HyperlinkButton x:Uid="TermsOfService" NavigateUri="{x:Bind const:Link.TermsOfServiceUrl}" />
|
<HyperlinkButton x:Uid="TermsOfService" NavigateUri="{x:Bind const:Link.TermsOfService}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
@@ -65,9 +64,9 @@
|
|||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<TextBlock x:Uid="SetingsPageFeedback" />
|
<TextBlock x:Uid="SetingsPageFeedback" />
|
||||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||||
<HyperlinkButton x:Uid="SettingsPageQQGroup" NavigateUri="{x:Bind const:Link.QQGroupUrl}" />
|
<HyperlinkButton x:Uid="SettingsPageQQGroup" NavigateUri="{x:Bind const:Link.QQGroup}" />
|
||||||
<HyperlinkButton x:Uid="SettingsPageDiscord" NavigateUri="{x:Bind const:Link.DiscordUrl}" />
|
<HyperlinkButton x:Uid="SettingsPageDiscord" NavigateUri="{x:Bind const:Link.Discord}" />
|
||||||
<HyperlinkButton x:Uid="SettingsPageTelegram" NavigateUri="{x:Bind const:Link.TelegramUrl}" />
|
<HyperlinkButton x:Uid="SettingsPageTelegram" NavigateUri="{x:Bind const:Link.Telegram}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtAreaEffectSettingsControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dev="using:DevWinUI"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
||||||
|
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||||
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
|
<TextBlock x:Uid="SettingsPageAlbumEffect" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageImageSwitchType" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ComboBox SelectedIndex="{x:Bind AlbumArtAreaEffectSettings.ImageSwitchType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageCrossfade" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageSlide" />
|
||||||
|
</ComboBox>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices.WindowsRuntime;
|
||||||
|
using Windows.Foundation;
|
||||||
|
using Windows.Foundation.Collections;
|
||||||
|
|
||||||
|
// 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.Controls;
|
||||||
|
|
||||||
|
public sealed partial class AlbumArtAreaEffectSettingsControl : UserControl
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty AlbumArtAreaEffectSettingsProperty =
|
||||||
|
DependencyProperty.Register(nameof(AlbumArtAreaEffectSettings), typeof(AlbumArtAreaEffectSettings), typeof(AlbumArtAreaEffectSettingsControl), new PropertyMetadata(default));
|
||||||
|
|
||||||
|
public AlbumArtAreaEffectSettings AlbumArtAreaEffectSettings
|
||||||
|
{
|
||||||
|
get => (AlbumArtAreaEffectSettings)GetValue(AlbumArtAreaEffectSettingsProperty);
|
||||||
|
set => SetValue(AlbumArtAreaEffectSettingsProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlbumArtAreaEffectSettingsControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<UserControl
|
<UserControl
|
||||||
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtLayoutSettingsControl"
|
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtAreaStyleSettingsControl"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||||
@@ -16,29 +16,40 @@
|
|||||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
|
<TextBlock x:Uid="SettingsPageAlbumStyle" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
|
||||||
|
<!-- 整体对齐 -->
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ComboBox SelectedIndex="{x:Bind AlbumArtLayoutSettings.SongInfoAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageLeft" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageCenter" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageRight" />
|
||||||
|
</ComboBox>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<TextBlock x:Uid="SettingsPageAlbumArt" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="SettingsPageAlbumArt" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
|
||||||
|
<!-- 专辑高度 -->
|
||||||
<dev:SettingsExpander
|
<dev:SettingsExpander
|
||||||
x:Uid="SettingsPageAlbumArtSize"
|
x:Uid="SettingsPageAlbumArtHeight"
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
Glyph=}"
|
Glyph=}"
|
||||||
IsExpanded="True">
|
IsExpanded="True">
|
||||||
<dev:SettingsExpander.Items>
|
<dev:SettingsExpander.Items>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
|
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
|
||||||
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.AutoAlbumArtSize, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.IsAutoCoverImageHeight, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
<dev:SettingsCard IsEnabled="{x:Bind AlbumArtLayoutSettings.AutoAlbumArtSize, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
|
<dev:SettingsCard IsEnabled="{x:Bind AlbumArtLayoutSettings.IsAutoCoverImageHeight, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
|
||||||
<local:ExtendedSlider
|
<local:ExtendedSlider
|
||||||
Frequency="2"
|
Maximum="512"
|
||||||
Maximum="800"
|
Minimum="0"
|
||||||
Minimum="10"
|
|
||||||
ResetButtonVisibility="Collapsed"
|
|
||||||
Unit="px"
|
Unit="px"
|
||||||
Value="{x:Bind AlbumArtLayoutSettings.AlbumArtSize, Mode=TwoWay}" />
|
Value="{x:Bind AlbumArtLayoutSettings.CoverImageHeight, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
<!-- 专辑圆角 -->
|
||||||
<dev:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<local:ExtendedSlider
|
<local:ExtendedSlider
|
||||||
Default="12"
|
Default="12"
|
||||||
@@ -48,6 +59,7 @@
|
|||||||
Value="{x:Bind AlbumArtLayoutSettings.CoverImageRadius, Mode=TwoWay}" />
|
Value="{x:Bind AlbumArtLayoutSettings.CoverImageRadius, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<!-- 专辑阴影 -->
|
||||||
<dev:SettingsCard x:Uid="SettingsPageAlbumShadowAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsCard x:Uid="SettingsPageAlbumShadowAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<local:ExtendedSlider
|
<local:ExtendedSlider
|
||||||
Default="12"
|
Default="12"
|
||||||
@@ -58,14 +70,6 @@
|
|||||||
|
|
||||||
<TextBlock x:Uid="SettingsPageSongInfo" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="SettingsPageSongInfo" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageSongInfoAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<ComboBox SelectedIndex="{x:Bind AlbumArtLayoutSettings.SongInfoAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageSongInfoLeft" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageSongInfoCenter" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageSongInfoRight" />
|
|
||||||
</ComboBox>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsExpander
|
<dev:SettingsExpander
|
||||||
x:Uid="SettingsPageLyricsFontSize"
|
x:Uid="SettingsPageLyricsFontSize"
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
@@ -87,21 +91,15 @@
|
|||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
<dev:SettingsExpander
|
<dev:SettingsCard x:Uid="SettingsPageShowTitle" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
x:Uid="SettingsPageShowTitle"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="True">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.ShowTitle, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.ShowTitle, Mode=TwoWay}" />
|
||||||
<dev:SettingsExpander.Items>
|
</dev:SettingsCard>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageShowArtists">
|
<dev:SettingsCard x:Uid="SettingsPageShowArtists" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<ToggleSwitch IsEnabled="{x:Bind AlbumArtLayoutSettings.ShowTitle, Mode=OneWay}" IsOn="{x:Bind AlbumArtLayoutSettings.ShowArtists, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.ShowArtists, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageShowAlbum">
|
<dev:SettingsCard x:Uid="SettingsPageShowAlbum" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<ToggleSwitch IsEnabled="{x:Bind AlbumArtLayoutSettings.ShowTitle, Mode=OneWay}" IsOn="{x:Bind AlbumArtLayoutSettings.ShowAlbum, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.ShowAlbum, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -7,18 +7,18 @@ using Microsoft.UI.Xaml.Controls;
|
|||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Controls
|
namespace BetterLyrics.WinUI3.Controls
|
||||||
{
|
{
|
||||||
public sealed partial class AlbumArtLayoutSettingsControl : UserControl
|
public sealed partial class AlbumArtAreaStyleSettingsControl : UserControl
|
||||||
{
|
{
|
||||||
public static readonly DependencyProperty AlbumArtLayoutSettingsProperty =
|
public static readonly DependencyProperty AlbumArtLayoutSettingsProperty =
|
||||||
DependencyProperty.Register(nameof(AlbumArtLayoutSettings), typeof(AlbumArtLayoutSettings), typeof(AlbumArtLayoutSettingsControl), new PropertyMetadata(default));
|
DependencyProperty.Register(nameof(AlbumArtLayoutSettings), typeof(AlbumArtAreaStyleSettings), typeof(AlbumArtAreaStyleSettingsControl), new PropertyMetadata(default));
|
||||||
|
|
||||||
public AlbumArtLayoutSettings AlbumArtLayoutSettings
|
public AlbumArtAreaStyleSettings AlbumArtLayoutSettings
|
||||||
{
|
{
|
||||||
get => (AlbumArtLayoutSettings)GetValue(AlbumArtLayoutSettingsProperty);
|
get => (AlbumArtAreaStyleSettings)GetValue(AlbumArtLayoutSettingsProperty);
|
||||||
set => SetValue(AlbumArtLayoutSettingsProperty, value);
|
set => SetValue(AlbumArtLayoutSettingsProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AlbumArtLayoutSettingsControl()
|
public AlbumArtAreaStyleSettingsControl()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
@@ -6,25 +6,54 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<TextBlock
|
||||||
|
x:Name="SelectedLocalizedFontFamilyTextBlock"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{StaticResource TextFillColorSecondaryBrush}"
|
||||||
|
IsTextSelectionEnabled="True" />
|
||||||
<AutoSuggestBox
|
<AutoSuggestBox
|
||||||
x:Name="AutoSuggestBox"
|
x:Name="AutoSuggestBox"
|
||||||
Width="150"
|
MinWidth="180"
|
||||||
GotFocus="AutoSuggestBox_GotFocus"
|
GotFocus="AutoSuggestBox_GotFocus"
|
||||||
LostFocus="AutoSuggestBox_LostFocus"
|
LostFocus="AutoSuggestBox_LostFocus"
|
||||||
SuggestionChosen="AutoSuggestBox_SuggestionChosen"
|
SuggestionChosen="AutoSuggestBox_SuggestionChosen"
|
||||||
Text="{x:Bind SelectedFontFamily, Mode=OneWay}"
|
Text="{x:Bind SelectedFontFamily, Mode=OneWay}"
|
||||||
TextChanged="AutoSuggestBox_TextChanged" />
|
TextChanged="AutoSuggestBox_TextChanged">
|
||||||
|
<!--<AutoSuggestBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="models:ExtendedFontFamily">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{x:Bind LocalizedFontFamily}" TextWrapping="Wrap" />
|
||||||
|
<TextBlock
|
||||||
|
FontSize="12"
|
||||||
|
Foreground="{StaticResource TextFillColorSecondaryBrush}"
|
||||||
|
Text="{x:Bind FontFamily}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</AutoSuggestBox.ItemTemplate>-->
|
||||||
|
</AutoSuggestBox>
|
||||||
<Button
|
<Button
|
||||||
Click="Button_Click"
|
Click="Button_Click"
|
||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
FontSize=12,
|
FontSize=12,
|
||||||
Glyph=}"
|
Glyph=}"
|
||||||
Style="{StaticResource GhostButtonStyle}" />
|
Style="{StaticResource GhostButtonStyle}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip x:Uid="SettingsPageRefreshDropdown" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
|
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip x:Uid="SettingsPageCollapseDropdown" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
using BetterLyrics.WinUI3.Helper;
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -11,11 +18,15 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
{
|
{
|
||||||
public sealed partial class FontFamilyAutoSuggestBox : UserControl
|
public sealed partial class FontFamilyAutoSuggestBox : UserControl
|
||||||
{
|
{
|
||||||
private List<string> SystemFontNames { get; set; } = [.. FontHelper.SystemFontFamilies];
|
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||||
|
|
||||||
|
//private List<ExtendedFontFamily> FontFamilies { get; set; } = [];
|
||||||
|
private List<string> FontFamilies { get; set; } = [];
|
||||||
|
|
||||||
public FontFamilyAutoSuggestBox()
|
public FontFamilyAutoSuggestBox()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
RefreshFontFamilies();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty SelectedFontFamilyProperty =
|
public static readonly DependencyProperty SelectedFontFamilyProperty =
|
||||||
@@ -27,20 +38,47 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
set => SetValue(SelectedFontFamilyProperty, value);
|
set => SetValue(SelectedFontFamilyProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RefreshFontFamilies()
|
||||||
|
{
|
||||||
|
//Task.Run(() =>
|
||||||
|
//{
|
||||||
|
// var fontFamilies = FontHelper.SystemFontFamilies.Select(x => new ExtendedFontFamily()
|
||||||
|
// {
|
||||||
|
// FontFamily = x,
|
||||||
|
// LocalizedFontFamily = FontHelper.GetLocalizedFontFamilyName(x, _settingsService.AppSettings.GeneralSettings.LanguageCode)
|
||||||
|
// }).OrderBy(x => x.LocalizedFontFamily).ToList();
|
||||||
|
// DispatcherQueue.TryEnqueue(() =>
|
||||||
|
// {
|
||||||
|
// FontFamilies = fontFamilies;
|
||||||
|
// });
|
||||||
|
//});
|
||||||
|
FontFamilies = FontHelper.SystemFontFamilies.OrderBy(x => x).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
|
private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
|
||||||
{
|
{
|
||||||
SelectedFontFamily = args.SelectedItem.ToString() ?? "";
|
if (args.SelectedItem is ExtendedFontFamily extendedFontFamily)
|
||||||
|
{
|
||||||
|
SelectedFontFamily = extendedFontFamily.FontFamily;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SelectedFontFamily = args.SelectedItem.ToString() ?? "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAutoSuggestBoxItemsSource()
|
private void UpdateAutoSuggestBoxItemsSource(string? query = null)
|
||||||
{
|
{
|
||||||
|
query ??= AutoSuggestBox.Text;
|
||||||
|
|
||||||
|
//var suitableItems = new List<ExtendedFontFamily>();
|
||||||
var suitableItems = new List<string>();
|
var suitableItems = new List<string>();
|
||||||
var splitText = AutoSuggestBox.Text.ToLower().Split(" ");
|
var splitText = query.ToLower().Split(" ");
|
||||||
foreach (var fontFamily in SystemFontNames)
|
foreach (var fontFamily in FontFamilies)
|
||||||
{
|
{
|
||||||
var found = splitText.All((key) =>
|
bool found = splitText.All((key) =>
|
||||||
{
|
{
|
||||||
|
//return fontFamily.FontFamily.ToLower().Contains(key) || fontFamily.LocalizedFontFamily.ToLower().Contains(key);
|
||||||
return fontFamily.ToLower().Contains(key);
|
return fontFamily.ToLower().Contains(key);
|
||||||
});
|
});
|
||||||
if (found)
|
if (found)
|
||||||
@@ -50,9 +88,15 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
}
|
}
|
||||||
if (suitableItems.Count == 0)
|
if (suitableItems.Count == 0)
|
||||||
{
|
{
|
||||||
|
//suitableItems.Add(new ExtendedFontFamily()
|
||||||
|
//{
|
||||||
|
// FontFamily = "",
|
||||||
|
// LocalizedFontFamily = "N/A"
|
||||||
|
//});
|
||||||
suitableItems.Add("N/A");
|
suitableItems.Add("N/A");
|
||||||
}
|
}
|
||||||
AutoSuggestBox.ItemsSource = suitableItems.Order();
|
//AutoSuggestBox.ItemsSource = suitableItems.OrderBy(x => x.LocalizedFontFamily);
|
||||||
|
AutoSuggestBox.ItemsSource = suitableItems.OrderBy(x => x);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||||
@@ -63,16 +107,18 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
{
|
{
|
||||||
UpdateAutoSuggestBoxItemsSource();
|
UpdateAutoSuggestBoxItemsSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectedLocalizedFontFamilyTextBlock.Text = FontHelper.GetLocalizedFontFamilyName(SelectedFontFamily, _settingsService.AppSettings.GeneralSettings.LanguageCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Button_Click(object sender, RoutedEventArgs e)
|
private void Button_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
SystemFontNames = [.. FontHelper.SystemFontFamilies];
|
RefreshFontFamilies();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AutoSuggestBox_GotFocus(object sender, RoutedEventArgs e)
|
private void AutoSuggestBox_GotFocus(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
UpdateAutoSuggestBoxItemsSource();
|
UpdateAutoSuggestBoxItemsSource("");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AutoSuggestBox_LostFocus(object sender, RoutedEventArgs e)
|
private void AutoSuggestBox_LostFocus(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="BetterLyrics.WinUI3.Controls.ImageSwitcher"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:const="using:BetterLyrics.WinUI3.Constants"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.OpacityTransition>
|
||||||
|
<ScalarTransition Duration="{x:Bind const:Time.AnimationDuration}" />
|
||||||
|
</Grid.OpacityTransition>
|
||||||
|
<local:ShadowImage
|
||||||
|
x:Name="LastAlbumArtImage"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CornerRadiusAmount="{x:Bind CornerRadiusAmount, Mode=OneWay}"
|
||||||
|
ShadowAmount="{x:Bind ShadowAmount, Mode=OneWay}" />
|
||||||
|
<local:ShadowImage
|
||||||
|
x:Name="AlbumArtImage"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CornerRadiusAmount="{x:Bind CornerRadiusAmount, Mode=OneWay}"
|
||||||
|
ShadowAmount="{x:Bind ShadowAmount, Mode=OneWay}" />
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices.WindowsRuntime;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Windows.Foundation;
|
||||||
|
using Windows.Foundation.Collections;
|
||||||
|
|
||||||
|
// 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.Controls;
|
||||||
|
|
||||||
|
public sealed partial class ImageSwitcher : UserControl
|
||||||
|
{
|
||||||
|
public int CornerRadiusAmount
|
||||||
|
{
|
||||||
|
get { return (int)GetValue(CornerRadiusAmountProperty); }
|
||||||
|
set { SetValue(CornerRadiusAmountProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty CornerRadiusAmountProperty =
|
||||||
|
DependencyProperty.Register(nameof(CornerRadiusAmount), typeof(int), typeof(ImageSwitcher), new PropertyMetadata(0));
|
||||||
|
|
||||||
|
public int ShadowAmount
|
||||||
|
{
|
||||||
|
get { return (int)GetValue(ShadowAmountProperty); }
|
||||||
|
set { SetValue(ShadowAmountProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty ShadowAmountProperty =
|
||||||
|
DependencyProperty.Register(nameof(ShadowAmount), typeof(int), typeof(ImageSwitcher), new PropertyMetadata(0));
|
||||||
|
|
||||||
|
public ImageSource? Source
|
||||||
|
{
|
||||||
|
get { return (ImageSource?)GetValue(SourceProperty); }
|
||||||
|
set { SetValue(SourceProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty SourceProperty =
|
||||||
|
DependencyProperty.Register(nameof(Source), typeof(ImageSource), typeof(ImageSwitcher), new PropertyMetadata(null, OnDependencyPropertyChanged));
|
||||||
|
|
||||||
|
public ImageSwitchType SwitchType
|
||||||
|
{
|
||||||
|
get { return (ImageSwitchType)GetValue(SwitchTypeProperty); }
|
||||||
|
set { SetValue(SwitchTypeProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty SwitchTypeProperty =
|
||||||
|
DependencyProperty.Register(nameof(SwitchType), typeof(ImageSwitchType), typeof(ImageSwitcher), new PropertyMetadata(ImageSwitchType.Crossfade));
|
||||||
|
|
||||||
|
public ImageSwitcher()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSource()
|
||||||
|
{
|
||||||
|
switch (SwitchType)
|
||||||
|
{
|
||||||
|
case ImageSwitchType.Crossfade:
|
||||||
|
UpdateSourceCrossfade();
|
||||||
|
break;
|
||||||
|
case ImageSwitchType.Slide:
|
||||||
|
UpdateSourceSlide();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSourceCrossfade()
|
||||||
|
{
|
||||||
|
// Ϊ<><CEAA><EFBFBD><EFBFBD>ͼƬ<CDBC><C6AC><EFBFBD>þ<EFBFBD>Դ
|
||||||
|
LastAlbumArtImage.Source = AlbumArtImage.Source;
|
||||||
|
// ʹ<><CAB9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD>
|
||||||
|
LastAlbumArtImage.TranslationTransition = null;
|
||||||
|
LastAlbumArtImage.OpacityTransition = null;
|
||||||
|
LastAlbumArtImage.Translation = new();
|
||||||
|
LastAlbumArtImage.Opacity = 1;
|
||||||
|
LastAlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||||
|
|
||||||
|
// ʹǰ<CAB9><C7B0>ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD>
|
||||||
|
AlbumArtImage.TranslationTransition = null;
|
||||||
|
AlbumArtImage.OpacityTransition = null;
|
||||||
|
AlbumArtImage.Translation = new();
|
||||||
|
AlbumArtImage.Opacity = 0;
|
||||||
|
AlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||||
|
// ֮<><D6AE>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||||||
|
AlbumArtImage.Source = Source;
|
||||||
|
|
||||||
|
// <20><><EFBFBD>浭<EFBFBD><E6B5AD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
LastAlbumArtImage.Opacity = 0;
|
||||||
|
AlbumArtImage.Opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSourceSlide()
|
||||||
|
{
|
||||||
|
// Ϊ<><CEAA><EFBFBD><EFBFBD>ͼƬ<CDBC><C6AC><EFBFBD>þ<EFBFBD>Դ
|
||||||
|
LastAlbumArtImage.Source = AlbumArtImage.Source;
|
||||||
|
// ʹ<><CAB9><EFBFBD><EFBFBD>λ
|
||||||
|
LastAlbumArtImage.TranslationTransition = null;
|
||||||
|
LastAlbumArtImage.OpacityTransition = null;
|
||||||
|
LastAlbumArtImage.Translation = new();
|
||||||
|
LastAlbumArtImage.Opacity = 1;
|
||||||
|
LastAlbumArtImage.TranslationTransition = new Vector3Transition { Duration = Constants.Time.AnimationDuration };
|
||||||
|
LastAlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||||
|
|
||||||
|
// ʹǰ<CAB9><C7B0>ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD>
|
||||||
|
AlbumArtImage.TranslationTransition = null;
|
||||||
|
AlbumArtImage.OpacityTransition = null;
|
||||||
|
AlbumArtImage.Translation = new(-(float)ActualWidth, 0, 0);
|
||||||
|
AlbumArtImage.Opacity = 0;
|
||||||
|
AlbumArtImage.TranslationTransition = new Vector3Transition { Duration = Constants.Time.AnimationDuration };
|
||||||
|
AlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||||
|
// ֮<><D6AE>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||||||
|
AlbumArtImage.Source = Source;
|
||||||
|
|
||||||
|
// <20><><EFBFBD><EFBFBD>
|
||||||
|
LastAlbumArtImage.Opacity = 0;
|
||||||
|
AlbumArtImage.Opacity = 1;
|
||||||
|
LastAlbumArtImage.Translation = new(-(float)ActualWidth, 0, 0);
|
||||||
|
AlbumArtImage.Translation = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (d is ImageSwitcher imageSwitcher)
|
||||||
|
{
|
||||||
|
if (e.Property == SourceProperty)
|
||||||
|
{
|
||||||
|
imageSwitcher.UpdateSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="SettingsPageBackgroundOverlay" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageTheme" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsCard x:Uid="SettingsPageTheme" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<ComboBox x:Name="ThemeComboBox" SelectedIndex="{x:Bind LyricsBackgroundSettings.LyricsBackgroundTheme, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
<ComboBox x:Name="ThemeComboBox" SelectedIndex="{x:Bind LyricsBackgroundSettings.LyricsBackgroundTheme, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
@@ -47,51 +47,6 @@
|
|||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
<dev:SettingsExpander
|
|
||||||
x:Uid="SettingsPageAlbumArtLayer"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=TwoWay}" />
|
|
||||||
<dev:SettingsExpander.Items>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
|
||||||
<uc:ExtendedSlider
|
|
||||||
Default="100"
|
|
||||||
Maximum="100"
|
|
||||||
Minimum="0"
|
|
||||||
Unit="%"
|
|
||||||
Value="{x:Bind LyricsBackgroundSettings.CoverOverlayOpacity, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageSpeed" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
|
||||||
<uc:ExtendedSlider
|
|
||||||
Default="50"
|
|
||||||
Maximum="100"
|
|
||||||
Minimum="0"
|
|
||||||
Unit="%"
|
|
||||||
Value="{x:Bind LyricsBackgroundSettings.CoverOverlaySpeed, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageBlurAmount" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
|
||||||
<uc:ExtendedSlider
|
|
||||||
Default="100"
|
|
||||||
Maximum="100"
|
|
||||||
Minimum="0"
|
|
||||||
Value="{x:Bind LyricsBackgroundSettings.CoverOverlayBlurAmount, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageBackgroundAcrylicEffectAmount" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
|
||||||
<uc:ExtendedSlider
|
|
||||||
Default="0"
|
|
||||||
Maximum="10"
|
|
||||||
Minimum="0"
|
|
||||||
Value="{x:Bind LyricsBackgroundSettings.CoverAcrylicEffectAmount, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
<dev:SettingsExpander
|
<dev:SettingsExpander
|
||||||
x:Uid="SettingsPageFluidLayer"
|
x:Uid="SettingsPageFluidLayer"
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
@@ -162,6 +117,21 @@
|
|||||||
</ComboBox>
|
</ComboBox>
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageSpectrumLayerStyle" IsEnabled="{x:Bind LyricsBackgroundSettings.IsSpectrumOverlayEnabled, Mode=OneWay}">
|
||||||
|
<ComboBox SelectedIndex="{x:Bind LyricsBackgroundSettings.SpectrumStyle, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageSpectrumStyleCurve" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageSpectrumStyleBar" />
|
||||||
|
</ComboBox>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsBackgroundSettings.IsSpectrumOverlayEnabled, Mode=OneWay}">
|
||||||
|
<uc:ExtendedSlider
|
||||||
|
Default="128"
|
||||||
|
Maximum="1024"
|
||||||
|
Minimum="1"
|
||||||
|
Value="{x:Bind LyricsBackgroundSettings.SpectrumCount, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<UserControl
|
<UserControl
|
||||||
x:Class="BetterLyrics.WinUI3.Renderer.LyricsRenderer"
|
x:Class="BetterLyrics.WinUI3.Controls.LyricsCanvas"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
|
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:local="using:BetterLyrics.WinUI3.Renderer"
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
Unloaded="LyricsCanvas_Unloaded"
|
Unloaded="Canvas_Unloaded"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<canvas:CanvasAnimatedControl
|
<canvas:CanvasAnimatedControl
|
||||||
x:Name="LyricsCanvas"
|
x:Name="Canvas"
|
||||||
CreateResources="LyricsCanvas_CreateResources"
|
CreateResources="Canvas_CreateResources"
|
||||||
Draw="LyricsCanvas_Draw"
|
Draw="Canvas_Draw"
|
||||||
Update="LyricsCanvas_Update" />
|
Update="Canvas_Update" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -0,0 +1,863 @@
|
|||||||
|
// 2025/6/23 by Zhe Fang
|
||||||
|
|
||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Logic;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
|
using BetterLyrics.WinUI3.Renderer;
|
||||||
|
using BetterLyrics.WinUI3.Services.LastFMService;
|
||||||
|
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
||||||
|
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||||
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
|
using Microsoft.UI;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Windows.Foundation;
|
||||||
|
using Windows.UI;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Controls
|
||||||
|
{
|
||||||
|
public sealed partial class LyricsCanvas : UserControl,
|
||||||
|
IRecipient<PropertyChangedMessage<int>>,
|
||||||
|
IRecipient<PropertyChangedMessage<AlbumArtThemeColors>>,
|
||||||
|
IRecipient<PropertyChangedMessage<TimeSpan>>,
|
||||||
|
IRecipient<PropertyChangedMessage<LyricsData?>>,
|
||||||
|
IRecipient<PropertyChangedMessage<LyricsWindowStatus>>,
|
||||||
|
IRecipient<PropertyChangedMessage<double>>,
|
||||||
|
IRecipient<PropertyChangedMessage<bool>>,
|
||||||
|
IRecipient<PropertyChangedMessage<TextAlignmentType>>,
|
||||||
|
IRecipient<PropertyChangedMessage<SongInfo?>>,
|
||||||
|
IRecipient<PropertyChangedMessage<LyricsFontWeight>>,
|
||||||
|
IRecipient<PropertyChangedMessage<string>>
|
||||||
|
{
|
||||||
|
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||||
|
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||||
|
private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
|
||||||
|
private readonly ILastFMService _lastFMService = Ioc.Default.GetRequiredService<ILastFMService>();
|
||||||
|
|
||||||
|
private readonly LyricsRenderer _lyricsRenderer = new();
|
||||||
|
private readonly FluidBackgroundRenderer _fluidRenderer = new();
|
||||||
|
private readonly PureColorBackgroundRenderer _pureColorRenderer = new();
|
||||||
|
private readonly SnowRenderer _snowRenderer = new();
|
||||||
|
private readonly FogRenderer _fogRenderer = new();
|
||||||
|
private readonly SpectrumRenderer _spectrumRenderer = new();
|
||||||
|
|
||||||
|
private readonly LyricsSynchronizer _synchronizer = new();
|
||||||
|
private readonly LyricsAnimator _animator = new();
|
||||||
|
|
||||||
|
private readonly SpectrumAnalyzer _spectrumAnalyzer = new();
|
||||||
|
|
||||||
|
private readonly ValueTransition<Color> _immersiveBgColorTransition = new(
|
||||||
|
initialValue: Colors.Transparent,
|
||||||
|
durationSeconds: 0.3f,
|
||||||
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
|
);
|
||||||
|
private readonly ValueTransition<double> _immersiveBgOpacityTransition = new(
|
||||||
|
initialValue: 1f,
|
||||||
|
durationSeconds: 0.3f
|
||||||
|
);
|
||||||
|
private readonly ValueTransition<Color> _accentColor1Transition = new(
|
||||||
|
initialValue: Colors.Transparent,
|
||||||
|
durationSeconds: 0.3f,
|
||||||
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
|
);
|
||||||
|
private readonly ValueTransition<Color> _accentColor2Transition = new(
|
||||||
|
initialValue: Colors.Transparent,
|
||||||
|
durationSeconds: 0.3f,
|
||||||
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
|
);
|
||||||
|
private readonly ValueTransition<Color> _accentColor3Transition = new(
|
||||||
|
initialValue: Colors.Transparent,
|
||||||
|
durationSeconds: 0.3f,
|
||||||
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
|
);
|
||||||
|
private readonly ValueTransition<Color> _accentColor4Transition = new(
|
||||||
|
initialValue: Colors.Transparent,
|
||||||
|
durationSeconds: 0.3f,
|
||||||
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
|
);
|
||||||
|
private readonly ValueTransition<double> _canvasYScrollTransition = new(
|
||||||
|
initialValue: 0f,
|
||||||
|
durationSeconds: 0.3f,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
private readonly ValueTransition<double> _mouseYScrollTransition = new(
|
||||||
|
initialValue: 0f,
|
||||||
|
durationSeconds: 0.3f,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
|
||||||
|
private TimeSpan _songPositionWithOffset;
|
||||||
|
private TimeSpan _songPosition; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1>
|
||||||
|
private TimeSpan _totalPlayedTime; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ػ<EFBFBD><D8BB>ظ<EFBFBD><D8B8><EFBFBD><EFBFBD>ŵ<EFBFBD>ʱ<EFBFBD>䣩
|
||||||
|
private bool _isLastFMTracked = false;
|
||||||
|
|
||||||
|
private double _renderLyricsStartX = 0;
|
||||||
|
private double _renderLyricsStartY = 0;
|
||||||
|
private double _renderLyricsWidth = 0;
|
||||||
|
private double _renderLyricsHeight = 0;
|
||||||
|
private double _renderLyricsOpacity = 0;
|
||||||
|
|
||||||
|
private Point _mousePosition = new(0, 0);
|
||||||
|
private int _mouseHoverLineIndex = -1;
|
||||||
|
private bool _isMouseInLyricsArea = false;
|
||||||
|
private bool _isMousePressing = false;
|
||||||
|
private bool _isMouseScrolling = false;
|
||||||
|
|
||||||
|
private List<RenderLyricsLine>? _renderLyricsLines = null;
|
||||||
|
|
||||||
|
private bool _isLayoutChanged = true;
|
||||||
|
private bool _isMouseScrollingChanged = false;
|
||||||
|
|
||||||
|
private int _playingLineIndex;
|
||||||
|
private (int Start, int End) _visibleRange;
|
||||||
|
private double _canvasTargetScrollOffset;
|
||||||
|
|
||||||
|
public TimeSpan SongPosition => _songPosition;
|
||||||
|
public double CurrentCanvasYScroll => _canvasYScrollTransition.Value;
|
||||||
|
public double ActualLyricsHeight => LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines);
|
||||||
|
public int CurrentHoveringLineIndex => _mouseHoverLineIndex;
|
||||||
|
|
||||||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC> X <20><><EFBFBD><EFBFBD>
|
||||||
|
public double LyricsStartX
|
||||||
|
{
|
||||||
|
get { return (double)GetValue(LyricsStartXProperty); }
|
||||||
|
set { SetValue(LyricsStartXProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty LyricsStartXProperty =
|
||||||
|
DependencyProperty.Register(nameof(LyricsStartX), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||||
|
|
||||||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ Y <20><><EFBFBD><EFBFBD>
|
||||||
|
public double LyricsStartY
|
||||||
|
{
|
||||||
|
get { return (double)GetValue(LyricsStartYProperty); }
|
||||||
|
set { SetValue(LyricsStartYProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty LyricsStartYProperty =
|
||||||
|
DependencyProperty.Register(nameof(LyricsStartY), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||||
|
|
||||||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
public double LyricsWidth
|
||||||
|
{
|
||||||
|
get { return (double)GetValue(LyricsWidthProperty); }
|
||||||
|
set { SetValue(LyricsWidthProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty LyricsWidthProperty =
|
||||||
|
DependencyProperty.Register(nameof(LyricsWidth), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||||
|
|
||||||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
|
||||||
|
public double LyricsHeight
|
||||||
|
{
|
||||||
|
get { return (double)GetValue(LyricsHeightProperty); }
|
||||||
|
set { SetValue(LyricsHeightProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty LyricsHeightProperty =
|
||||||
|
DependencyProperty.Register(nameof(LyricsHeight), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||||
|
|
||||||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><CDB8><EFBFBD><EFBFBD>
|
||||||
|
public double LyricsOpacity
|
||||||
|
{
|
||||||
|
get { return (double)GetValue(LyricsOpacityProperty); }
|
||||||
|
set { SetValue(LyricsOpacityProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty LyricsOpacityProperty =
|
||||||
|
DependencyProperty.Register(nameof(LyricsOpacity), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <20>û<EFBFBD><C3BB>ٿ<EFBFBD><D9BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD>ľ<EFBFBD><C4BE>루<EFBFBD><EBA3A8> 0 <20><>ʼ<EFBFBD>㣩
|
||||||
|
/// </summary>
|
||||||
|
public double MouseScrollOffset
|
||||||
|
{
|
||||||
|
get { return (double)GetValue(MouseScrollOffsetProperty); }
|
||||||
|
set { SetValue(MouseScrollOffsetProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty MouseScrollOffsetProperty =
|
||||||
|
DependencyProperty.Register(nameof(MouseScrollOffset), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <20>û<EFBFBD><C3BB><EFBFBD><EFBFBD>굱ǰ<EAB5B1><C7B0>λ<EFBFBD>ã<EFBFBD><C3A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڸ<EFBFBD><DAB8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͻǣ<CFBD>
|
||||||
|
/// </summary>
|
||||||
|
public Point MousePosition
|
||||||
|
{
|
||||||
|
get { return (Point)GetValue(MousePositionProperty); }
|
||||||
|
set { SetValue(MousePositionProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty MousePositionProperty =
|
||||||
|
DependencyProperty.Register(nameof(MousePosition), typeof(Point), typeof(LyricsCanvas), new PropertyMetadata(new Point(0, 0), OnLayoutPropChanged));
|
||||||
|
|
||||||
|
public bool IsMouseInLyricsArea
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(IsMouseInLyricsAreaProperty); }
|
||||||
|
set { SetValue(IsMouseInLyricsAreaProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty IsMouseInLyricsAreaProperty =
|
||||||
|
DependencyProperty.Register(nameof(IsMouseInLyricsArea), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
|
||||||
|
|
||||||
|
public bool IsMousePressing
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(IsMousePressingProperty); }
|
||||||
|
set { SetValue(IsMousePressingProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty IsMousePressingProperty =
|
||||||
|
DependencyProperty.Register(nameof(IsMousePressing), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
|
||||||
|
|
||||||
|
public bool IsMouseScrolling
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(IsMouseScrollingProperty); }
|
||||||
|
set { SetValue(IsMouseScrollingProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty IsMouseScrollingProperty =
|
||||||
|
DependencyProperty.Register(nameof(IsMouseScrolling), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
|
||||||
|
|
||||||
|
public LyricsCanvas()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<int>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<AlbumArtThemeColors>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<TimeSpan>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsData?>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsWindowStatus>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<double>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<bool>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<TextAlignmentType>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<SongInfo?>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsFontWeight>>(this);
|
||||||
|
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<string>>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnLayoutPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (d is LyricsCanvas canvas)
|
||||||
|
{
|
||||||
|
if (e.Property == LyricsStartXProperty)
|
||||||
|
{
|
||||||
|
canvas._renderLyricsStartX = Convert.ToDouble(e.NewValue);
|
||||||
|
canvas._isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (e.Property == LyricsStartYProperty)
|
||||||
|
{
|
||||||
|
canvas._renderLyricsStartY = Convert.ToDouble(e.NewValue);
|
||||||
|
canvas._isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (e.Property == LyricsWidthProperty)
|
||||||
|
{
|
||||||
|
canvas._renderLyricsWidth = Convert.ToDouble(e.NewValue);
|
||||||
|
canvas._isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (e.Property == LyricsHeightProperty)
|
||||||
|
{
|
||||||
|
canvas._renderLyricsHeight = Convert.ToDouble(e.NewValue);
|
||||||
|
canvas._isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (e.Property == LyricsOpacityProperty)
|
||||||
|
{
|
||||||
|
canvas._renderLyricsOpacity = Convert.ToDouble(e.NewValue);
|
||||||
|
canvas._isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (e.Property == MouseScrollOffsetProperty)
|
||||||
|
{
|
||||||
|
canvas._mouseYScrollTransition.StartTransition(Convert.ToDouble(e.NewValue));
|
||||||
|
}
|
||||||
|
else if (e.Property == MousePositionProperty)
|
||||||
|
{
|
||||||
|
canvas._mousePosition = (Point)e.NewValue;
|
||||||
|
}
|
||||||
|
else if (e.Property == IsMouseInLyricsAreaProperty)
|
||||||
|
{
|
||||||
|
canvas._isMouseInLyricsArea = (bool)e.NewValue;
|
||||||
|
}
|
||||||
|
else if (e.Property == IsMousePressingProperty)
|
||||||
|
{
|
||||||
|
canvas._isMousePressing = (bool)e.NewValue;
|
||||||
|
}
|
||||||
|
else if (e.Property == IsMouseScrollingProperty)
|
||||||
|
{
|
||||||
|
var value = (bool)e.NewValue;
|
||||||
|
if (canvas._isMouseScrolling != value)
|
||||||
|
{
|
||||||
|
canvas._isMouseScrollingChanged = true;
|
||||||
|
}
|
||||||
|
canvas._isMouseScrolling = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
|
||||||
|
private void Canvas_Draw(ICanvasAnimatedControl sender, CanvasAnimatedDrawEventArgs args)
|
||||||
|
{
|
||||||
|
var bounds = new Rect(0, 0, sender.Size.Width, sender.Size.Height);
|
||||||
|
|
||||||
|
var status = _liveStatesService.LiveStates.LyricsWindowStatus;
|
||||||
|
var albumArtLayout = status.AlbumArtLayoutSettings;
|
||||||
|
var lyricsBg = status.LyricsBackgroundSettings;
|
||||||
|
var lyricsStyle = status.LyricsStyleSettings;
|
||||||
|
var lyricsEffect = status.LyricsEffectSettings;
|
||||||
|
|
||||||
|
double songDuration = _mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0;
|
||||||
|
bool isForceWordByWord = _settingsService.AppSettings.GeneralSettings.IsForceWordByWordEffect;
|
||||||
|
|
||||||
|
var lyricsThemeColors = _mediaSessionsService.AlbumArtThemeColors;
|
||||||
|
|
||||||
|
Color overlayColor;
|
||||||
|
double finalOpacity;
|
||||||
|
|
||||||
|
if (status.IsAdaptToEnvironment)
|
||||||
|
{
|
||||||
|
// <20><><EFBFBD><EFBFBD>Ӧɫ
|
||||||
|
overlayColor = _immersiveBgColorTransition.Value;
|
||||||
|
finalOpacity = _immersiveBgOpacityTransition.Value * lyricsBg.PureColorOverlayOpacity / 100.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ר<><D7A8>ɫ
|
||||||
|
overlayColor = _accentColor1Transition.Value;
|
||||||
|
finalOpacity = lyricsBg.PureColorOverlayOpacity / 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pureColorRenderer.Draw(
|
||||||
|
args.DrawingSession,
|
||||||
|
bounds,
|
||||||
|
overlayColor,
|
||||||
|
finalOpacity,
|
||||||
|
lyricsBg.IsPureColorOverlayEnabled
|
||||||
|
);
|
||||||
|
|
||||||
|
_fluidRenderer.Opacity = lyricsBg.FluidOverlayOpacity;
|
||||||
|
_fluidRenderer.IsEnabled = lyricsBg.IsFluidOverlayEnabled;
|
||||||
|
_fluidRenderer.Draw(sender, args.DrawingSession);
|
||||||
|
|
||||||
|
_snowRenderer.Draw(sender, args.DrawingSession);
|
||||||
|
|
||||||
|
_fogRenderer.Draw(sender, args.DrawingSession);
|
||||||
|
|
||||||
|
_lyricsRenderer.Draw(
|
||||||
|
control: sender,
|
||||||
|
ds: args.DrawingSession,
|
||||||
|
lines: _renderLyricsLines,
|
||||||
|
playingLineIndex: _playingLineIndex,
|
||||||
|
mouseHoverLineIndex: _mouseHoverLineIndex,
|
||||||
|
isMousePressing: _isMousePressing,
|
||||||
|
startVisibleIndex: _visibleRange.Start,
|
||||||
|
endVisibleIndex: _visibleRange.End,
|
||||||
|
lyricsX: _renderLyricsStartX,
|
||||||
|
lyricsY: _renderLyricsStartY,
|
||||||
|
lyricsWidth: _renderLyricsWidth,
|
||||||
|
lyricsHeight: _renderLyricsHeight,
|
||||||
|
userScrollOffset: _mouseYScrollTransition.Value,
|
||||||
|
lyricsOpacity: _renderLyricsOpacity,
|
||||||
|
playingLineTopOffsetFactor: lyricsStyle.PlayingLineTopOffset / 100.0,
|
||||||
|
windowStatus: status,
|
||||||
|
strokeColor: lyricsThemeColors.StrokeFontColor,
|
||||||
|
bgColor: lyricsThemeColors.BgFontColor,
|
||||||
|
fgColor: lyricsThemeColors.FgFontColor,
|
||||||
|
getPlaybackState: (lineIndex) =>
|
||||||
|
{
|
||||||
|
if (_renderLyricsLines == null) return new LinePlaybackState();
|
||||||
|
|
||||||
|
var line = _renderLyricsLines.ElementAtOrDefault(lineIndex);
|
||||||
|
if (line == null) return new LinePlaybackState();
|
||||||
|
|
||||||
|
var nextLine = _renderLyricsLines.ElementAtOrDefault(lineIndex + 1);
|
||||||
|
|
||||||
|
return _synchronizer.GetLinePlayingProgress(
|
||||||
|
_songPositionWithOffset.TotalMilliseconds,
|
||||||
|
line,
|
||||||
|
nextLine,
|
||||||
|
songDuration,
|
||||||
|
isForceWordByWord
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_spectrumAnalyzer.IsCapturing)
|
||||||
|
{
|
||||||
|
_spectrumRenderer.Draw(
|
||||||
|
resourceCreator: sender,
|
||||||
|
ds: args.DrawingSession,
|
||||||
|
spectrumData: _spectrumAnalyzer?.SmoothSpectrum,
|
||||||
|
barCount: _spectrumAnalyzer?.BarCount ?? 1,
|
||||||
|
isEnabled: lyricsBg.IsSpectrumOverlayEnabled,
|
||||||
|
placement: lyricsBg.SpectrumPlacement,
|
||||||
|
style: lyricsBg.SpectrumStyle,
|
||||||
|
canvasWidth: sender.Size.Width,
|
||||||
|
canvasHeight: sender.Size.Height,
|
||||||
|
fillColor: lyricsThemeColors.BgFontColor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
args.DrawingSession.DrawText(
|
||||||
|
$"Lyrics render start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
|
||||||
|
$"Lyrics render size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
|
||||||
|
$"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines)}\n" +
|
||||||
|
$"Playing line (idx): {_playingLineIndex}\n" +
|
||||||
|
$"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
|
||||||
|
$"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
|
||||||
|
$"Total line count: {LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines).End + 1}\n" +
|
||||||
|
$"Played: {_songPosition} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
|
||||||
|
$"Y offset: {_canvasYScrollTransition.Value}\n" +
|
||||||
|
$"User scroll offset: {_mouseYScrollTransition.Value}",
|
||||||
|
new Vector2(0, 0), Colors.Red);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Canvas_Update(ICanvasAnimatedControl sender, CanvasAnimatedUpdateEventArgs args)
|
||||||
|
{
|
||||||
|
var lyricsBg = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings;
|
||||||
|
var lyricsStyle = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings;
|
||||||
|
var lyricsEffect = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings;
|
||||||
|
var albumArtThemeColors = _mediaSessionsService.AlbumArtThemeColors;
|
||||||
|
var lyricsData = _mediaSessionsService.CurrentLyricsData;
|
||||||
|
|
||||||
|
TimeSpan elapsedTime = args.Timing.ElapsedTime;
|
||||||
|
|
||||||
|
_accentColor1Transition.Update(elapsedTime);
|
||||||
|
_accentColor2Transition.Update(elapsedTime);
|
||||||
|
_accentColor3Transition.Update(elapsedTime);
|
||||||
|
_accentColor4Transition.Update(elapsedTime);
|
||||||
|
|
||||||
|
_immersiveBgOpacityTransition.Update(elapsedTime);
|
||||||
|
_immersiveBgColorTransition.Update(elapsedTime);
|
||||||
|
|
||||||
|
UpdatePlaybackState(elapsedTime);
|
||||||
|
|
||||||
|
TriggerRelayout();
|
||||||
|
|
||||||
|
#region UpdatePlayingLineIndex
|
||||||
|
|
||||||
|
int newPlayingIndex = _synchronizer.GetCurrentLineIndex(_songPositionWithOffset.TotalMilliseconds, lyricsData);
|
||||||
|
bool isPlayingLineChanged = newPlayingIndex != _playingLineIndex;
|
||||||
|
_playingLineIndex = newPlayingIndex;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region UpdateTargetScrollOffset
|
||||||
|
|
||||||
|
if (isPlayingLineChanged || _isLayoutChanged)
|
||||||
|
{
|
||||||
|
var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_renderLyricsLines, _playingLineIndex);
|
||||||
|
if (targetScroll.HasValue) _canvasTargetScrollOffset = targetScroll.Value;
|
||||||
|
|
||||||
|
_canvasYScrollTransition.SetEasingType(lyricsEffect.LyricsScrollEasingType);
|
||||||
|
_canvasYScrollTransition.SetDuration(lyricsEffect.LyricsScrollDuration / 1000.0);
|
||||||
|
_canvasYScrollTransition.StartTransition(_canvasTargetScrollOffset, _isLayoutChanged);
|
||||||
|
}
|
||||||
|
_canvasYScrollTransition.Update(elapsedTime);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
_mouseYScrollTransition.Update(elapsedTime);
|
||||||
|
|
||||||
|
_mouseHoverLineIndex = LyricsLayoutManager.FindMouseHoverLineIndex(
|
||||||
|
_renderLyricsLines,
|
||||||
|
_isMouseInLyricsArea,
|
||||||
|
_mousePosition,
|
||||||
|
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value,
|
||||||
|
_renderLyricsStartY,
|
||||||
|
_renderLyricsHeight,
|
||||||
|
lyricsStyle.PlayingLineTopOffset / 100.0
|
||||||
|
);
|
||||||
|
|
||||||
|
_visibleRange = LyricsLayoutManager.CalculateVisibleRange(
|
||||||
|
_renderLyricsLines,
|
||||||
|
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value, // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>λ<EFBFBD><CEBB>
|
||||||
|
_renderLyricsStartY,
|
||||||
|
_renderLyricsHeight,
|
||||||
|
sender.Size.Height,
|
||||||
|
lyricsStyle.PlayingLineTopOffset / 100.0
|
||||||
|
);
|
||||||
|
|
||||||
|
var maxRange = LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines);
|
||||||
|
|
||||||
|
_animator.UpdateLines(
|
||||||
|
_renderLyricsLines,
|
||||||
|
_isMouseScrolling ? maxRange.Start : _visibleRange.Start,
|
||||||
|
_isMouseScrolling ? maxRange.End : _visibleRange.End,
|
||||||
|
_playingLineIndex,
|
||||||
|
sender.Size.Height,
|
||||||
|
_canvasTargetScrollOffset,
|
||||||
|
lyricsStyle.PlayingLineTopOffset / 100.0,
|
||||||
|
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings,
|
||||||
|
_canvasYScrollTransition,
|
||||||
|
albumArtThemeColors.BgFontColor,
|
||||||
|
albumArtThemeColors.FgFontColor,
|
||||||
|
elapsedTime,
|
||||||
|
_isMouseScrolling,
|
||||||
|
_isLayoutChanged,
|
||||||
|
isPlayingLineChanged,
|
||||||
|
_isMouseScrollingChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
_isMouseScrollingChanged = false;
|
||||||
|
|
||||||
|
_lyricsRenderer.CalculateLyrics3DMatrix(
|
||||||
|
lyricsEffect: lyricsEffect,
|
||||||
|
lyricsX: _renderLyricsStartX,
|
||||||
|
lyricsY: _renderLyricsStartY,
|
||||||
|
lyricsWidth: _renderLyricsWidth,
|
||||||
|
canvasHeight: sender.Size.Height
|
||||||
|
);
|
||||||
|
|
||||||
|
_isLayoutChanged = false;
|
||||||
|
|
||||||
|
if (_fluidRenderer.IsEnabled)
|
||||||
|
{
|
||||||
|
_fluidRenderer.UpdateColors(
|
||||||
|
_accentColor1Transition.Value,
|
||||||
|
_accentColor2Transition.Value,
|
||||||
|
_accentColor3Transition.Value,
|
||||||
|
_accentColor4Transition.Value
|
||||||
|
);
|
||||||
|
_fluidRenderer.Update(elapsedTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
_snowRenderer.IsEnabled = lyricsBg.IsSnowFlakeOverlayEnabled;
|
||||||
|
_snowRenderer.Amount = lyricsBg.SnowFlakeOverlayAmount / 100f;
|
||||||
|
_snowRenderer.Speed = lyricsBg.SnowFlakeOverlaySpeed;
|
||||||
|
_snowRenderer.Update(elapsedTime.TotalSeconds);
|
||||||
|
|
||||||
|
_fogRenderer.IsEnabled = lyricsBg.IsFogOverlayEnabled;
|
||||||
|
_fogRenderer.Update(elapsedTime.TotalSeconds);
|
||||||
|
|
||||||
|
if (lyricsBg.IsSpectrumOverlayEnabled && !_spectrumAnalyzer.IsCapturing)
|
||||||
|
{
|
||||||
|
_spectrumAnalyzer.BarCount = lyricsBg.SpectrumCount;
|
||||||
|
_spectrumAnalyzer.StartCapture();
|
||||||
|
}
|
||||||
|
else if (!lyricsBg.IsSpectrumOverlayEnabled && _spectrumAnalyzer.IsCapturing)
|
||||||
|
{
|
||||||
|
_spectrumAnalyzer.StopCapture();
|
||||||
|
}
|
||||||
|
if (_spectrumAnalyzer.IsCapturing)
|
||||||
|
{
|
||||||
|
_spectrumAnalyzer.UpdateSmoothSpectrum();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Canvas_Unloaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Canvas.RemoveFromVisualTree();
|
||||||
|
Canvas = null;
|
||||||
|
|
||||||
|
_fluidRenderer.Dispose();
|
||||||
|
_snowRenderer.Dispose();
|
||||||
|
_fogRenderer.Dispose();
|
||||||
|
_spectrumRenderer.Dispose();
|
||||||
|
DisposeAnalyzer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Canvas_CreateResources(CanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
|
||||||
|
{
|
||||||
|
args.TrackAsyncAction(_fluidRenderer.LoadResourcesAsync().AsAsyncAction());
|
||||||
|
_snowRenderer.LoadResources();
|
||||||
|
_fogRenderer.LoadResources();
|
||||||
|
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
TriggerRelayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
|
||||||
|
private void DisposeAnalyzer()
|
||||||
|
{
|
||||||
|
if (_spectrumAnalyzer.IsCapturing)
|
||||||
|
{
|
||||||
|
_spectrumAnalyzer.StopCapture();
|
||||||
|
}
|
||||||
|
_spectrumAnalyzer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerRelayout()
|
||||||
|
{
|
||||||
|
if (_renderLyricsLines == null || !_isLayoutChanged) return;
|
||||||
|
|
||||||
|
LyricsLayoutManager.MeasureAndArrange(
|
||||||
|
resourceCreator: Canvas,
|
||||||
|
lines: _renderLyricsLines,
|
||||||
|
status: _liveStatesService.LiveStates.LyricsWindowStatus,
|
||||||
|
appSettings: _settingsService.AppSettings,
|
||||||
|
canvasWidth: Canvas.Size.Width,
|
||||||
|
canvasHeight: Canvas.Size.Height,
|
||||||
|
lyricsWidth: _renderLyricsWidth,
|
||||||
|
lyricsHeight: _renderLyricsHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePlaybackState(TimeSpan elapsedTime)
|
||||||
|
{
|
||||||
|
if (_mediaSessionsService.CurrentIsPlaying)
|
||||||
|
{
|
||||||
|
_songPosition += elapsedTime;
|
||||||
|
_totalPlayedTime += elapsedTime;
|
||||||
|
_songPositionWithOffset = _songPosition + TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0);
|
||||||
|
CheckAndScrobbleLastFM();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckAndScrobbleLastFM()
|
||||||
|
{
|
||||||
|
bool isEnabled = _mediaSessionsService.CurrentMediaSourceProviderInfo?.IsLastFMTrackEnabled ?? false;
|
||||||
|
if (!isEnabled || _isLastFMTracked) return;
|
||||||
|
|
||||||
|
var songInfo = _mediaSessionsService.CurrentSongInfo;
|
||||||
|
if (songInfo == null || songInfo.Duration <= 0) return;
|
||||||
|
|
||||||
|
if (_totalPlayedTime.TotalSeconds >= songInfo.Duration * 0.5)
|
||||||
|
{
|
||||||
|
_isLastFMTracked = true;
|
||||||
|
_lastFMService.TrackAsync(songInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetPlaybackState()
|
||||||
|
{
|
||||||
|
_songPosition = TimeSpan.Zero;
|
||||||
|
_totalPlayedTime = TimeSpan.Zero;
|
||||||
|
_isLastFMTracked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<AlbumArtThemeColors> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is IMediaSessionsService)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(IMediaSessionsService.AlbumArtThemeColors))
|
||||||
|
{
|
||||||
|
var lyricsThemeColors = message.NewValue;
|
||||||
|
_immersiveBgColorTransition.StartTransition(lyricsThemeColors.EnvColor);
|
||||||
|
_accentColor1Transition.StartTransition(lyricsThemeColors.AccentColor1);
|
||||||
|
_accentColor2Transition.StartTransition(lyricsThemeColors.AccentColor2);
|
||||||
|
_accentColor3Transition.StartTransition(lyricsThemeColors.AccentColor3);
|
||||||
|
_accentColor4Transition.StartTransition(lyricsThemeColors.AccentColor4);
|
||||||
|
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<TimeSpan> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is IMediaSessionsService)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(IMediaSessionsService.CurrentPosition))
|
||||||
|
{
|
||||||
|
var realPosition = message.NewValue;
|
||||||
|
|
||||||
|
var diff = Math.Abs(_songPosition.TotalMilliseconds - realPosition.TotalMilliseconds);
|
||||||
|
var timelineSyncThreshold = _mediaSessionsService.CurrentMediaSourceProviderInfo?.TimelineSyncThreshold ?? 0;
|
||||||
|
|
||||||
|
// ƫ<><C6AB> or seek
|
||||||
|
if (diff >= timelineSyncThreshold)
|
||||||
|
{
|
||||||
|
_songPosition = realPosition;
|
||||||
|
|
||||||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˿<EFBFBD>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD> LastFM ͳ<><CDB3>״̬
|
||||||
|
if (_songPosition.TotalSeconds <= 1)
|
||||||
|
{
|
||||||
|
_totalPlayedTime = TimeSpan.Zero;
|
||||||
|
_isLastFMTracked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <20>϶<EFBFBD><CFB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȴ<EFBFBD><C8B4><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
if (diff >= timelineSyncThreshold + 5000)
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<LyricsData?> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is IMediaSessionsService)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(IMediaSessionsService.CurrentLyricsData))
|
||||||
|
{
|
||||||
|
_renderLyricsLines = null;
|
||||||
|
if (_mediaSessionsService.CurrentLyricsData is LyricsData lyricsData)
|
||||||
|
{
|
||||||
|
_renderLyricsLines = lyricsData.LyricsLines.Select(x => new RenderLyricsLine()
|
||||||
|
{
|
||||||
|
LyricsSyllables = x.LyricsSyllables,
|
||||||
|
StartMs = x.StartMs,
|
||||||
|
EndMs = x.EndMs,
|
||||||
|
PhoneticText = x.PhoneticText,
|
||||||
|
OriginalText = x.OriginalText,
|
||||||
|
TranslatedText = x.TranslatedText
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<LyricsWindowStatus> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is LiveStates)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(LiveStates.LyricsWindowStatus))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<int> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is LyricsStyleSettings)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(LyricsStyleSettings.PhoneticLyricsFontSize))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsStyleSettings.OriginalLyricsFontSize))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsStyleSettings.TranslatedLyricsFontSize))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontStrokeWidth))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsStyleSettings.PlayingLineTopOffset))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message.Sender is LyricsEffectSettings)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollDuration))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollTopDuration))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollBottomDuration))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollTopDelay))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollBottomDelay))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsEffectSettings.FanLyricsAngle))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<double> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is LyricsStyleSettings)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsLineSpacingFactor))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<bool> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is LyricsEffectSettings)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(LyricsEffectSettings.IsFanLyricsEnabled))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsEffectSettings.IsLyricsBlurEffectEnabled))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message.Sender is LyricsStyleSettings)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(LyricsStyleSettings.IsDynamicLyricsFontSize))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<TextAlignmentType> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is LyricsStyleSettings)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsAlignmentType))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<SongInfo?> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is IMediaSessionsService)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
|
||||||
|
{
|
||||||
|
ResetPlaybackState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<LyricsFontWeight> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is LyricsStyleSettings)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontWeight))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<string> message)
|
||||||
|
{
|
||||||
|
if (message.Sender is LyricsStyleSettings)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCJKFontFamily))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsWesternFontFamily))
|
||||||
|
{
|
||||||
|
_isLayoutChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,147 +18,30 @@
|
|||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
<!-- Effect -->
|
<!-- Effect -->
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Uid="SettingsPageLyricsEffect"
|
x:Uid="SettingsPageLyricsEffect"
|
||||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||||
Text="Effect" />
|
Text="Effect" />
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsVerticalEdgeOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<!-- 模糊效果 -->
|
||||||
<local:ExtendedSlider
|
<dev:SettingsCard x:Uid="SettingsPageLyricsBlurEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
x:Uid="SettingsPageLyricsVerticalEdgeOpacitySlider"
|
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsBlurEffectEnabled, Mode=TwoWay}" />
|
||||||
Default="0"
|
|
||||||
Maximum="100"
|
|
||||||
Minimum="0"
|
|
||||||
Unit="%"
|
|
||||||
Value="{x:Bind LyricsEffectSettings.LyricsVerticalEdgeOpacity, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsBlurAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<local:ExtendedSlider
|
|
||||||
x:Uid="SettingsPageLyricsBlurAmountExtendedSlider"
|
|
||||||
Default="5"
|
|
||||||
Maximum="10"
|
|
||||||
Minimum="0"
|
|
||||||
Value="{x:Bind LyricsEffectSettings.LyricsBlurAmount, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsLineFade" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsLineFadeEnabled, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<!-- 高亮 -->
|
|
||||||
<dev:SettingsExpander x:Uid="SettingsPageLyricsHighlight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<dev:SettingsExpander.Items>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPagePhoneticText">
|
|
||||||
<local:ExtendedSlider
|
|
||||||
Default="60"
|
|
||||||
Frequency="5"
|
|
||||||
Maximum="100"
|
|
||||||
Minimum="0"
|
|
||||||
Unit="%"
|
|
||||||
Value="{x:Bind LyricsEffectSettings.PhoneticLyricsHighlightAmount, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsHighlightScope">
|
|
||||||
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.OriginalLyricsHighlightScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
|
|
||||||
</ComboBox>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageOriginalText">
|
|
||||||
<local:ExtendedSlider
|
|
||||||
Default="60"
|
|
||||||
Frequency="5"
|
|
||||||
Maximum="100"
|
|
||||||
Minimum="0"
|
|
||||||
Unit="%"
|
|
||||||
Value="{x:Bind LyricsEffectSettings.OriginalLyricsHighlightAmount, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageTranslatedText">
|
|
||||||
<local:ExtendedSlider
|
|
||||||
Default="60"
|
|
||||||
Frequency="5"
|
|
||||||
Maximum="100"
|
|
||||||
Minimum="0"
|
|
||||||
Unit="%"
|
|
||||||
Value="{x:Bind LyricsEffectSettings.TranslatedLyricsHighlightAmount, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
<!-- 阴影 -->
|
|
||||||
<dev:SettingsExpander
|
|
||||||
x:Uid="SettingsPageLyricsShadow"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=OneWay}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=TwoWay}" />
|
|
||||||
<dev:SettingsExpander.Items>
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageScope" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=OneWay}">
|
|
||||||
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsShadowScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
|
|
||||||
</ComboBox>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsShadowEnabled, Mode=OneWay}">
|
|
||||||
<local:ExtendedSlider
|
|
||||||
Default="8"
|
|
||||||
Maximum="20"
|
|
||||||
Minimum="1"
|
|
||||||
Value="{x:Bind LyricsEffectSettings.LyricsShadowAmount, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
<!-- 辉光效果 -->
|
<!-- 辉光效果 -->
|
||||||
<dev:SettingsExpander
|
<dev:SettingsCard x:Uid="SettingsPageLyricsGlowEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
x:Uid="SettingsPageLyricsGlowEffect"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
||||||
<dev:SettingsExpander.Items>
|
</dev:SettingsCard>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageScope" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
|
||||||
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsGlowEffectScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
<!-- 缩放效果 -->
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
|
<dev:SettingsCard x:Uid="SettingsPageLyricsScaleEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
|
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsScaleEffectEnabled, Mode=TwoWay}" />
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
|
</dev:SettingsCard>
|
||||||
</ComboBox>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
|
||||||
<local:ExtendedSlider
|
|
||||||
Default="8"
|
|
||||||
Maximum="20"
|
|
||||||
Minimum="1"
|
|
||||||
Value="{x:Bind LyricsEffectSettings.LyricsGlowEffectAmount, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
<!-- 浮动动画 -->
|
<!-- 浮动动画 -->
|
||||||
<dev:SettingsExpander
|
<dev:SettingsCard x:Uid="SettingsPageLyricsFloatAnimation" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
x:Uid="SettingsPageLyricsFloatAnimation"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="True">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationEnabled, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationEnabled, Mode=TwoWay}" />
|
||||||
<dev:SettingsExpander.Items>
|
</dev:SettingsCard>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
|
||||||
<local:ExtendedSlider
|
|
||||||
Default="1"
|
|
||||||
Maximum="4"
|
|
||||||
Minimum="1"
|
|
||||||
Value="{x:Bind LyricsEffectSettings.LyricsFloatAmount, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
<!-- 扇形歌词 -->
|
<!-- 扇形歌词 -->
|
||||||
<dev:SettingsExpander
|
<dev:SettingsExpander
|
||||||
@@ -224,7 +107,7 @@
|
|||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
<!-- 滚动动画 -->
|
<!-- 歌词动画 -->
|
||||||
<dev:SettingsExpander x:Uid="SettingsPageScrollEasing" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsExpander x:Uid="SettingsPageScrollEasing" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsScrollEasingType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsScrollEasingType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeLinear" />
|
<ComboBoxItem x:Uid="SettingsPageEasingTypeLinear" />
|
||||||
@@ -246,7 +129,7 @@
|
|||||||
Default="500"
|
Default="500"
|
||||||
Frequency="50"
|
Frequency="50"
|
||||||
Maximum="1000"
|
Maximum="1000"
|
||||||
Minimum="50"
|
Minimum="0"
|
||||||
Unit="ms"
|
Unit="ms"
|
||||||
Value="{x:Bind LyricsEffectSettings.LyricsScrollTopDuration, Mode=TwoWay}" />
|
Value="{x:Bind LyricsEffectSettings.LyricsScrollTopDuration, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
@@ -255,7 +138,7 @@
|
|||||||
Default="500"
|
Default="500"
|
||||||
Frequency="50"
|
Frequency="50"
|
||||||
Maximum="1000"
|
Maximum="1000"
|
||||||
Minimum="50"
|
Minimum="0"
|
||||||
Unit="ms"
|
Unit="ms"
|
||||||
Value="{x:Bind LyricsEffectSettings.LyricsScrollDuration, Mode=TwoWay}" />
|
Value="{x:Bind LyricsEffectSettings.LyricsScrollDuration, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
@@ -264,7 +147,7 @@
|
|||||||
Default="500"
|
Default="500"
|
||||||
Frequency="50"
|
Frequency="50"
|
||||||
Maximum="1000"
|
Maximum="1000"
|
||||||
Minimum="50"
|
Minimum="0"
|
||||||
Unit="ms"
|
Unit="ms"
|
||||||
Value="{x:Bind LyricsEffectSettings.LyricsScrollBottomDuration, Mode=TwoWay}" />
|
Value="{x:Bind LyricsEffectSettings.LyricsScrollBottomDuration, Mode=TwoWay}" />
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<Grid Grid.Column="0">
|
<Grid Grid.Column="0">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlSongInfoMapping" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="LyricsSearchControlSongInfoMapping" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
@@ -34,22 +35,32 @@
|
|||||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
CornerRadius="4">
|
CornerRadius="4">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<TextBlock x:Uid="LyricsSearchControlTitle" />
|
|
||||||
<TextBlock
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
|
||||||
IsTextSelectionEnabled="True"
|
|
||||||
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalTitle, Mode=OneWay}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
|
<local:PropertyRow x:Uid="LyricsSearchControlTitle" Value="{x:Bind ViewModel.MappedSongSearchQuery.OriginalTitle, Mode=OneWay}" />
|
||||||
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedTitle, Mode=TwoWay}" TextWrapping="Wrap" />
|
|
||||||
<Button
|
<Grid ColumnSpacing="6">
|
||||||
VerticalAlignment="Center"
|
<Grid.ColumnDefinitions>
|
||||||
Command="{x:Bind ViewModel.ResetMappedTitleCommand}"
|
<ColumnDefinition Width="Auto" />
|
||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
<ColumnDefinition Width="*" />
|
||||||
FontSize=12,
|
<ColumnDefinition Width="Auto" />
|
||||||
Glyph=}"
|
</Grid.ColumnDefinitions>
|
||||||
Style="{StaticResource GhostButtonStyle}" />
|
<TextBlock
|
||||||
|
x:Uid="LyricsSearchControlMappedAs"
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBox
|
||||||
|
Grid.Column="1"
|
||||||
|
Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedTitle, Mode=TwoWay}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{x:Bind ViewModel.ResetMappedTitleCommand}"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
FontSize=12,
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource GhostButtonStyle}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -60,33 +71,52 @@
|
|||||||
CornerRadius="4">
|
CornerRadius="4">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlArtist" />
|
<local:PropertyRow x:Uid="LyricsSearchControlArtist" Value="{x:Bind ViewModel.MappedSongSearchQuery.OriginalArtist, Mode=OneWay}" />
|
||||||
<TextBlock
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
|
||||||
IsTextSelectionEnabled="True"
|
|
||||||
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalArtist, Mode=OneWay}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
|
<Grid ColumnSpacing="6">
|
||||||
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedArtist, Mode=TwoWay}" TextWrapping="Wrap" />
|
<Grid.ColumnDefinitions>
|
||||||
<RichTextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" TextWrapping="Wrap">
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="LyricsSearchControlMappedAs"
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBox
|
||||||
|
Grid.Column="1"
|
||||||
|
Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedArtist, Mode=TwoWay}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{x:Bind ViewModel.ResetMappedArtistCommand}"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
FontSize=12,
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource GhostButtonStyle}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<RichTextBlock
|
||||||
|
FontSize="12"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
TextWrapping="Wrap">
|
||||||
<Paragraph>
|
<Paragraph>
|
||||||
|
<Run Text="*" />
|
||||||
<Run x:Uid="ArtistsSplitHint" />
|
<Run x:Uid="ArtistsSplitHint" />
|
||||||
<Run Text=";" />
|
|
||||||
<Run Text="," />
|
|
||||||
<Run Text="/" />
|
|
||||||
<Run Text=";" />
|
|
||||||
<Run Text="、" />
|
|
||||||
<Run Text="," />
|
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
</RichTextBlock>
|
</RichTextBlock>
|
||||||
<Button
|
|
||||||
VerticalAlignment="Center"
|
<RichTextBlock
|
||||||
Command="{x:Bind ViewModel.ResetMappedArtistCommand}"
|
FontSize="12"
|
||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
FontWeight="Bold"
|
||||||
FontSize=12,
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
Glyph=}"
|
Loaded="ArtistsSplitHintRichTextBlock_Loaded"
|
||||||
Style="{StaticResource GhostButtonStyle}" />
|
TextWrapping="Wrap">
|
||||||
|
<Paragraph>
|
||||||
|
<Run Text="; , / ; 、 ," />
|
||||||
|
</Paragraph>
|
||||||
|
</RichTextBlock>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -97,22 +127,32 @@
|
|||||||
CornerRadius="4">
|
CornerRadius="4">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlAlbum" />
|
<local:PropertyRow x:Uid="LyricsSearchControlAlbum" Value="{x:Bind ViewModel.MappedSongSearchQuery.OriginalAlbum, Mode=OneWay}" />
|
||||||
<TextBlock
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
|
||||||
IsTextSelectionEnabled="True"
|
|
||||||
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalAlbum, Mode=OneWay}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
|
<Grid ColumnSpacing="6">
|
||||||
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedAlbum, Mode=TwoWay}" TextWrapping="Wrap" />
|
<Grid.ColumnDefinitions>
|
||||||
<Button
|
<ColumnDefinition Width="Auto" />
|
||||||
VerticalAlignment="Center"
|
<ColumnDefinition Width="*" />
|
||||||
Command="{x:Bind ViewModel.ResetMappedAlbumCommand}"
|
<ColumnDefinition Width="Auto" />
|
||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
</Grid.ColumnDefinitions>
|
||||||
FontSize=12,
|
|
||||||
Glyph=}"
|
<TextBlock
|
||||||
Style="{StaticResource GhostButtonStyle}" />
|
x:Uid="LyricsSearchControlMappedAs"
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBox
|
||||||
|
Grid.Column="1"
|
||||||
|
Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedAlbum, Mode=TwoWay}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{x:Bind ViewModel.ResetMappedAlbumCommand}"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
FontSize=12,
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource GhostButtonStyle}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -136,66 +176,29 @@
|
|||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="models:LyricsSearchResult">
|
<DataTemplate x:DataType="models:LyricsSearchResult">
|
||||||
<ListViewItem IsEnabled="{x:Bind IsFound}">
|
<ListViewItem IsEnabled="{x:Bind IsFound}">
|
||||||
<StackPanel Padding="3,6" Opacity="{x:Bind IsFound, Converter={StaticResource BoolToPartialOpacityConverter}}">
|
<StackPanel Padding="0,6" Opacity="{x:Bind IsFound, Converter={StaticResource BoolToPartialOpacityConverter}}">
|
||||||
<HyperlinkButton
|
<local:PropertyRow
|
||||||
Padding="0"
|
Margin="-8,0,0,0"
|
||||||
Content="{x:Bind Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
|
Link="{x:Bind Reference, Mode=OneWay}"
|
||||||
NavigateUri="{x:Bind Reference, FallbackValue=about:blank}" />
|
ToolTipService.ToolTip="{x:Bind Reference, TargetNullValue=N/A, Mode=OneWay}"
|
||||||
<!-- Title -->
|
Value="{x:Bind Provider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||||
<Grid ColumnSpacing="12" Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
<!-- Lyrics search result -->
|
||||||
<Grid.ColumnDefinitions>
|
<StackPanel Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||||
<ColumnDefinition Width="Auto" />
|
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<ColumnDefinition Width="*" />
|
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
</Grid.ColumnDefinitions>
|
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<TextBlock x:Uid="LyricsSearchControlTitle" Grid.Column="0" />
|
<local:PropertyRow
|
||||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
x:Uid="LyricsSearchControlDurauion"
|
||||||
<Paragraph>
|
Unit="s"
|
||||||
<Run Text="{x:Bind Title, TargetNullValue=N/A, Mode=OneWay}" />
|
Value="{x:Bind Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
</Paragraph>
|
<local:PropertyRow
|
||||||
</RichTextBlock>
|
x:Uid="LyricsPageMatchPercentage"
|
||||||
</Grid>
|
Unit="%"
|
||||||
<!-- Artist -->
|
Value="{x:Bind MatchPercentage, Mode=OneWay}" />
|
||||||
<Grid ColumnSpacing="12" Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
<local:PropertyRow
|
||||||
<Grid.ColumnDefinitions>
|
x:Uid="LyricsPageCachePath"
|
||||||
<ColumnDefinition Width="Auto" />
|
Link="{x:Bind SelfPath, TargetNullValue=N/A, Mode=OneWay}"
|
||||||
<ColumnDefinition Width="*" />
|
ToolTipService.ToolTip="{x:Bind SelfPath, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlArtist" Grid.Column="0" />
|
|
||||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="{x:Bind DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
</Paragraph>
|
|
||||||
</RichTextBlock>
|
|
||||||
</Grid>
|
|
||||||
<!-- Album -->
|
|
||||||
<Grid ColumnSpacing="12" Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlAlbum" Grid.Column="0" />
|
|
||||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="{x:Bind Album, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
</Paragraph>
|
|
||||||
</RichTextBlock>
|
|
||||||
</Grid>
|
|
||||||
<!-- Duration -->
|
|
||||||
<StackPanel
|
|
||||||
Orientation="Horizontal"
|
|
||||||
Spacing="6"
|
|
||||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlDurauion" />
|
|
||||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind Duration}" />
|
|
||||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="s" />
|
|
||||||
</StackPanel>
|
|
||||||
<!-- Match percentage -->
|
|
||||||
<StackPanel
|
|
||||||
Orientation="Horizontal"
|
|
||||||
Spacing="6"
|
|
||||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<TextBlock x:Uid="LyricsPageMatchPercentage" />
|
|
||||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind MatchPercentage}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!-- NOT FOUND -->
|
<!-- NOT FOUND -->
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -266,23 +269,30 @@
|
|||||||
</interactivity:DataTriggerBehavior>
|
</interactivity:DataTriggerBehavior>
|
||||||
</interactivity:Interaction.Behaviors>
|
</interactivity:Interaction.Behaviors>
|
||||||
<Pivot.HeaderTemplate>
|
<Pivot.HeaderTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="models:LyricsData">
|
||||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{Binding LanguageCode, Mode=OneWay, Converter={StaticResource LanguageCodeToDisplayedNameConverter}}" />
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Bind LanguageCode, Mode=OneWay, Converter={StaticResource LanguageCodeToDisplayedNameConverter}}" />
|
||||||
|
<InfoBadge
|
||||||
|
x:Uid="LyricsSearchControlAutoGenerated"
|
||||||
|
Margin="6,0,0,0"
|
||||||
|
Style="{StaticResource StringInfoBadgeStyle}"
|
||||||
|
Visibility="{x:Bind AutoGenerated, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</Pivot.HeaderTemplate>
|
</Pivot.HeaderTemplate>
|
||||||
<Pivot.ItemTemplate>
|
<Pivot.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="models:LyricsData">
|
||||||
<ListView ItemsSource="{Binding LyricsLines, Mode=OneWay}" SelectionChanged="ListView_SelectionChanged">
|
<ListView ItemsSource="{x:Bind LyricsLines, Mode=OneWay}" SelectionChanged="ListView_SelectionChanged">
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="models:LyricsLine">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{Binding StartMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind StartMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="1,0"
|
Margin="1,0"
|
||||||
Foreground="{ThemeResource SystemFillColorNeutralBrush}"
|
Foreground="{ThemeResource SystemFillColorNeutralBrush}"
|
||||||
Text="-" />
|
Text="-" />
|
||||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{Binding EndMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind EndMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||||
<TextBlock Margin="6,0" Text="{Binding OriginalText}" />
|
<TextBlock Margin="6,0" Text="{x:Bind OriginalText, Mode=OneWay}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
@@ -313,31 +323,29 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Grid.Row="1">
|
<Grid Grid.Row="1" ColumnSpacing="6">
|
||||||
<RelativePanel>
|
<Grid.ColumnDefinitions>
|
||||||
<TextBlock
|
<ColumnDefinition Width="*" />
|
||||||
x:Uid="LyricsSearchControlHelp"
|
<ColumnDefinition Width="Auto" />
|
||||||
Margin="0,0,24,0"
|
<ColumnDefinition Width="Auto" />
|
||||||
FontSize="12"
|
<ColumnDefinition Width="Auto" />
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
</Grid.ColumnDefinitions>
|
||||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
<TextBlock
|
||||||
RelativePanel.LeftOf="Reset"
|
x:Uid="LyricsSearchControlHelp"
|
||||||
TextWrapping="Wrap" />
|
Grid.Column="1"
|
||||||
<Button
|
VerticalAlignment="Center"
|
||||||
x:Name="Reset"
|
FontSize="12"
|
||||||
x:Uid="LyricsSearchControlReset"
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
Margin="0,0,6,0"
|
TextWrapping="Wrap" />
|
||||||
Command="{x:Bind ViewModel.ResetCommand}"
|
<Button
|
||||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
x:Uid="LyricsSearchControlReset"
|
||||||
RelativePanel.LeftOf="SaveChanges" />
|
Grid.Column="2"
|
||||||
<Button
|
Command="{x:Bind ViewModel.ResetCommand}" />
|
||||||
x:Name="SaveChanges"
|
<Button
|
||||||
x:Uid="LyricsSearchControlSaveChanges"
|
x:Uid="LyricsSearchControlSaveChanges"
|
||||||
Command="{x:Bind ViewModel.SaveCommand}"
|
Grid.Column="3"
|
||||||
RelativePanel.AlignRightWithPanel="True"
|
Command="{x:Bind ViewModel.SaveCommand}"
|
||||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
Style="{StaticResource AccentButtonStyle}" />
|
||||||
Style="{StaticResource AccentButtonStyle}" />
|
|
||||||
</RelativePanel>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ using BetterLyrics.WinUI3.Models;
|
|||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Documents;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -22,5 +24,26 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
{
|
{
|
||||||
ViewModel.SelectedLyricsLine = e.OriginalSource as LyricsLine;
|
ViewModel.SelectedLyricsLine = e.OriginalSource as LyricsLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ArtistsSplitHintRichTextBlock_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is RichTextBlock richTextBlock)
|
||||||
|
{
|
||||||
|
TextHighlighter highlighter = new()
|
||||||
|
{
|
||||||
|
Background = App.Current.Resources["AccentTextFillColorPrimaryBrush"] as SolidColorBrush,
|
||||||
|
Ranges =
|
||||||
|
{
|
||||||
|
new() { StartIndex = 0, Length = 1 },
|
||||||
|
new() { StartIndex = 5, Length = 1 },
|
||||||
|
new() { StartIndex = 10, Length = 1 },
|
||||||
|
new() { StartIndex = 15, Length = 1 },
|
||||||
|
new() { StartIndex = 20, Length = 1 },
|
||||||
|
new() { StartIndex = 25, Length = 1 },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
richTextBlock.TextHighlighters.Add(highlighter);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,32 @@
|
|||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsCard x:Uid="SettingsPageLyricsAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsLeft" />
|
<ComboBoxItem x:Uid="SettingsPageLeft" />
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsCenter" />
|
<ComboBoxItem x:Uid="SettingsPageCenter" />
|
||||||
<ComboBoxItem x:Uid="SettingsPageLyricsRight" />
|
<ComboBoxItem x:Uid="SettingsPageRight" />
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageLyricsCenterTopOffset" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<local:ExtendedSlider
|
||||||
|
Default="50"
|
||||||
|
Maximum="100"
|
||||||
|
Minimum="0"
|
||||||
|
Unit="%"
|
||||||
|
Value="{x:Bind LyricsStyleSettings.PlayingLineTopOffset, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<local:ExtendedSlider
|
||||||
|
x:Uid="SettingsPageLyricsLineSpacingFactorSlider"
|
||||||
|
Default="0.5"
|
||||||
|
Frequency="0.1"
|
||||||
|
Maximum="2"
|
||||||
|
Minimum="0"
|
||||||
|
Unit="x"
|
||||||
|
Value="{x:Bind LyricsStyleSettings.LyricsLineSpacingFactor, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<dev:SettingsExpander x:Uid="SettingsPageLyricsFontFamily" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsExpander x:Uid="SettingsPageLyricsFontFamily" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<dev:SettingsExpander.Items>
|
<dev:SettingsExpander.Items>
|
||||||
<dev:SettingsCard x:Uid="SettingsPageCJK">
|
<dev:SettingsCard x:Uid="SettingsPageCJK">
|
||||||
@@ -56,15 +76,6 @@
|
|||||||
</ComboBox>
|
</ComboBox>
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsBgFontOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<local:ExtendedSlider
|
|
||||||
Default="30"
|
|
||||||
Maximum="100"
|
|
||||||
Minimum="0"
|
|
||||||
Unit="%"
|
|
||||||
Value="{x:Bind LyricsStyleSettings.LyricsBgFontOpacity, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsFontStrokeWidth" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsCard x:Uid="SettingsPageLyricsFontStrokeWidth" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
<local:ExtendedSlider
|
<local:ExtendedSlider
|
||||||
Default="0"
|
Default="0"
|
||||||
@@ -216,24 +227,6 @@
|
|||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<local:ExtendedSlider
|
|
||||||
x:Uid="SettingsPageLyricsLineSpacingFactorSlider"
|
|
||||||
Default="0.5"
|
|
||||||
Frequency="0.1"
|
|
||||||
Maximum="2"
|
|
||||||
Minimum="0"
|
|
||||||
Unit="x"
|
|
||||||
Value="{x:Bind LyricsStyleSettings.LyricsLineSpacingFactor, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageLyricsTranslationSeparator" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
|
||||||
<TextBox AcceptsReturn="True" Text="{x:Bind LyricsStyleSettings.LyricsTranslationSeparator, Mode=TwoWay}" />
|
|
||||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}" />
|
|
||||||
</StackPanel>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|||||||
@@ -13,21 +13,32 @@
|
|||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid ColumnSpacing="6">
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
<Grid x:Name="DisplayGrid" SizeChanged="DisplayGrid_SizeChanged">
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Grid Grid.Column="0">
|
|
||||||
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
||||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||||
|
|
||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
<TextBlock
|
<Grid>
|
||||||
x:Uid="SettingsPageRecordedWindowStatus"
|
<Grid.ColumnDefinitions>
|
||||||
RelativePanel.AlignLeftWithPanel="True"
|
<ColumnDefinition Width="Auto" />
|
||||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageRecordedWindowStatus"
|
||||||
|
Grid.Column="0"
|
||||||
|
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="0,30,0,0"
|
||||||
|
Command="{x:Bind ViewModel.OpenConfigPanelCommand}"
|
||||||
|
Style="{StaticResource AccentButtonStyle}">
|
||||||
|
<TextBlock x:Uid="LyricsWindowSettingsControlCurrentLyricsWindowConfig" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="3">
|
<StackPanel Orientation="Horizontal" Spacing="3">
|
||||||
|
|
||||||
@@ -60,7 +71,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<!-- Sharing hub -->
|
<!-- Sharing hub -->
|
||||||
<HyperlinkButton x:Uid="SettingsPageShareHub" NavigateUri="{x:Bind constants:Link.ShareHubUrl}" />
|
<HyperlinkButton x:Uid="SettingsPageShareHub" NavigateUri="{x:Bind constants:Link.ShareHub}" />
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
@@ -114,16 +125,44 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Grid.Column="1">
|
|
||||||
|
<Grid
|
||||||
|
x:Name="ConfigGrid"
|
||||||
|
Background="{ThemeResource AcrylicBackgroundFillColorDefaultBrush}"
|
||||||
|
Opacity="{x:Bind ViewModel.IsConfigPanelOpened, Mode=OneWay, Converter={StaticResource BoolToOpacityConverter}}"
|
||||||
|
Translation="{x:Bind ViewModel.ConfigPanelTranslation, Mode=OneWay}">
|
||||||
|
<Grid.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</Grid.OpacityTransition>
|
||||||
|
<Grid.TranslationTransition>
|
||||||
|
<Vector3Transition />
|
||||||
|
</Grid.TranslationTransition>
|
||||||
|
|
||||||
<Grid Padding="36,0" Style="{StaticResource SettingsGridStyle}">
|
<Grid Padding="36,0" Style="{StaticResource SettingsGridStyle}">
|
||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
<Grid>
|
||||||
<TextBlock x:Uid="LyricsWindowSettingsControlCurrentLyricsWindowConfig" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="LyricsWindowSettingsControlCurrentLyricsWindowConfig"
|
||||||
|
Grid.Column="0"
|
||||||
|
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="0,30,0,0"
|
||||||
|
Command="{x:Bind ViewModel.CloseConfigPanelCommand}"
|
||||||
|
Content="{ui:FontIcon FontSize=16,
|
||||||
|
FontFamily={StaticResource IconFontFamily},
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource AccentButtonStyle}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Pivot SelectionChanged="Pivot_SelectionChanged">
|
<Pivot SelectionChanged="Pivot_SelectionChanged">
|
||||||
|
|
||||||
<PivotItem Tag="General">
|
<PivotItem Tag="Window">
|
||||||
<PivotItem.Header>
|
<PivotItem.Header>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Uid="AppSettingsControlGeneral"
|
x:Uid="AppSettingsControlGeneral"
|
||||||
@@ -132,6 +171,15 @@
|
|||||||
</PivotItem.Header>
|
</PivotItem.Header>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
|
|
||||||
|
<PivotItem Tag="Layout">
|
||||||
|
<PivotItem.Header>
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageLayout"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource BodyTextBlockStyle}" />
|
||||||
|
</PivotItem.Header>
|
||||||
|
</PivotItem>
|
||||||
|
|
||||||
<PivotItem Tag="AlbumArtStyle">
|
<PivotItem Tag="AlbumArtStyle">
|
||||||
<PivotItem.Header>
|
<PivotItem.Header>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -141,6 +189,15 @@
|
|||||||
</PivotItem.Header>
|
</PivotItem.Header>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
|
|
||||||
|
<PivotItem Tag="AlbumArtEffect">
|
||||||
|
<PivotItem.Header>
|
||||||
|
<TextBlock
|
||||||
|
x:Uid="SettingsPageAlbumEffect"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource BodyTextBlockStyle}" />
|
||||||
|
</PivotItem.Header>
|
||||||
|
</PivotItem>
|
||||||
|
|
||||||
<PivotItem Tag="LyricsStyle">
|
<PivotItem Tag="LyricsStyle">
|
||||||
<PivotItem.Header>
|
<PivotItem.Header>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -168,15 +225,6 @@
|
|||||||
</PivotItem.Header>
|
</PivotItem.Header>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
|
|
||||||
<PivotItem Tag="Advanced">
|
|
||||||
<PivotItem.Header>
|
|
||||||
<TextBlock
|
|
||||||
x:Uid="SettingsPageAdvanced"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource BodyTextBlockStyle}" />
|
|
||||||
</PivotItem.Header>
|
|
||||||
</PivotItem>
|
|
||||||
|
|
||||||
</Pivot>
|
</Pivot>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -189,29 +237,20 @@
|
|||||||
</TransitionCollection>
|
</TransitionCollection>
|
||||||
</controls:SwitchPresenter.ContentTransitions>
|
</controls:SwitchPresenter.ContentTransitions>
|
||||||
|
|
||||||
<!-- General -->
|
<!-- Window -->
|
||||||
<controls:Case Value="General">
|
<controls:Case Value="Window">
|
||||||
|
<uc:WindowSettingsControl LyricsWindowStatus="{x:Bind ViewModel.LiveStates.LyricsWindowStatus, Mode=OneWay}" />
|
||||||
|
</controls:Case>
|
||||||
|
|
||||||
|
<!-- Layout -->
|
||||||
|
<controls:Case Value="Layout">
|
||||||
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
||||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="SettingsPageLayout" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
<dev:SettingsExpander x:Uid="SettingsPageDisplayTypeSwitcher" IsExpanded="True">
|
||||||
<StackPanel
|
|
||||||
Margin="0,6,0,0"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
Spacing="6">
|
|
||||||
<TextBox Text="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.Name, Mode=TwoWay}" TextWrapping="Wrap" />
|
|
||||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}" />
|
|
||||||
</StackPanel>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsExpander
|
|
||||||
x:Uid="SettingsPageDisplayTypeSwitcher"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="True">
|
|
||||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
|
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
|
||||||
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
|
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
|
||||||
@@ -227,115 +266,6 @@
|
|||||||
</dev:SettingsExpander.Items>
|
</dev:SettingsExpander.Items>
|
||||||
</dev:SettingsExpander>
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
<dev:SettingsExpander
|
|
||||||
x:Uid="SettingsPageWorkArea"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=TwoWay}" />
|
|
||||||
<dev:SettingsExpander.Items>
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageWorkAreaHeight" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
|
||||||
<uc:ExtendedSlider
|
|
||||||
Default="64"
|
|
||||||
Maximum="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.MonitorBounds.Height, Mode=OneWay}"
|
|
||||||
Minimum="64"
|
|
||||||
Unit="px"
|
|
||||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.DockHeight, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageDockPlacement" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
|
||||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
|
|
||||||
</ComboBox>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageDockMonitor" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
|
||||||
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.MonitorDeviceName, Mode=TwoWay}" />
|
|
||||||
<Button
|
|
||||||
Command="{x:Bind ViewModel.RefreshMonitorDeviceNamesCommand}"
|
|
||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
FontSize=12,
|
|
||||||
Glyph=}"
|
|
||||||
Style="{StaticResource GhostButtonStyle}" />
|
|
||||||
</StackPanel>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
<dev:SettingsExpander
|
|
||||||
x:Uid="SettingsPageAdaptEnvColor"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=TwoWay}" />
|
|
||||||
<dev:SettingsExpander.Items>
|
|
||||||
<dev:SettingsCard
|
|
||||||
x:Uid="SettingsPageEnvColorSample"
|
|
||||||
Header="Environment color sample mode"
|
|
||||||
IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
|
||||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.EnvironmentSampleMode, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleBelow" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleAbove" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleInner" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleEdge" />
|
|
||||||
</ComboBox>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
<dev:SettingsExpander
|
|
||||||
x:Uid="SettingsPageWindowBounds"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="True">
|
|
||||||
<dev:SettingsExpander.Items>
|
|
||||||
<dev:SettingsCard Header="X">
|
|
||||||
<NumberBox
|
|
||||||
SmallChange="10"
|
|
||||||
SpinButtonPlacementMode="Inline"
|
|
||||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowX, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
<dev:SettingsCard Header="Y">
|
|
||||||
<NumberBox
|
|
||||||
SmallChange="10"
|
|
||||||
SpinButtonPlacementMode="Inline"
|
|
||||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowY, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageWidth">
|
|
||||||
<NumberBox
|
|
||||||
SmallChange="10"
|
|
||||||
SpinButtonPlacementMode="Inline"
|
|
||||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowWidth, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageHeight">
|
|
||||||
<NumberBox
|
|
||||||
SmallChange="10"
|
|
||||||
SpinButtonPlacementMode="Inline"
|
|
||||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowHeight, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
<dev:SettingsExpander
|
|
||||||
x:Uid="SettingsPageAOT"
|
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
|
||||||
Glyph=}"
|
|
||||||
IsExpanded="True">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTop, Mode=TwoWay}" />
|
|
||||||
<dev:SettingsExpander.Items>
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageForceAlwaysOnTop" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTop, Mode=OneWay}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTopPolling, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
</dev:SettingsExpander.Items>
|
|
||||||
</dev:SettingsExpander>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
@@ -343,7 +273,12 @@
|
|||||||
|
|
||||||
<!-- Album art area style -->
|
<!-- Album art area style -->
|
||||||
<controls:Case Value="AlbumArtStyle">
|
<controls:Case Value="AlbumArtStyle">
|
||||||
<uc:AlbumArtLayoutSettingsControl AlbumArtLayoutSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings, Mode=OneWay}" />
|
<uc:AlbumArtAreaStyleSettingsControl AlbumArtLayoutSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings, Mode=OneWay}" />
|
||||||
|
</controls:Case>
|
||||||
|
|
||||||
|
<!-- Album art area effect -->
|
||||||
|
<controls:Case Value="AlbumArtEffect">
|
||||||
|
<uc:AlbumArtAreaEffectSettingsControl AlbumArtAreaEffectSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtAreaEffectSettings, Mode=OneWay}" />
|
||||||
</controls:Case>
|
</controls:Case>
|
||||||
|
|
||||||
<!-- Lyrics style -->
|
<!-- Lyrics style -->
|
||||||
@@ -361,39 +296,9 @@
|
|||||||
<uc:LyricsBackgroundSettingsControl LyricsBackgroundSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings, Mode=OneWay}" />
|
<uc:LyricsBackgroundSettingsControl LyricsBackgroundSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings, Mode=OneWay}" />
|
||||||
</controls:Case>
|
</controls:Case>
|
||||||
|
|
||||||
<!-- Advanced -->
|
|
||||||
<controls:Case Value="Advanced">
|
|
||||||
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
|
||||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
|
||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageShowInSwitchers" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsShownInSwitchers, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageClickThrough" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsClickThrough, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageBorderless" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsBorderless, Mode=TwoWay}" />
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageDragArea" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
|
||||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.TitleBarArea, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaNone" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaTop" />
|
|
||||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaWhole" />
|
|
||||||
</ComboBox>
|
|
||||||
</dev:SettingsCard>
|
|
||||||
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</ScrollViewer>
|
|
||||||
</controls:Case>
|
|
||||||
|
|
||||||
</controls:SwitchPresenter>
|
</controls:SwitchPresenter>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using BetterLyrics.WinUI3.Helper;
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
using BetterLyrics.WinUI3.Serialization;
|
using BetterLyrics.WinUI3.Serialization;
|
||||||
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
|
||||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
using BetterLyrics.WinUI3.ViewModels;
|
||||||
using BetterLyrics.WinUI3.Views;
|
using BetterLyrics.WinUI3.Views;
|
||||||
@@ -27,7 +26,6 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
|
|
||||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||||
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
|
||||||
|
|
||||||
public LyricsWindowSettingsControl()
|
public LyricsWindowSettingsControl()
|
||||||
{
|
{
|
||||||
@@ -75,7 +73,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
StorageFile? file;
|
StorageFile? file;
|
||||||
if (this.Parent is FlyoutPresenter)
|
if (this.Parent is FlyoutPresenter)
|
||||||
{
|
{
|
||||||
file = await PickerHelper.PickSaveFileAsync<LyricsWindow>(fileTypeChoices);
|
file = await PickerHelper.PickSaveFileAsync<NowPlayingWindow>(fileTypeChoices);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -87,7 +85,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
clonedData.IsDefault = false;
|
clonedData.IsDefault = false;
|
||||||
var json = System.Text.Json.JsonSerializer.Serialize(clonedData, SourceGenerationContext.Default.LyricsWindowStatus);
|
var json = System.Text.Json.JsonSerializer.Serialize(clonedData, SourceGenerationContext.Default.LyricsWindowStatus);
|
||||||
File.WriteAllText(file.Path, json);
|
File.WriteAllText(file.Path, json);
|
||||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ExportSettingsSuccess"));
|
ToastHelper.ShowToast("ExportSettingsSuccess", null, InfoBarSeverity.Success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +132,7 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
StorageFile? file;
|
StorageFile? file;
|
||||||
if (this.Parent is FlyoutPresenter)
|
if (this.Parent is FlyoutPresenter)
|
||||||
{
|
{
|
||||||
file = await PickerHelper.PickSingleFileAsync<LyricsWindow>(fileTypeFilter);
|
file = await PickerHelper.PickSingleFileAsync<NowPlayingWindow>(fileTypeFilter);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -147,9 +145,14 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
if (data != null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
ViewModel.AppSettings.WindowBoundsRecords.Add(data);
|
ViewModel.AppSettings.WindowBoundsRecords.Add(data);
|
||||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ImportSettingsSuccess"));
|
ToastHelper.ShowToast("ImportSettingsSuccess", null, InfoBarSeverity.Success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DisplayGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
ViewModel.DisplayPanelHeight = e.NewSize.Height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,6 +188,14 @@
|
|||||||
<ComboBoxItem x:Uid="SettingsPageLyricsSearchBestMatch" />
|
<ComboBoxItem x:Uid="SettingsPageLyricsSearchBestMatch" />
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</dev:SettingsCard>
|
</dev:SettingsCard>
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageMatchingThreshold">
|
||||||
|
<local:ExtendedSlider
|
||||||
|
Default="0"
|
||||||
|
Maximum="100"
|
||||||
|
Minimum="0"
|
||||||
|
Unit="%"
|
||||||
|
Value="{x:Bind ViewModel.SelectedMediaSourceProvider.MatchingThreshold, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
x:Name="LyricsSearchProvidersListView"
|
x:Name="LyricsSearchProvidersListView"
|
||||||
@@ -209,12 +217,48 @@
|
|||||||
</ListView.ItemContainerStyle>
|
</ListView.ItemContainerStyle>
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="models:LyricsSearchProviderInfo">
|
<DataTemplate x:DataType="models:LyricsSearchProviderInfo">
|
||||||
<dev:SettingsCard Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}">
|
<Grid>
|
||||||
<dev:SettingsCard.HeaderIcon>
|
<dev:SettingsExpander Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}" IsExpanded="{Binding IsMatchingThresholdOverwritten, Mode=OneWay}">
|
||||||
<FontIcon FontFamily="Segoe UI Symbol" Glyph="⠿" />
|
<dev:SettingsExpander.HeaderIcon>
|
||||||
</dev:SettingsCard.HeaderIcon>
|
<FontIcon FontFamily="Segoe UI Symbol" Glyph="⠿" />
|
||||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
|
</dev:SettingsExpander.HeaderIcon>
|
||||||
</dev:SettingsCard>
|
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
|
||||||
|
<dev:SettingsExpander.Items>
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageOverwriteMatchingThreshold">
|
||||||
|
<ToggleSwitch IsOn="{Binding IsMatchingThresholdOverwritten, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageMatchingThreshold" IsEnabled="{Binding IsMatchingThresholdOverwritten, Mode=OneWay}">
|
||||||
|
<local:ExtendedSlider
|
||||||
|
Default="0"
|
||||||
|
Maximum="100"
|
||||||
|
Minimum="0"
|
||||||
|
Unit="%"
|
||||||
|
Value="{Binding MatchingThreshold, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
</dev:SettingsExpander.Items>
|
||||||
|
</dev:SettingsExpander>
|
||||||
|
<Grid
|
||||||
|
Width="48"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Background="{ThemeResource ControlStrokeColorDefaultBrush}"
|
||||||
|
CornerRadius="4,0,0,4"
|
||||||
|
Opacity="0">
|
||||||
|
<interactivity:Interaction.Behaviors>
|
||||||
|
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||||
|
</interactivity:EventTriggerBehavior>
|
||||||
|
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
||||||
|
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||||
|
</interactivity:EventTriggerBehavior>
|
||||||
|
</interactivity:Interaction.Behaviors>
|
||||||
|
<Grid.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</Grid.OpacityTransition>
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip x:Uid="SettingsPageHoldDragSort" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</ListView>
|
</ListView>
|
||||||
@@ -255,96 +299,65 @@
|
|||||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
<!-- Provider info -->
|
<!-- Realtime info -->
|
||||||
<TextBlock x:Uid="SettingsPageRealtimeStatus" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
<TextBlock x:Uid="SettingsPageRealtimeStatus" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
<dev:SettingsCard ContentAlignment="Left">
|
|
||||||
|
<!-- Playback source info -->
|
||||||
|
<Expander
|
||||||
|
x:Uid="SettingsPagePlaybackStatus"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Left">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<!-- Playback source -->
|
<local:PropertyRow x:Uid="SettingsPagePlaybackSource" Value="{x:Bind ViewModel.MediaSessionsService.CurrentMediaSourceProviderInfo.DisplayName, Mode=OneWay}" />
|
||||||
<Grid ColumnSpacing="12">
|
<local:PropertyRow x:Uid="SettingsPagePlaybackSourceID" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.PlayerId, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<TextBlock x:Uid="SettingsPagePlaybackSource" Grid.Column="0" />
|
|
||||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentMediaSourceProviderInfo.DisplayName, Mode=OneWay}" />
|
|
||||||
</Paragraph>
|
|
||||||
</RichTextBlock>
|
|
||||||
</Grid>
|
|
||||||
<!-- Playback source ID -->
|
|
||||||
<Grid ColumnSpacing="12">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<TextBlock x:Uid="SettingsPagePlaybackSourceID" Grid.Column="0" />
|
|
||||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.PlayerId, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
</Paragraph>
|
|
||||||
</RichTextBlock>
|
|
||||||
</Grid>
|
|
||||||
<!-- Song title -->
|
|
||||||
<Grid ColumnSpacing="12">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlTitle" Grid.Column="0" />
|
|
||||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
</Paragraph>
|
|
||||||
</RichTextBlock>
|
|
||||||
</Grid>
|
|
||||||
<!-- Song artists -->
|
|
||||||
<Grid ColumnSpacing="12">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlArtist" Grid.Column="0" />
|
|
||||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
</Paragraph>
|
|
||||||
</RichTextBlock>
|
|
||||||
</Grid>
|
|
||||||
<!-- Song album -->
|
|
||||||
<Grid ColumnSpacing="12">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<TextBlock x:Uid="LyricsSearchControlAlbum" Grid.Column="0" />
|
|
||||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
|
||||||
<Paragraph>
|
|
||||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
|
||||||
</Paragraph>
|
|
||||||
</RichTextBlock>
|
|
||||||
</Grid>
|
|
||||||
<!-- Lyrics source -->
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
|
||||||
<TextBlock x:Uid="LyricsPageLyricsProviderPrefix" />
|
|
||||||
<HyperlinkButton
|
|
||||||
Padding="0"
|
|
||||||
Content="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
|
|
||||||
IsEnabled="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.IsFound, Mode=OneWay}"
|
|
||||||
NavigateUri="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, Mode=OneWay}" />
|
|
||||||
</StackPanel>
|
|
||||||
<!-- Translation source -->
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
|
||||||
<TextBlock x:Uid="LyricsPageTranslationProviderPrefix" />
|
|
||||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
|
||||||
</StackPanel>
|
|
||||||
<!-- Match percentage -->
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
|
||||||
<TextBlock x:Uid="LyricsPageMatchPercentage" />
|
|
||||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.MatchPercentage, Mode=OneWay}" />
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</dev:SettingsCard>
|
</Expander>
|
||||||
|
|
||||||
|
<!-- Song info -->
|
||||||
|
<Expander
|
||||||
|
x:Uid="SettingsPageSongStatus"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Left">
|
||||||
|
<StackPanel Spacing="6">
|
||||||
|
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
|
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
|
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
|
<local:PropertyRow
|
||||||
|
x:Uid="LyricsSearchControlDurauion"
|
||||||
|
Unit="s"
|
||||||
|
Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Expander>
|
||||||
|
|
||||||
|
<!-- Search result info -->
|
||||||
|
<Expander
|
||||||
|
x:Uid="SettingsPageSearchResultStatus"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Left">
|
||||||
|
<StackPanel Spacing="6">
|
||||||
|
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
|
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
|
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
|
<local:PropertyRow
|
||||||
|
x:Uid="LyricsSearchControlDurauion"
|
||||||
|
Unit="s"
|
||||||
|
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
|
<local:PropertyRow
|
||||||
|
x:Uid="LyricsPageLyricsProviderPrefix"
|
||||||
|
Link="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, Mode=OneWay}"
|
||||||
|
ToolTipService.ToolTip="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, TargetNullValue=N/A, Mode=OneWay}"
|
||||||
|
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||||
|
<local:PropertyRow x:Uid="LyricsPageTranslationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
||||||
|
<local:PropertyRow
|
||||||
|
x:Uid="LyricsPageMatchPercentage"
|
||||||
|
Unit="%"
|
||||||
|
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.MatchPercentage, Mode=OneWay}" />
|
||||||
|
<local:PropertyRow
|
||||||
|
x:Uid="LyricsPageCachePath"
|
||||||
|
Link="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.SelfPath, TargetNullValue=N/A, Mode=OneWay}"
|
||||||
|
ToolTipService.ToolTip="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.SelfPath, TargetNullValue=N/A, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Expander>
|
||||||
|
|
||||||
<dev:SettingsCard x:Uid="SettingsPageForceWordByWordEffect">
|
<dev:SettingsCard x:Uid="SettingsPageForceWordByWordEffect">
|
||||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsForceWordByWordEffect, Mode=TwoWay}" />
|
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsForceWordByWordEffect, Mode=TwoWay}" />
|
||||||
@@ -486,7 +499,7 @@
|
|||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
FontSize=12,
|
FontSize=12,
|
||||||
Glyph=}"
|
Glyph=}"
|
||||||
NavigateUri="{x:Bind constants:Link.AppleMusicCfgUrl}" />
|
NavigateUri="{x:Bind constants:Link.AppleMusicCfg}" />
|
||||||
<Button
|
<Button
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Command="{x:Bind ViewModel.SaveAppleMusicMediaUserTokenCommand}"
|
Command="{x:Bind ViewModel.SaveAppleMusicMediaUserTokenCommand}"
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="BetterLyrics.WinUI3.Controls.PropertyRow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid HorizontalAlignment="Left" ColumnSpacing="6">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Text="{x:Bind Header, Mode=OneWay}" />
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Column="1"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
PointerEntered="OnPointerEntered"
|
||||||
|
PointerExited="OnPointerExited">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Column="0">
|
||||||
|
<RichTextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Visibility="{x:Bind TextVisibility, Mode=OneWay}">
|
||||||
|
<Paragraph>
|
||||||
|
<Run Text="{x:Bind Value, Mode=OneWay}" />
|
||||||
|
<Run Text="{x:Bind Unit, Mode=OneWay}" />
|
||||||
|
</Paragraph>
|
||||||
|
</RichTextBlock>
|
||||||
|
|
||||||
|
<HyperlinkButton
|
||||||
|
Margin="0,-2,0,0"
|
||||||
|
Padding="0"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
Click="OnLinkClicked"
|
||||||
|
Visibility="{x:Bind LinkVisibility, Mode=OneWay}">
|
||||||
|
<TextBlock Text="{x:Bind Value, Mode=OneWay}" TextWrapping="Wrap" />
|
||||||
|
</HyperlinkButton>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
x:Name="CopyButton"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="8,-2,0,0"
|
||||||
|
Padding="4"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Click="OnCopyClicked"
|
||||||
|
Opacity="0">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip x:Uid="Copy" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
<Button.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</Button.OpacityTransition>
|
||||||
|
<Grid>
|
||||||
|
<FontIcon
|
||||||
|
x:Name="CopyIcon"
|
||||||
|
FontFamily="{StaticResource IconFontFamily}"
|
||||||
|
FontSize="16"
|
||||||
|
Glyph=""
|
||||||
|
Opacity="1">
|
||||||
|
<FontIcon.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</FontIcon.OpacityTransition>
|
||||||
|
</FontIcon>
|
||||||
|
<FontIcon
|
||||||
|
x:Name="CheckIcon"
|
||||||
|
FontFamily="{StaticResource IconFontFamily}"
|
||||||
|
FontSize="16"
|
||||||
|
Glyph=""
|
||||||
|
Opacity="0">
|
||||||
|
<FontIcon.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</FontIcon.OpacityTransition>
|
||||||
|
</FontIcon>
|
||||||
|
</Grid>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Windows.ApplicationModel.DataTransfer;
|
||||||
|
|
||||||
|
// 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.Controls
|
||||||
|
{
|
||||||
|
public sealed partial class PropertyRow : UserControl
|
||||||
|
{
|
||||||
|
public PropertyRow()
|
||||||
|
{
|
||||||
|
this.InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Header
|
||||||
|
{
|
||||||
|
get => (string)GetValue(HeaderProperty);
|
||||||
|
set => SetValue(HeaderProperty, value);
|
||||||
|
}
|
||||||
|
public static readonly DependencyProperty HeaderProperty =
|
||||||
|
DependencyProperty.Register(nameof(Header), typeof(string), typeof(PropertyRow), new PropertyMetadata(string.Empty));
|
||||||
|
|
||||||
|
public string Value
|
||||||
|
{
|
||||||
|
get => (string)GetValue(ValueProperty);
|
||||||
|
set => SetValue(ValueProperty, value);
|
||||||
|
}
|
||||||
|
public static readonly DependencyProperty ValueProperty =
|
||||||
|
DependencyProperty.Register(nameof(Value), typeof(string), typeof(PropertyRow), new PropertyMetadata(string.Empty));
|
||||||
|
|
||||||
|
public string Link
|
||||||
|
{
|
||||||
|
get => (string)GetValue(LinkProperty);
|
||||||
|
set => SetValue(LinkProperty, value);
|
||||||
|
}
|
||||||
|
public static readonly DependencyProperty LinkProperty =
|
||||||
|
DependencyProperty.Register(nameof(Link), typeof(string), typeof(PropertyRow), new PropertyMetadata(string.Empty));
|
||||||
|
|
||||||
|
public string Unit
|
||||||
|
{
|
||||||
|
get => (string)GetValue(UnitProperty);
|
||||||
|
set => SetValue(UnitProperty, value);
|
||||||
|
}
|
||||||
|
public static readonly DependencyProperty UnitProperty =
|
||||||
|
DependencyProperty.Register(nameof(Unit), typeof(string), typeof(PropertyRow), new PropertyMetadata(string.Empty));
|
||||||
|
|
||||||
|
private Visibility TextVisibility => Link == string.Empty ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
private Visibility LinkVisibility => Link != string.Empty ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
|
||||||
|
private void OnPointerEntered(object sender, PointerRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Value))
|
||||||
|
{
|
||||||
|
CopyButton.Opacity = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPointerExited(object sender, PointerRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
CopyButton.Opacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCopyClicked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var targetValue = string.IsNullOrEmpty(Link) ? Value : Link;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(targetValue)) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DataPackage dataPackage = new DataPackage();
|
||||||
|
dataPackage.SetText(targetValue);
|
||||||
|
Clipboard.SetContent(dataPackage);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Copy failed: {ex.Message}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckIcon.Opacity = 1;
|
||||||
|
CopyIcon.Opacity = 0;
|
||||||
|
|
||||||
|
this.DispatcherQueue.TryEnqueue(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
|
CheckIcon.Opacity = 0;
|
||||||
|
CopyIcon.Opacity = 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnLinkClicked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Uri.TryCreate(Link, UriKind.Absolute, out var uri);
|
||||||
|
if (uri != null)
|
||||||
|
{
|
||||||
|
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
||||||
|
{
|
||||||
|
await Windows.System.Launcher.LaunchUriAsync(uri);
|
||||||
|
}
|
||||||
|
else if (uri.Scheme == Uri.UriSchemeFile)
|
||||||
|
{
|
||||||
|
await LauncherHelper.SelectAndShowFile(uri.LocalPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="BetterLyrics.WinUI3.Controls.ShadowImage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:const="using:BetterLyrics.WinUI3.Constants"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<UserControl.OpacityTransition>
|
||||||
|
<ScalarTransition />
|
||||||
|
</UserControl.OpacityTransition>
|
||||||
|
|
||||||
|
<Grid Margin="-32" Padding="32">
|
||||||
|
<Grid
|
||||||
|
x:Name="ShadowCastGrid"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
SizeChanged="ShadowCastGrid_SizeChanged">
|
||||||
|
<Image Source="{x:Bind Source, Mode=OneWay}" Stretch="Uniform" />
|
||||||
|
</Grid>
|
||||||
|
<Border
|
||||||
|
x:Name="ShadowRect"
|
||||||
|
Loaded="ShadowRect_Loaded"
|
||||||
|
SizeChanged="ShadowRect_SizeChanged"
|
||||||
|
Translation="0,0,0">
|
||||||
|
<Border.Shadow>
|
||||||
|
<ThemeShadow x:Name="Shadow" />
|
||||||
|
</Border.Shadow>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices.WindowsRuntime;
|
||||||
|
using Windows.Foundation;
|
||||||
|
using Windows.Foundation.Collections;
|
||||||
|
|
||||||
|
// 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.Controls
|
||||||
|
{
|
||||||
|
public sealed partial class ShadowImage : UserControl
|
||||||
|
{
|
||||||
|
public int CornerRadiusAmount
|
||||||
|
{
|
||||||
|
get { return (int)GetValue(CornerRadiusAmountProperty); }
|
||||||
|
set { SetValue(CornerRadiusAmountProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty CornerRadiusAmountProperty =
|
||||||
|
DependencyProperty.Register(nameof(CornerRadiusAmount), typeof(int), typeof(ShadowImage), new PropertyMetadata(0, OnDependencyPropertyChanged));
|
||||||
|
|
||||||
|
public int ShadowAmount
|
||||||
|
{
|
||||||
|
get { return (int)GetValue(ShadowAmountProperty); }
|
||||||
|
set { SetValue(ShadowAmountProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty ShadowAmountProperty =
|
||||||
|
DependencyProperty.Register(nameof(ShadowAmount), typeof(int), typeof(ShadowImage), new PropertyMetadata(0, OnDependencyPropertyChanged));
|
||||||
|
|
||||||
|
public ImageSource? Source
|
||||||
|
{
|
||||||
|
get { return (ImageSource?)GetValue(SourceProperty); }
|
||||||
|
set { SetValue(SourceProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty SourceProperty =
|
||||||
|
DependencyProperty.Register(nameof(Source), typeof(ImageSource), typeof(ShadowImage), new PropertyMetadata(null));
|
||||||
|
|
||||||
|
public ShadowImage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (d is ShadowImage shadowImage)
|
||||||
|
{
|
||||||
|
if (e.Property == CornerRadiusAmountProperty)
|
||||||
|
{
|
||||||
|
shadowImage.UpdateShadowCastGridCornerRadius();
|
||||||
|
shadowImage.UpdateShadowRectCornerRadius();
|
||||||
|
}
|
||||||
|
else if (e.Property == ShadowAmountProperty)
|
||||||
|
{
|
||||||
|
shadowImage.UpdateShadowRectShadow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateShadowRectShadow()
|
||||||
|
{
|
||||||
|
ShadowRect.Translation = new(0, 0, ShadowAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateShadowCastGridCornerRadius()
|
||||||
|
{
|
||||||
|
var minSize = Math.Min(ShadowCastGrid.ActualHeight, ShadowCastGrid.ActualWidth);
|
||||||
|
ShadowCastGrid.CornerRadius = new(CornerRadiusAmount / 100.0 * minSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateShadowRectCornerRadius()
|
||||||
|
{
|
||||||
|
var minSize = Math.Min(ShadowRect.ActualHeight, ShadowRect.ActualWidth);
|
||||||
|
ShadowRect.CornerRadius = new(CornerRadiusAmount / 100.0 * minSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShadowCastGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
UpdateShadowCastGridCornerRadius();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShadowRect_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
UpdateShadowRectCornerRadius();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShadowRect_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Shadow.Receivers.Add(ShadowCastGrid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,11 @@
|
|||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
FontSize=12,
|
FontSize=12,
|
||||||
Glyph=}"
|
Glyph=}"
|
||||||
Style="{StaticResource GhostButtonStyle}" />
|
Style="{StaticResource GhostButtonStyle}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip x:Uid="SettingsPageClear" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
Margin="3,0,0,0"
|
Margin="3,0,0,0"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
@@ -31,7 +35,11 @@
|
|||||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
FontSize=12,
|
FontSize=12,
|
||||||
Glyph=}"
|
Glyph=}"
|
||||||
Style="{StaticResource GhostButtonStyle}" />
|
Style="{StaticResource GhostButtonStyle}">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip x:Uid="SettingsPageCheckShortcut" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
using BetterLyrics.WinUI3.Hooks;
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
@@ -92,11 +93,11 @@ namespace BetterLyrics.WinUI3.Controls
|
|||||||
bool registered = GlobalHotKeyHook.IsHotKeyRegistered(Shortcut);
|
bool registered = GlobalHotKeyHook.IsHotKeyRegistered(Shortcut);
|
||||||
if (registered)
|
if (registered)
|
||||||
{
|
{
|
||||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("SettingsPageShortcutRegSuccessInfo"));
|
ToastHelper.ShowToast("SettingsPageShortcutRegSuccessInfo", null, InfoBarSeverity.Success);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("SettingsPageShortcutRegFailInfo"));
|
ToastHelper.ShowToast("SettingsPageShortcutRegFailInfo", null, InfoBarSeverity.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
x:Uid="SystemTrayMusicGallery"
|
x:Uid="SystemTrayMusicGallery"
|
||||||
Command="{x:Bind ViewModel.OpenMusicGalleryCommand}"
|
Command="{x:Bind ViewModel.OpenMusicGalleryCommand}"
|
||||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
Glyph=}" />
|
Glyph=}" />
|
||||||
<MenuFlyoutItem
|
<MenuFlyoutItem
|
||||||
x:Uid="SystemTraySettings"
|
x:Uid="SystemTraySettings"
|
||||||
Command="{x:Bind ViewModel.OpenSettingsCommand}"
|
Command="{x:Bind ViewModel.OpenSettingsCommand}"
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="BetterLyrics.WinUI3.Controls.WindowSettingsControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dev="using:DevWinUI"
|
||||||
|
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
||||||
|
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||||
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
|
<TextBlock x:Uid="AppSettingsControlGeneral" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<StackPanel
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="6">
|
||||||
|
<TextBox Text="{x:Bind LyricsWindowStatus.Name, Mode=TwoWay}" TextWrapping="Wrap" />
|
||||||
|
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}" />
|
||||||
|
</StackPanel>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsExpander
|
||||||
|
x:Uid="SettingsPageWorkArea"
|
||||||
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
Glyph=}"
|
||||||
|
IsExpanded="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=TwoWay}" />
|
||||||
|
<dev:SettingsExpander.Items>
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageWorkAreaHeight" IsEnabled="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||||
|
<local:ExtendedSlider
|
||||||
|
Default="64"
|
||||||
|
Maximum="{x:Bind LyricsWindowStatus.MonitorBounds.Height, Mode=OneWay}"
|
||||||
|
Minimum="64"
|
||||||
|
Unit="px"
|
||||||
|
Value="{x:Bind LyricsWindowStatus.DockHeight, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageDockPlacement" IsEnabled="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||||
|
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
|
||||||
|
</ComboBox>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageDockMonitor" IsEnabled="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<ComboBox ItemsSource="{x:Bind MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind LyricsWindowStatus.MonitorDeviceName, Mode=TwoWay}" />
|
||||||
|
<Button
|
||||||
|
Click="RefreshMonitorButton_Click"
|
||||||
|
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
FontSize=12,
|
||||||
|
Glyph=}"
|
||||||
|
Style="{StaticResource GhostButtonStyle}" />
|
||||||
|
</StackPanel>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
</dev:SettingsExpander.Items>
|
||||||
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
<dev:SettingsExpander
|
||||||
|
x:Uid="SettingsPageAdaptEnvColor"
|
||||||
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
Glyph=}"
|
||||||
|
IsExpanded="{x:Bind LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAdaptToEnvironment, Mode=TwoWay}" />
|
||||||
|
<dev:SettingsExpander.Items>
|
||||||
|
<dev:SettingsCard
|
||||||
|
x:Uid="SettingsPageEnvColorSample"
|
||||||
|
Header="Environment color sample mode"
|
||||||
|
IsEnabled="{x:Bind LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||||
|
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.EnvironmentSampleMode, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleBelow" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleAbove" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleInner" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleEdge" />
|
||||||
|
</ComboBox>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
</dev:SettingsExpander.Items>
|
||||||
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
<dev:SettingsExpander
|
||||||
|
x:Uid="SettingsPageWindowBounds"
|
||||||
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
Glyph=}"
|
||||||
|
IsExpanded="True">
|
||||||
|
<dev:SettingsExpander.Items>
|
||||||
|
<dev:SettingsCard Header="X">
|
||||||
|
<NumberBox
|
||||||
|
SmallChange="10"
|
||||||
|
SpinButtonPlacementMode="Inline"
|
||||||
|
Value="{x:Bind LyricsWindowStatus.WindowX, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
<dev:SettingsCard Header="Y">
|
||||||
|
<NumberBox
|
||||||
|
SmallChange="10"
|
||||||
|
SpinButtonPlacementMode="Inline"
|
||||||
|
Value="{x:Bind LyricsWindowStatus.WindowY, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageWidth">
|
||||||
|
<NumberBox
|
||||||
|
SmallChange="10"
|
||||||
|
SpinButtonPlacementMode="Inline"
|
||||||
|
Value="{x:Bind LyricsWindowStatus.WindowWidth, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageHeight">
|
||||||
|
<NumberBox
|
||||||
|
SmallChange="10"
|
||||||
|
SpinButtonPlacementMode="Inline"
|
||||||
|
Value="{x:Bind LyricsWindowStatus.WindowHeight, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
</dev:SettingsExpander.Items>
|
||||||
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
<dev:SettingsExpander
|
||||||
|
x:Uid="SettingsPageAOT"
|
||||||
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||||
|
Glyph=}"
|
||||||
|
IsExpanded="True">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAlwaysOnTop, Mode=TwoWay}" />
|
||||||
|
<dev:SettingsExpander.Items>
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageForceAlwaysOnTop" IsEnabled="{x:Bind LyricsWindowStatus.IsAlwaysOnTop, Mode=OneWay}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAlwaysOnTopPolling, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
</dev:SettingsExpander.Items>
|
||||||
|
</dev:SettingsExpander>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.AutoShowOrHideWindow, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageShowInSwitchers" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsShownInSwitchers, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageClickThrough" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsClickThrough, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageBorderless" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsBorderless, Mode=TwoWay}" />
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
<dev:SettingsCard x:Uid="SettingsPageDragArea" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||||
|
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.TitleBarArea, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaNone" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaTop" />
|
||||||
|
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaWhole" />
|
||||||
|
</ComboBox>
|
||||||
|
</dev:SettingsCard>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using BetterLyrics.WinUI3.Hooks;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices.WindowsRuntime;
|
||||||
|
using Windows.Foundation;
|
||||||
|
using Windows.Foundation.Collections;
|
||||||
|
|
||||||
|
// 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.Controls;
|
||||||
|
|
||||||
|
public sealed partial class WindowSettingsControl : UserControl
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty LyricsWindowStatusProperty =
|
||||||
|
DependencyProperty.Register(nameof(LyricsWindowStatus), typeof(AlbumArtAreaEffectSettings), typeof(WindowSettingsControl), new PropertyMetadata(default));
|
||||||
|
|
||||||
|
public LyricsWindowStatus LyricsWindowStatus
|
||||||
|
{
|
||||||
|
get => (LyricsWindowStatus)GetValue(LyricsWindowStatusProperty);
|
||||||
|
set => SetValue(LyricsWindowStatusProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<string> MonitorDeviceNames { get; set; } = [];
|
||||||
|
|
||||||
|
public WindowSettingsControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshMonitorDeviceNames()
|
||||||
|
{
|
||||||
|
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
|
||||||
|
LyricsWindowStatus.MonitorDeviceName = MonitorDeviceNames.FirstOrDefault() ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshMonitorButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
RefreshMonitorDeviceNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using Microsoft.UI.Text;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
|
{
|
||||||
|
public partial class LyricsFontWeightToFontWeightConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is LyricsFontWeight weight)
|
||||||
|
{
|
||||||
|
return weight.ToFontWeight();
|
||||||
|
}
|
||||||
|
return FontWeights.Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
|
{
|
||||||
|
public partial class LyricsLayoutOrientationNegationToOrientationConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is LyricsLayoutOrientation orientation)
|
||||||
|
{
|
||||||
|
return orientation.ToOrientationInverse();
|
||||||
|
}
|
||||||
|
return Orientation.Horizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
|
{
|
||||||
|
public partial class LyricsLayoutOrientationToOrientationConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is LyricsLayoutOrientation orientation)
|
||||||
|
{
|
||||||
|
return orientation.ToOrientation();
|
||||||
|
}
|
||||||
|
return Orientation.Horizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using ATL;
|
||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
|
{
|
||||||
|
public partial class PictureInfosToImageSourceConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
BitmapImage bitmapImage = new();
|
||||||
|
if (value is IList<PictureInfo> list && list.FirstOrDefault()?.PictureData is byte[] pictureData)
|
||||||
|
{
|
||||||
|
bitmapImage.SetSource(ImageHelper.ToIRandomAccessStream(pictureData));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bitmapImage.UriSource = new Uri(PathHelper.AlbumArtPlaceholderPath);
|
||||||
|
}
|
||||||
|
return bitmapImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Converter
|
||||||
|
{
|
||||||
|
public partial class TextAlignmentTypeToHorizontalAlignmentConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is TextAlignmentType type)
|
||||||
|
{
|
||||||
|
return type.ToHorizontalAlignment();
|
||||||
|
}
|
||||||
|
return HorizontalAlignment.Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
|
{
|
||||||
|
public enum ImageSwitchType
|
||||||
|
{
|
||||||
|
Crossfade,
|
||||||
|
Slide
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
// 2025/6/23 by Zhe Fang
|
||||||
|
|
||||||
using BetterLyrics.WinUI3.Helper;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Enums
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
{
|
{
|
||||||
public enum LyricsSearchProvider
|
public enum LyricsSearchProvider
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
using System;
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Enums
|
|
||||||
{
|
{
|
||||||
public enum LyricsSearchType
|
public enum LyricsSearchType
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
|
{
|
||||||
|
public enum SpectrumStyle
|
||||||
|
{
|
||||||
|
Curve,
|
||||||
|
Bar
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
// 2025/6/23 by Zhe Fang
|
||||||
|
|
||||||
using Microsoft.Graphics.Canvas.Text;
|
using Microsoft.Graphics.Canvas.Text;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Enums
|
namespace BetterLyrics.WinUI3.Enums
|
||||||
@@ -14,6 +15,17 @@ namespace BetterLyrics.WinUI3.Enums
|
|||||||
|
|
||||||
public static class LyricsAlignmentTypeExtensions
|
public static class LyricsAlignmentTypeExtensions
|
||||||
{
|
{
|
||||||
|
public static HorizontalAlignment ToHorizontalAlignment(this TextAlignmentType alignmentType)
|
||||||
|
{
|
||||||
|
return alignmentType switch
|
||||||
|
{
|
||||||
|
TextAlignmentType.Left => HorizontalAlignment.Left,
|
||||||
|
TextAlignmentType.Center => HorizontalAlignment.Center,
|
||||||
|
TextAlignmentType.Right => HorizontalAlignment.Right,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(alignmentType), alignmentType, null),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static CanvasHorizontalAlignment ToCanvasHorizontalAlignment(this TextAlignmentType alignmentType)
|
public static CanvasHorizontalAlignment ToCanvasHorizontalAlignment(this TextAlignmentType alignmentType)
|
||||||
{
|
{
|
||||||
return alignmentType switch
|
return alignmentType switch
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Extensions
|
namespace BetterLyrics.WinUI3.Extensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Extensions
|
||||||
|
{
|
||||||
|
public static class LyricsLayoutOrientationExtensions
|
||||||
|
{
|
||||||
|
extension(LyricsLayoutOrientation orientation)
|
||||||
|
{
|
||||||
|
public Orientation ToOrientation() => orientation switch
|
||||||
|
{
|
||||||
|
LyricsLayoutOrientation.Horizontal => Orientation.Horizontal,
|
||||||
|
LyricsLayoutOrientation.Vertical => Orientation.Vertical,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(orientation)),
|
||||||
|
};
|
||||||
|
|
||||||
|
public Orientation ToOrientationInverse() => orientation switch
|
||||||
|
{
|
||||||
|
LyricsLayoutOrientation.Horizontal => Orientation.Vertical,
|
||||||
|
LyricsLayoutOrientation.Vertical => Orientation.Horizontal,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(orientation)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Helper;
|
using BetterLyrics.WinUI3.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Extensions
|
namespace BetterLyrics.WinUI3.Extensions
|
||||||
{
|
{
|
||||||
@@ -18,6 +16,10 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
LyricsSearchProvider.Kugou => PathHelper.KugouLyricsCacheDirectory,
|
LyricsSearchProvider.Kugou => PathHelper.KugouLyricsCacheDirectory,
|
||||||
LyricsSearchProvider.AmllTtmlDb => PathHelper.AmllTtmlDbLyricsCacheDirectory,
|
LyricsSearchProvider.AmllTtmlDb => PathHelper.AmllTtmlDbLyricsCacheDirectory,
|
||||||
LyricsSearchProvider.AppleMusic => PathHelper.AppleMusicCacheDirectory,
|
LyricsSearchProvider.AppleMusic => PathHelper.AppleMusicCacheDirectory,
|
||||||
|
LyricsSearchProvider.LocalMusicFile => PathHelper.LocalMusicCacheDirectory,
|
||||||
|
LyricsSearchProvider.LocalLrcFile => PathHelper.LocalLrcCacheDirectory,
|
||||||
|
LyricsSearchProvider.LocalEslrcFile => PathHelper.LocalEslrcCacheDirectory,
|
||||||
|
LyricsSearchProvider.LocalTtmlFile => PathHelper.LocalTtmlCacheDirectory,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(provider)),
|
_ => throw new ArgumentOutOfRangeException(nameof(provider)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
{
|
{
|
||||||
Name = _resourceService.GetLocalizedString("FullscreenMode"),
|
Name = _resourceService.GetLocalizedString("FullscreenMode"),
|
||||||
IsBorderless = true,
|
IsBorderless = true,
|
||||||
IsAlwaysOnTop = false,
|
IsAlwaysOnTop = true,
|
||||||
TitleBarArea = TitleBarArea.None,
|
TitleBarArea = TitleBarArea.None,
|
||||||
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
|
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
|
||||||
LyricsStyleSettings = new LyricsStyleSettings
|
LyricsStyleSettings = new LyricsStyleSettings
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
extension(Point point)
|
extension(Point point)
|
||||||
{
|
{
|
||||||
public PointInt32 ToPointInt32() => new((int)point.X, (int)point.Y);
|
public PointInt32 ToPointInt32() => new((int)point.X, (int)point.Y);
|
||||||
|
|
||||||
|
public Point AddX(double deltaX) => new(point.X + deltaX, point.Y);
|
||||||
|
public Point AddY(double deltaY) => new(point.X, point.Y + deltaY);
|
||||||
|
|
||||||
|
public Point WithX(double x) => new(x, point.Y);
|
||||||
|
public Point WithY(double y) => new(point.X, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,41 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
rect.Width,
|
rect.Width,
|
||||||
rect.Height
|
rect.Height
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public Rect AddY(double y) => new(
|
||||||
|
rect.X,
|
||||||
|
rect.Y + y,
|
||||||
|
rect.Width,
|
||||||
|
rect.Height
|
||||||
|
);
|
||||||
|
|
||||||
|
public Rect Extend(double left, double top, double right, double bottom) => new(
|
||||||
|
rect.X - left,
|
||||||
|
rect.Y - top,
|
||||||
|
rect.Width + left + right,
|
||||||
|
rect.Height + top + bottom
|
||||||
|
);
|
||||||
|
|
||||||
|
public Rect Extend(double padding) => Extend(rect, padding, padding, padding, padding);
|
||||||
|
|
||||||
|
public Rect Scale(double scale)
|
||||||
|
{
|
||||||
|
double originalWidth = rect.Width;
|
||||||
|
double originalHeight = rect.Height;
|
||||||
|
|
||||||
|
double scaledWidth = originalWidth * scale;
|
||||||
|
double scaledHeight = originalHeight * scale;
|
||||||
|
|
||||||
|
double scaleOffsetX = (scaledWidth - originalWidth) / 2;
|
||||||
|
double scaleOffsetY = (scaledHeight - originalHeight) / 2;
|
||||||
|
|
||||||
|
return new Rect(
|
||||||
|
rect.X - scaleOffsetX,
|
||||||
|
rect.Y - scaleOffsetY,
|
||||||
|
scaledWidth,
|
||||||
|
scaledHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using NTextCat.Commons;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Extensions
|
namespace BetterLyrics.WinUI3.Extensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,7 +15,14 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
{
|
{
|
||||||
if (track.Path is string path)
|
if (track.Path is string path)
|
||||||
{
|
{
|
||||||
return TagLib.File.Create(path).Tag.Lyrics;
|
try
|
||||||
|
{
|
||||||
|
return TagLib.File.Create(path).Tag.Lyrics;
|
||||||
|
}
|
||||||
|
catch (System.Exception)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using Microsoft.Graphics.Canvas;
|
||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using Microsoft.Graphics.Canvas;
|
|
||||||
using Microsoft.Graphics.Canvas.Brushes;
|
|
||||||
using Microsoft.Graphics.Canvas.Effects;
|
using Microsoft.Graphics.Canvas.Effects;
|
||||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
using System;
|
||||||
using Microsoft.UI;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using Windows.Foundation;
|
|
||||||
using Windows.Graphics.Effects;
|
using Windows.Graphics.Effects;
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
|
|
||||||
@@ -16,351 +8,6 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
public class CanvasHelper
|
public class CanvasHelper
|
||||||
{
|
{
|
||||||
public static CanvasLinearGradientBrush CreateHorizontalFillBrush(
|
|
||||||
ICanvasAnimatedControl control,
|
|
||||||
List<(double position, double opacity)> stops,
|
|
||||||
double startX,
|
|
||||||
double width
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return new CanvasLinearGradientBrush(control, stops.Select(stops => new CanvasGradientStop
|
|
||||||
{
|
|
||||||
Position = (float)stops.position,
|
|
||||||
Color = Color.FromArgb((byte)(stops.opacity * 255), 128, 128, 128),
|
|
||||||
}).ToArray())
|
|
||||||
{
|
|
||||||
StartPoint = new Vector2((float)startX, 0),
|
|
||||||
EndPoint = new Vector2((float)(startX + width), 0),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 背景层
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="lyricsLayerOpacity">_lyricsOpacityTransition.Value</param>
|
|
||||||
public static OpacityEffect CreateBackgroundEffect(LyricsLine lyricsLine, CanvasCommandList backgroundFontEffect, double lyricsLayerOpacity)
|
|
||||||
{
|
|
||||||
if (lyricsLine.BlurAmountTransition.Value == 0)
|
|
||||||
{
|
|
||||||
return new OpacityEffect
|
|
||||||
{
|
|
||||||
Source = backgroundFontEffect,
|
|
||||||
Opacity = (float)(lyricsLine.OpacityTransition.Value * lyricsLayerOpacity),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new OpacityEffect
|
|
||||||
{
|
|
||||||
Source = new GaussianBlurEffect
|
|
||||||
{
|
|
||||||
Source = backgroundFontEffect,
|
|
||||||
BlurAmount = (float)lyricsLine.BlurAmountTransition.Value,
|
|
||||||
BorderMode = EffectBorderMode.Soft,
|
|
||||||
Optimization = EffectOptimization.Speed,
|
|
||||||
},
|
|
||||||
Opacity = (float)(lyricsLine.OpacityTransition.Value * lyricsLayerOpacity),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CanvasCommandList CreateFontEffect(LyricsLine lyricsLine, ICanvasAnimatedControl control, Color strokeColor, int strokeWidth, Color fontColor)
|
|
||||||
{
|
|
||||||
CanvasCommandList list = new(control);
|
|
||||||
using var ds = list.CreateDrawingSession();
|
|
||||||
|
|
||||||
// 描边
|
|
||||||
if (strokeWidth > 0)
|
|
||||||
{
|
|
||||||
if (lyricsLine.PhoneticCanvasGeometry != null)
|
|
||||||
{
|
|
||||||
ds.DrawGeometry(lyricsLine.PhoneticCanvasGeometry, lyricsLine.PhoneticPosition, strokeColor, strokeWidth);
|
|
||||||
}
|
|
||||||
if (lyricsLine.OriginalCanvasGeometry != null)
|
|
||||||
{
|
|
||||||
ds.DrawGeometry(lyricsLine.OriginalCanvasGeometry, lyricsLine.OriginalPosition, strokeColor, strokeWidth);
|
|
||||||
}
|
|
||||||
if (lyricsLine.TranslatedCanvasGeometry != null)
|
|
||||||
{
|
|
||||||
ds.DrawGeometry(lyricsLine.TranslatedCanvasGeometry, lyricsLine.TranslatedPosition, strokeColor, strokeWidth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制文本(填充)
|
|
||||||
if (lyricsLine.PhoneticCanvasTextLayout != null)
|
|
||||||
{
|
|
||||||
ds.DrawTextLayout(lyricsLine.PhoneticCanvasTextLayout, lyricsLine.PhoneticPosition, fontColor);
|
|
||||||
}
|
|
||||||
if (lyricsLine.OriginalCanvasTextLayout != null)
|
|
||||||
{
|
|
||||||
ds.DrawTextLayout(lyricsLine.OriginalCanvasTextLayout, lyricsLine.OriginalPosition, fontColor);
|
|
||||||
}
|
|
||||||
if (lyricsLine.TranslatedCanvasTextLayout != null)
|
|
||||||
{
|
|
||||||
ds.DrawTextLayout(lyricsLine.TranslatedCanvasTextLayout, lyricsLine.TranslatedPosition, fontColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建辉光效果层
|
|
||||||
/// 仅需在布局重构 (Relayout) 时调用
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="lineRenderingType">_lyricsGlowEffectScope</param>
|
|
||||||
/// <param name="glowEffectAmount">_lyricsGlowEffectAmount</param>
|
|
||||||
public static GaussianBlurEffect CreateForegroundBlurEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask, double glowEffectAmount)
|
|
||||||
{
|
|
||||||
return new GaussianBlurEffect
|
|
||||||
{
|
|
||||||
Source = new AlphaMaskEffect
|
|
||||||
{
|
|
||||||
Source = foregroundFontEffect,
|
|
||||||
AlphaMask = mask,
|
|
||||||
},
|
|
||||||
BlurAmount = (float)glowEffectAmount,
|
|
||||||
Optimization = EffectOptimization.Speed,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CanvasCommandList CreateCharMask(ICanvasAnimatedControl control, LyricsLine lyricsLine, int charStartIndex, int charLength, double charProgress)
|
|
||||||
{
|
|
||||||
var mask = new CanvasCommandList(control);
|
|
||||||
using var ds = mask.CreateDrawingSession();
|
|
||||||
|
|
||||||
if (lyricsLine.OriginalCanvasTextLayout == null)
|
|
||||||
{
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
var highlightRegion = lyricsLine.OriginalCanvasTextLayout.GetCharacterRegions(charStartIndex, charLength).FirstOrDefault();
|
|
||||||
|
|
||||||
double highlightTotalWidth = (double)highlightRegion.LayoutBounds.Width;
|
|
||||||
// Draw the highlight for the current character
|
|
||||||
double highlightWidth = highlightTotalWidth * charProgress;
|
|
||||||
|
|
||||||
double fadingWidth = (double)highlightRegion.LayoutBounds.Height / 2;
|
|
||||||
|
|
||||||
// Rects
|
|
||||||
var highlightRect = new Rect(
|
|
||||||
highlightRegion.LayoutBounds.X,
|
|
||||||
highlightRegion.LayoutBounds.Y + lyricsLine.OriginalPosition.Y,
|
|
||||||
highlightWidth,
|
|
||||||
highlightRegion.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
|
|
||||||
var fadeInRect = new Rect(
|
|
||||||
highlightRect.Right - fadingWidth,
|
|
||||||
highlightRegion.LayoutBounds.Y + lyricsLine.OriginalPosition.Y,
|
|
||||||
fadingWidth,
|
|
||||||
highlightRegion.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
var fadeOutRect = new Rect(
|
|
||||||
highlightRect.Right,
|
|
||||||
highlightRegion.LayoutBounds.Y + lyricsLine.OriginalPosition.Y,
|
|
||||||
fadingWidth,
|
|
||||||
highlightRegion.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
|
|
||||||
// Brushes
|
|
||||||
using var fadeInBrush = CanvasHelper.CreateHorizontalFillBrush(
|
|
||||||
control,
|
|
||||||
[(0f, 0f), (1f, 1f)],
|
|
||||||
(double)highlightRect.Right - fadingWidth,
|
|
||||||
fadingWidth
|
|
||||||
);
|
|
||||||
using var fadeOutBrush = CanvasHelper.CreateHorizontalFillBrush(
|
|
||||||
control,
|
|
||||||
[(0f, 1f), (1f, 0f)],
|
|
||||||
(double)highlightRect.Right,
|
|
||||||
fadingWidth
|
|
||||||
);
|
|
||||||
|
|
||||||
ds.FillRectangle(fadeInRect, fadeInBrush);
|
|
||||||
ds.FillRectangle(fadeOutRect, fadeOutBrush);
|
|
||||||
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CanvasCommandList CreateLineStartToCharMask(ICanvasAnimatedControl control, LyricsLine lyricsLine, int charStartIndex, int charLength, double charProgress, bool fade)
|
|
||||||
{
|
|
||||||
var mask = new CanvasCommandList(control);
|
|
||||||
|
|
||||||
if (lyricsLine.OriginalCanvasTextLayout == null)
|
|
||||||
{
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var ds = mask.CreateDrawingSession();
|
|
||||||
|
|
||||||
var regions = lyricsLine.OriginalCanvasTextLayout.GetCharacterRegions(0, charStartIndex);
|
|
||||||
var highlightRegion = lyricsLine.OriginalCanvasTextLayout
|
|
||||||
.GetCharacterRegions(charStartIndex, charLength)
|
|
||||||
.FirstOrDefault();
|
|
||||||
if (regions.Length > 0)
|
|
||||||
{
|
|
||||||
// Draw the mask for the current line
|
|
||||||
for (int j = 0; j < regions.Length; j++)
|
|
||||||
{
|
|
||||||
var region = regions[j];
|
|
||||||
var rect = new Rect(
|
|
||||||
region.LayoutBounds.X,
|
|
||||||
region.LayoutBounds.Y + lyricsLine.OriginalPosition.Y,
|
|
||||||
region.LayoutBounds.Width,
|
|
||||||
region.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
ds.FillRectangle(rect, Color.FromArgb(255, 128, 128, 128));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double highlightTotalWidth = (double)highlightRegion.LayoutBounds.Width;
|
|
||||||
// Draw the highlight for the current character
|
|
||||||
double highlightWidth = highlightTotalWidth * charProgress;
|
|
||||||
|
|
||||||
double fadingWidth = (double)highlightRegion.LayoutBounds.Height / 2;
|
|
||||||
|
|
||||||
// Rects
|
|
||||||
var highlightRect = new Rect(
|
|
||||||
highlightRegion.LayoutBounds.X,
|
|
||||||
highlightRegion.LayoutBounds.Y + lyricsLine.OriginalPosition.Y,
|
|
||||||
highlightWidth,
|
|
||||||
highlightRegion.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
|
|
||||||
var fadeInRect = new Rect(
|
|
||||||
highlightRect.Right - fadingWidth,
|
|
||||||
highlightRegion.LayoutBounds.Y + lyricsLine.OriginalPosition.Y,
|
|
||||||
fadingWidth,
|
|
||||||
highlightRegion.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
|
|
||||||
ds.FillRectangle(highlightRect, Color.FromArgb(255, 128, 128, 128));
|
|
||||||
|
|
||||||
if (fade)
|
|
||||||
{
|
|
||||||
var fadeOutRect = new Rect(
|
|
||||||
highlightRect.Right,
|
|
||||||
highlightRegion.LayoutBounds.Y + lyricsLine.OriginalPosition.Y,
|
|
||||||
fadingWidth,
|
|
||||||
highlightRegion.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
using var fadeOutBrush = CreateHorizontalFillBrush(
|
|
||||||
control,
|
|
||||||
[(0f, 1f), (1f, 0f)],
|
|
||||||
(double)highlightRect.Right,
|
|
||||||
fadingWidth
|
|
||||||
);
|
|
||||||
ds.FillRectangle(fadeOutRect, fadeOutBrush);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CanvasCommandList CreateLineMask(ICanvasAnimatedControl control, LyricsLine lyricsLine)
|
|
||||||
{
|
|
||||||
var mask = new CanvasCommandList(control);
|
|
||||||
using var ds = mask.CreateDrawingSession();
|
|
||||||
|
|
||||||
if (lyricsLine.OriginalCanvasTextLayout == null)
|
|
||||||
{
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
var regions = lyricsLine.OriginalCanvasTextLayout.GetCharacterRegions(0, lyricsLine.OriginalText.Length);
|
|
||||||
if (regions.Length > 0)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < regions.Length; j++)
|
|
||||||
{
|
|
||||||
var region = regions[j];
|
|
||||||
var rect = new Rect(
|
|
||||||
region.LayoutBounds.X,
|
|
||||||
region.LayoutBounds.Y + lyricsLine.OriginalPosition.Y,
|
|
||||||
region.LayoutBounds.Width,
|
|
||||||
region.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
ds.FillRectangle(rect, Colors.White);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CanvasCommandList CreatePhoneticHighlightMask(ICanvasAnimatedControl control, LyricsLine lyricsLine)
|
|
||||||
{
|
|
||||||
var mask = new CanvasCommandList(control);
|
|
||||||
using var ds = mask.CreateDrawingSession();
|
|
||||||
|
|
||||||
if (lyricsLine.PhoneticCanvasTextLayout == null)
|
|
||||||
{
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
var regions = lyricsLine.PhoneticCanvasTextLayout.GetCharacterRegions(0, lyricsLine.PhoneticText.Length);
|
|
||||||
if (regions.Length > 0)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < regions.Length; j++)
|
|
||||||
{
|
|
||||||
var region = regions[j];
|
|
||||||
var rect = new Rect(
|
|
||||||
region.LayoutBounds.X,
|
|
||||||
region.LayoutBounds.Y + lyricsLine.PhoneticPosition.Y,
|
|
||||||
region.LayoutBounds.Width,
|
|
||||||
region.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
ds.FillRectangle(rect, Colors.White);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CanvasCommandList CreateTranslatedHighlightMask(ICanvasAnimatedControl control, LyricsLine lyricsLine)
|
|
||||||
{
|
|
||||||
var mask = new CanvasCommandList(control);
|
|
||||||
using var ds = mask.CreateDrawingSession();
|
|
||||||
|
|
||||||
if (lyricsLine.TranslatedCanvasTextLayout == null)
|
|
||||||
{
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
var regions = lyricsLine.TranslatedCanvasTextLayout.GetCharacterRegions(0, lyricsLine.TranslatedText.Length);
|
|
||||||
if (regions.Length > 0)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < regions.Length; j++)
|
|
||||||
{
|
|
||||||
var region = regions[j];
|
|
||||||
var rect = new Rect(
|
|
||||||
region.LayoutBounds.X,
|
|
||||||
region.LayoutBounds.Y + lyricsLine.TranslatedPosition.Y,
|
|
||||||
region.LayoutBounds.Width,
|
|
||||||
region.LayoutBounds.Height
|
|
||||||
);
|
|
||||||
ds.FillRectangle(rect, Colors.White);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建高亮效果层
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="control"></param>
|
|
||||||
/// <param name="lineRenderingType"></param>
|
|
||||||
public static OpacityEffect CreateForegroundHighlightEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask, double opacity)
|
|
||||||
{
|
|
||||||
return new OpacityEffect
|
|
||||||
{
|
|
||||||
Source = new AlphaMaskEffect
|
|
||||||
{
|
|
||||||
Source = foregroundFontEffect,
|
|
||||||
AlphaMask = mask,
|
|
||||||
},
|
|
||||||
Opacity = (float)opacity,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ShadowEffect CreateForegroundShadowEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask, Color shadowColor, double shadowAmount)
|
public static ShadowEffect CreateForegroundShadowEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask, Color shadowColor, double shadowAmount)
|
||||||
{
|
{
|
||||||
return new ShadowEffect
|
return new ShadowEffect
|
||||||
@@ -371,34 +18,10 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
AlphaMask = mask,
|
AlphaMask = mask,
|
||||||
},
|
},
|
||||||
ShadowColor = shadowColor,
|
ShadowColor = shadowColor,
|
||||||
BlurAmount = (float)shadowAmount,
|
BlurAmount = (float)Math.Clamp(shadowAmount, 0, 100),
|
||||||
Optimization = EffectOptimization.Speed,
|
Optimization = EffectOptimization.Speed,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OpacityEffect CreateForegroundTranslationEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask, double opacity)
|
|
||||||
{
|
|
||||||
return new OpacityEffect
|
|
||||||
{
|
|
||||||
Source = new AlphaMaskEffect
|
|
||||||
{
|
|
||||||
Source = foregroundFontEffect,
|
|
||||||
AlphaMask = mask,
|
|
||||||
},
|
|
||||||
Opacity = (float)opacity,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IGraphicsEffectSource GetAlphaMask(ICanvasAnimatedControl control, IGraphicsEffectSource charMask, IGraphicsEffectSource lineStartToCharMask, IGraphicsEffectSource lineMask, LineRenderingType lineRenderingType)
|
|
||||||
{
|
|
||||||
var result = lineRenderingType switch
|
|
||||||
{
|
|
||||||
LineRenderingType.CurrentChar => charMask,
|
|
||||||
LineRenderingType.LineStartToCurrentChar => lineStartToCharMask,
|
|
||||||
LineRenderingType.CurrentLine => lineMask,
|
|
||||||
_ => new CanvasCommandList(control),
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Extensions;
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
using BetterLyrics.WinUI3.Models.Settings;
|
|
||||||
using BetterLyrics.WinUI3.Serialization;
|
using BetterLyrics.WinUI3.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -49,6 +48,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
var json = File.ReadAllText(cacheFilePath);
|
var json = File.ReadAllText(cacheFilePath);
|
||||||
var data = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.LyricsSearchResult);
|
var data = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.LyricsSearchResult);
|
||||||
|
data?.SelfPath = cacheFilePath;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -69,6 +69,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
var cacheFilePath = Path.Combine(
|
var cacheFilePath = Path.Combine(
|
||||||
lyricsSearchResult.Provider.GetCacheDirectory(),
|
lyricsSearchResult.Provider.GetCacheDirectory(),
|
||||||
SanitizeFileName($"{songInfo.ToFileName()}.json"));
|
SanitizeFileName($"{songInfo.ToFileName()}.json"));
|
||||||
|
lyricsSearchResult.SelfPath = cacheFilePath;
|
||||||
var json = System.Text.Json.JsonSerializer.Serialize(lyricsSearchResult, SourceGenerationContext.Default.LyricsSearchResult);
|
var json = System.Text.Json.JsonSerializer.Serialize(lyricsSearchResult, SourceGenerationContext.Default.LyricsSearchResult);
|
||||||
File.WriteAllText(cacheFilePath, json);
|
File.WriteAllText(cacheFilePath, json);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,39 @@
|
|||||||
using Microsoft.Graphics.Canvas.Text;
|
using Microsoft.Graphics.Canvas.Text;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Windows.Markup;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
{
|
{
|
||||||
public static class FontHelper
|
public static class FontHelper
|
||||||
{
|
{
|
||||||
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies().Order().ToArray();
|
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies().Order().ToArray();
|
||||||
|
|
||||||
|
public static string GetLocalizedFontFamilyName(string sourceName, string langCode)
|
||||||
|
{
|
||||||
|
if (langCode == "")
|
||||||
|
{
|
||||||
|
langCode = CultureInfo.CurrentCulture.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var font in Fonts.SystemFontFamilies)
|
||||||
|
{
|
||||||
|
if (font.FamilyNames.TryGetValue(XmlLanguage.GetLanguage("en-us"), out string englishFamilyName) && englishFamilyName == sourceName)
|
||||||
|
{
|
||||||
|
if (font.FamilyNames.ContainsKey(XmlLanguage.GetLanguage(langCode)))
|
||||||
|
{
|
||||||
|
if (font.FamilyNames.TryGetValue(XmlLanguage.GetLanguage(langCode), out string localizedFamilyName))
|
||||||
|
{
|
||||||
|
return localizedFamilyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<byte[]?> GetImageBytesFromUrlAsync(string url)
|
public static async Task<byte[]?> GetImageByteArrayFromUrlAsync(string url)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(url))
|
if (string.IsNullOrWhiteSpace(url))
|
||||||
{
|
{
|
||||||
@@ -185,5 +185,21 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
return buffer.AsStream().AsRandomAccessStream();
|
return buffer.AsStream().AsRandomAccessStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] ToByteArray(IBuffer buffer)
|
||||||
|
{
|
||||||
|
using (var dataReader = DataReader.FromBuffer(buffer))
|
||||||
|
{
|
||||||
|
byte[] byteArray = new byte[buffer.Length];
|
||||||
|
dataReader.ReadBytes(byteArray);
|
||||||
|
return byteArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IRandomAccessStream ToIRandomAccessStream(byte[] arr)
|
||||||
|
{
|
||||||
|
MemoryStream stream = new MemoryStream(arr);
|
||||||
|
return stream.AsRandomAccessStream();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
private static readonly RankedLanguageIdentifier _identifier;
|
private static readonly RankedLanguageIdentifier _identifier;
|
||||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||||
|
|
||||||
|
public const string ChineseCode = "zh";
|
||||||
|
public const string JapaneseCode = "ja";
|
||||||
|
|
||||||
public static List<ExtendedLanguage> SupportedTranslationTargetLanguages { get; set; } =
|
public static List<ExtendedLanguage> SupportedTranslationTargetLanguages { get; set; } =
|
||||||
[
|
[
|
||||||
new ExtendedLanguage("ar"),
|
new ExtendedLanguage("ar"),
|
||||||
@@ -105,7 +108,6 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
public static string? DetectLanguageCode(string? text)
|
public static string? DetectLanguageCode(string? text)
|
||||||
{
|
{
|
||||||
if (text == null) return null;
|
if (text == null) return null;
|
||||||
|
|
||||||
var guessList = _identifier.Identify(text);
|
var guessList = _identifier.Identify(text);
|
||||||
string? code = guessList?.FirstOrDefault()?.Item1.Iso639_2T;
|
string? code = guessList?.FirstOrDefault()?.Item1.Iso639_2T;
|
||||||
code = code switch
|
code = code switch
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
|
{
|
||||||
|
public static class LyricsLayoutHelper
|
||||||
|
{
|
||||||
|
// 硬性限制
|
||||||
|
private const float BaseMinFontSize = 14f;
|
||||||
|
private const float BaseMaxFontSize = 80f;
|
||||||
|
private const float TargetMinVisibleLines = 5f;
|
||||||
|
private const float WidthPaddingRatio = 0.85f;
|
||||||
|
|
||||||
|
// 比例配置
|
||||||
|
private const float RatioSongTitle = 1f;
|
||||||
|
private const float RatioArtist = 0.85f;
|
||||||
|
private const float RatioAlbum = 0.75f;
|
||||||
|
private const float RatioTranslation = 0.7f;
|
||||||
|
private const float RatioTransliteration = 0.55f;
|
||||||
|
private const float AbsoluteMinReadableSize = 10f;
|
||||||
|
|
||||||
|
public static LyricsLayoutMetrics CalculateLayout(double width, double height)
|
||||||
|
{
|
||||||
|
float baseSize = CalculateBaseFontSize(width, height);
|
||||||
|
|
||||||
|
return new LyricsLayoutMetrics
|
||||||
|
{
|
||||||
|
MainLyricsSize = baseSize,
|
||||||
|
TranslationSize = ApplyRatio(baseSize, RatioTranslation),
|
||||||
|
TransliterationSize = ApplyRatio(baseSize, RatioTransliteration),
|
||||||
|
SongTitleSize = ApplyRatio(baseSize, RatioSongTitle),
|
||||||
|
ArtistNameSize = ApplyRatio(baseSize, RatioArtist),
|
||||||
|
AlbumNameSize = ApplyRatio(baseSize, RatioAlbum)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float CalculateBaseFontSize(double width, double height)
|
||||||
|
{
|
||||||
|
float usableWidth = (float)width * WidthPaddingRatio;
|
||||||
|
|
||||||
|
// 宽度 300~500px 时,除以 14 (字大)
|
||||||
|
// 宽度 >1000px 时,除以 30 (字适中,展示更多内容)
|
||||||
|
float targetCharsPerLine;
|
||||||
|
if (width < 500)
|
||||||
|
{
|
||||||
|
targetCharsPerLine = 14f;
|
||||||
|
}
|
||||||
|
else if (width > 1000)
|
||||||
|
{
|
||||||
|
targetCharsPerLine = 30f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 平滑过渡
|
||||||
|
float t = (float)(width - 500) / 500f;
|
||||||
|
targetCharsPerLine = 14f + 16f * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sizeByWidth = usableWidth / targetCharsPerLine;
|
||||||
|
float sizeByHeight = (float)height / TargetMinVisibleLines;
|
||||||
|
|
||||||
|
float targetSize = Math.Min(sizeByWidth, sizeByHeight);
|
||||||
|
|
||||||
|
// 窄屏时底线设高一点 (16px),宽屏如果高度不够可能允许更小
|
||||||
|
float currentMinLimit = (width < 400) ? 16f : BaseMinFontSize;
|
||||||
|
|
||||||
|
return Math.Clamp(targetSize, currentMinLimit, BaseMaxFontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float ApplyRatio(float baseSize, float ratio)
|
||||||
|
{
|
||||||
|
return Math.Max(baseSize * ratio, AbsoluteMinReadableSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,475 +0,0 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
|
||||||
|
|
||||||
using BetterLyrics.WinUI3.Enums;
|
|
||||||
using BetterLyrics.WinUI3.Extensions;
|
|
||||||
using BetterLyrics.WinUI3.Models;
|
|
||||||
using Lyricify.Lyrics.Parsers;
|
|
||||||
using NTextCat.Commons;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper
|
|
||||||
{
|
|
||||||
public partial class LyricsParser
|
|
||||||
{
|
|
||||||
public List<LyricsData> LyricsDataArr { get; private set; } = [];
|
|
||||||
|
|
||||||
public void Parse(SongInfo? songInfo, LyricsSearchResult? lyricsSearchResult)
|
|
||||||
{
|
|
||||||
LyricsDataArr = [];
|
|
||||||
if (lyricsSearchResult?.Raw == null)
|
|
||||||
{
|
|
||||||
LyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder((int)(songInfo?.DurationMs ?? 0)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (lyricsSearchResult.Raw.DetectFormat())
|
|
||||||
{
|
|
||||||
case LyricsFormat.Lrc:
|
|
||||||
case LyricsFormat.Eslrc:
|
|
||||||
ParseLrc(lyricsSearchResult.Raw);
|
|
||||||
break;
|
|
||||||
case LyricsFormat.Qrc:
|
|
||||||
ParseQrcKrc(QrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
|
||||||
break;
|
|
||||||
case LyricsFormat.Krc:
|
|
||||||
ParseQrcKrc(KrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
|
||||||
break;
|
|
||||||
case LyricsFormat.Ttml:
|
|
||||||
ParseTtml(lyricsSearchResult.Raw);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FillRomanizationLyricsData();
|
|
||||||
FillTranslationFromCache(lyricsSearchResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FillTranslationFromCache(LyricsSearchResult? lyricsSearchResult)
|
|
||||||
{
|
|
||||||
if (lyricsSearchResult?.Translation != null)
|
|
||||||
{
|
|
||||||
switch (lyricsSearchResult.Provider)
|
|
||||||
{
|
|
||||||
case LyricsSearchProvider.QQ:
|
|
||||||
case LyricsSearchProvider.Kugou:
|
|
||||||
case LyricsSearchProvider.Netease:
|
|
||||||
ParseLrc(lyricsSearchResult.Translation);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FillRomanizationLyricsData()
|
|
||||||
{
|
|
||||||
var chinese = LyricsDataArr.FirstOrDefault(x => x.LanguageCode == "zh");
|
|
||||||
if (chinese != null)
|
|
||||||
{
|
|
||||||
LyricsDataArr.Add(new LyricsData
|
|
||||||
{
|
|
||||||
LanguageCode = PhoneticHelper.PinyinCode,
|
|
||||||
LyricsLines = chinese.LyricsLines.Select(line => new LyricsLine
|
|
||||||
{
|
|
||||||
StartMs = line.StartMs,
|
|
||||||
EndMs = line.EndMs,
|
|
||||||
OriginalText = PhoneticHelper.ToPinyin(line.OriginalText),
|
|
||||||
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
|
|
||||||
{
|
|
||||||
StartMs = c.StartMs,
|
|
||||||
EndMs = c.EndMs,
|
|
||||||
Text = PhoneticHelper.ToPinyin(c.Text),
|
|
||||||
StartIndex = c.StartIndex
|
|
||||||
}).ToList()
|
|
||||||
}).ToList()
|
|
||||||
});
|
|
||||||
LyricsDataArr.Add(new LyricsData
|
|
||||||
{
|
|
||||||
LanguageCode = PhoneticHelper.JyutpingCode,
|
|
||||||
LyricsLines = chinese.LyricsLines.Select(line => new LyricsLine
|
|
||||||
{
|
|
||||||
StartMs = line.StartMs,
|
|
||||||
EndMs = line.EndMs,
|
|
||||||
OriginalText = PhoneticHelper.ToJyutping(line.OriginalText),
|
|
||||||
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
|
|
||||||
{
|
|
||||||
StartMs = c.StartMs,
|
|
||||||
EndMs = c.EndMs,
|
|
||||||
Text = PhoneticHelper.ToJyutping(c.Text),
|
|
||||||
StartIndex = c.StartIndex
|
|
||||||
}).ToList()
|
|
||||||
}).ToList()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var japanese = LyricsDataArr.FirstOrDefault(x => x.LanguageCode == "ja");
|
|
||||||
if (japanese != null)
|
|
||||||
{
|
|
||||||
LyricsDataArr.Add(new LyricsData
|
|
||||||
{
|
|
||||||
LanguageCode = PhoneticHelper.RomajiCode,
|
|
||||||
LyricsLines = japanese.LyricsLines.Select(line => new LyricsLine
|
|
||||||
{
|
|
||||||
StartMs = line.StartMs,
|
|
||||||
EndMs = line.EndMs,
|
|
||||||
OriginalText = PhoneticHelper.ToRomaji(line.OriginalText),
|
|
||||||
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
|
|
||||||
{
|
|
||||||
StartMs = c.StartMs,
|
|
||||||
EndMs = c.EndMs,
|
|
||||||
Text = PhoneticHelper.ToRomaji(c.Text),
|
|
||||||
StartIndex = c.StartIndex
|
|
||||||
}).ToList()
|
|
||||||
}).ToList()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ParseLrc(string raw)
|
|
||||||
{
|
|
||||||
var lines = raw.Split(["\r\n", "\n"], StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
var lrcLines =
|
|
||||||
new List<(int time, string text, List<(int time, string text)> syllables)>();
|
|
||||||
|
|
||||||
// 支持 [mm:ss.xx]字、<mm:ss.xx>字,毫秒两位或三位
|
|
||||||
var syllableRegex = SyllableRegex();
|
|
||||||
|
|
||||||
foreach (var line in lines)
|
|
||||||
{
|
|
||||||
var matches = syllableRegex.Matches(line);
|
|
||||||
var syllables = new List<(int, string)>();
|
|
||||||
for (int i = 0; i < matches.Count; i++)
|
|
||||||
{
|
|
||||||
var m = matches[i];
|
|
||||||
int min = int.Parse(m.Groups[2].Value);
|
|
||||||
int sec = int.Parse(m.Groups[3].Value);
|
|
||||||
int ms = int.Parse(m.Groups[4].Value.PadRight(3, '0'));
|
|
||||||
int totalMs = min * 60_000 + sec * 1000 + ms;
|
|
||||||
string text = m.Groups[6].Value;
|
|
||||||
|
|
||||||
syllables.Add((totalMs, text));
|
|
||||||
}
|
|
||||||
if (syllables.Count > 1)
|
|
||||||
{
|
|
||||||
lrcLines.Add(
|
|
||||||
(
|
|
||||||
syllables[0].Item1,
|
|
||||||
string.Concat(syllables.Select(s => s.Item2)),
|
|
||||||
syllables
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 普通LRC行
|
|
||||||
Regex? bracketRegex = LrcRegex();
|
|
||||||
var bracketMatches = bracketRegex.Matches(line);
|
|
||||||
|
|
||||||
string content = line;
|
|
||||||
int? lineStartTime = null;
|
|
||||||
if (bracketMatches.Count > 0)
|
|
||||||
{
|
|
||||||
var m = bracketMatches[0];
|
|
||||||
int min = int.Parse(m.Groups[1].Value);
|
|
||||||
int sec = int.Parse(m.Groups[2].Value);
|
|
||||||
int ms = int.Parse(m.Groups[4].Value.PadRight(3, '0'));
|
|
||||||
lineStartTime = min * 60_000 + sec * 1000 + ms;
|
|
||||||
content = bracketRegex!.Replace(line, "").Trim();
|
|
||||||
if (content == "//") content = "";
|
|
||||||
lrcLines.Add((lineStartTime.Value, content, new List<(int, string)>()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按时间分组
|
|
||||||
var grouped = lrcLines.GroupBy(l => l.time).OrderBy(g => g.Key).ToList();
|
|
||||||
int languageCount = 0;
|
|
||||||
if (grouped != null && grouped.Count > 0)
|
|
||||||
{
|
|
||||||
// 计算最大语言数量
|
|
||||||
languageCount = grouped.Max(g => g.Count());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化每种语言的歌词列表
|
|
||||||
//LyricsDataArr.Clear();
|
|
||||||
int langStartIndex = LyricsDataArr.Count;
|
|
||||||
for (int i = 0; i < languageCount; i++) LyricsDataArr.Add(new LyricsData());
|
|
||||||
|
|
||||||
// 遍历每个时间分组
|
|
||||||
if (grouped != null)
|
|
||||||
{
|
|
||||||
foreach (var group in grouped)
|
|
||||||
{
|
|
||||||
var linesInGroup = group.ToList();
|
|
||||||
for (int langIdx = 0; langIdx < languageCount; langIdx++)
|
|
||||||
{
|
|
||||||
// 只添加有对应行的语言,否则跳过
|
|
||||||
if (langIdx < linesInGroup.Count)
|
|
||||||
{
|
|
||||||
var (start, text, syllables) = linesInGroup[langIdx];
|
|
||||||
var line = new LyricsLine
|
|
||||||
{
|
|
||||||
StartMs = start,
|
|
||||||
OriginalText = text,
|
|
||||||
LyricsChars = [],
|
|
||||||
};
|
|
||||||
if (syllables != null && syllables.Count > 0)
|
|
||||||
{
|
|
||||||
int currentIndex = 0;
|
|
||||||
for (int j = 0; j < syllables.Count; j++)
|
|
||||||
{
|
|
||||||
var (charStart, charText) = syllables[j];
|
|
||||||
int startIndex = currentIndex;
|
|
||||||
line.LyricsChars.Add(
|
|
||||||
new LyricsChar
|
|
||||||
{
|
|
||||||
StartMs = charStart,
|
|
||||||
Text = charText ?? "",
|
|
||||||
StartIndex = startIndex,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
currentIndex += charText?.Length ?? 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LyricsDataArr[langStartIndex + langIdx].LyricsLines.Add(line);
|
|
||||||
}
|
|
||||||
// 没有翻译行则不补原文,直接跳过
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ParseTtml(string raw)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
List<LyricsLine> originalLines = [];
|
|
||||||
List<LyricsLine> translationLines = [];
|
|
||||||
var xdoc = XDocument.Parse(raw, LoadOptions.PreserveWhitespace);
|
|
||||||
var body = xdoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "body");
|
|
||||||
if (body == null) return;
|
|
||||||
var ps = body.Descendants().Where(e => e.Name.LocalName == "p");
|
|
||||||
foreach (var p in ps)
|
|
||||||
{
|
|
||||||
// 句级时间
|
|
||||||
string? pBegin = p.Attribute("begin")?.Value;
|
|
||||||
string? pEnd = p.Attribute("end")?.Value;
|
|
||||||
int pStartMs = ParseTtmlTime(pBegin);
|
|
||||||
int pEndMs = ParseTtmlTime(pEnd);
|
|
||||||
|
|
||||||
// 只获取一级span,且排除 ttm:role="x-bg" 的 span 和 ttm:role="x-roman"
|
|
||||||
var spans = p.Elements()
|
|
||||||
.Where(s => s.Name.LocalName == "span" &&
|
|
||||||
s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-bg" &&
|
|
||||||
s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-roman")
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// 原文和翻译分离
|
|
||||||
var originalTextSpans = spans
|
|
||||||
.Where(s => s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-translation")
|
|
||||||
.ToList();
|
|
||||||
var translationTextSpans = spans
|
|
||||||
.Where(s => s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value == "x-translation")
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// 处理原文span后的空白
|
|
||||||
for (int i = 0; i < originalTextSpans.Count; i++)
|
|
||||||
{
|
|
||||||
var span = originalTextSpans[i];
|
|
||||||
var nextNode = span.NodesAfterSelf().FirstOrDefault();
|
|
||||||
if (nextNode is XText textNode)
|
|
||||||
{
|
|
||||||
span.Value += textNode.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 拼接空白字符后的原文
|
|
||||||
string originalText = string.Concat(originalTextSpans.Select(s => s.Value));
|
|
||||||
|
|
||||||
var originalCharTimings = new List<LyricsChar>();
|
|
||||||
int originalStartIndex = 0;
|
|
||||||
foreach (var span in originalTextSpans)
|
|
||||||
{
|
|
||||||
string? sBegin = span.Attribute("begin")?.Value;
|
|
||||||
string? sEnd = span.Attribute("end")?.Value;
|
|
||||||
int sStartMs = ParseTtmlTime(sBegin);
|
|
||||||
int sEndMs = ParseTtmlTime(sEnd);
|
|
||||||
originalCharTimings.Add(new LyricsChar
|
|
||||||
{
|
|
||||||
StartMs = sStartMs,
|
|
||||||
EndMs = sEndMs,
|
|
||||||
StartIndex = originalStartIndex,
|
|
||||||
Text = span.Value
|
|
||||||
});
|
|
||||||
originalStartIndex += span.Value.Length;
|
|
||||||
}
|
|
||||||
if (originalTextSpans.Count == 0)
|
|
||||||
originalText = p.Value;
|
|
||||||
|
|
||||||
originalLines.Add(new LyricsLine
|
|
||||||
{
|
|
||||||
StartMs = pStartMs,
|
|
||||||
EndMs = pEndMs,
|
|
||||||
OriginalText = originalText,
|
|
||||||
LyricsChars = originalCharTimings,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 翻译
|
|
||||||
string translationText = string.Concat(translationTextSpans.Select(s => s.Value));
|
|
||||||
var translationCharTimings = new List<LyricsChar>();
|
|
||||||
int translationStartIndex = 0;
|
|
||||||
foreach (var span in translationTextSpans)
|
|
||||||
{
|
|
||||||
string? sBegin = span.Attribute("begin")?.Value;
|
|
||||||
string? sEnd = span.Attribute("end")?.Value;
|
|
||||||
int sStartMs = ParseTtmlTime(sBegin);
|
|
||||||
int sEndMs = ParseTtmlTime(sEnd);
|
|
||||||
translationCharTimings.Add(new LyricsChar
|
|
||||||
{
|
|
||||||
StartMs = sStartMs,
|
|
||||||
EndMs = sEndMs,
|
|
||||||
StartIndex = translationStartIndex,
|
|
||||||
Text = span.Value
|
|
||||||
});
|
|
||||||
translationStartIndex += span.Value.Length;
|
|
||||||
}
|
|
||||||
if (translationTextSpans.Count > 0)
|
|
||||||
{
|
|
||||||
translationLines.Add(new LyricsLine
|
|
||||||
{
|
|
||||||
StartMs = pStartMs,
|
|
||||||
EndMs = pEndMs,
|
|
||||||
OriginalText = translationText,
|
|
||||||
LyricsChars = translationCharTimings,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LyricsDataArr.Add(new LyricsData(originalLines));
|
|
||||||
if (translationLines.Count > 0)
|
|
||||||
LyricsDataArr.Add(new LyricsData(translationLines));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// 解析失败,忽略
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ParseTtmlTime(string? t)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(t))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
t = t.Trim();
|
|
||||||
|
|
||||||
// 支持 "1.000s"
|
|
||||||
if (t.EndsWith("s"))
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
double.TryParse(
|
|
||||||
t.TrimEnd('s'),
|
|
||||||
System.Globalization.NumberStyles.Float,
|
|
||||||
System.Globalization.CultureInfo.InvariantCulture,
|
|
||||||
out double seconds
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return (int)(seconds * 1000);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var parts = t.Split(':');
|
|
||||||
if (parts.Length == 3)
|
|
||||||
{
|
|
||||||
// hh:mm:ss.xxx
|
|
||||||
int h = int.Parse(parts[0]);
|
|
||||||
int m = int.Parse(parts[1]);
|
|
||||||
double s = double.Parse(
|
|
||||||
parts[2],
|
|
||||||
System.Globalization.CultureInfo.InvariantCulture
|
|
||||||
);
|
|
||||||
return (int)((h * 3600 + m * 60 + s) * 1000);
|
|
||||||
}
|
|
||||||
else if (parts.Length == 2)
|
|
||||||
{
|
|
||||||
// mm:ss.xxx
|
|
||||||
int m = int.Parse(parts[0]);
|
|
||||||
double s = double.Parse(
|
|
||||||
parts[1],
|
|
||||||
System.Globalization.CultureInfo.InvariantCulture
|
|
||||||
);
|
|
||||||
return (int)((m * 60 + s) * 1000);
|
|
||||||
}
|
|
||||||
else if (parts.Length == 1)
|
|
||||||
{
|
|
||||||
// ss.xxx
|
|
||||||
if (
|
|
||||||
double.TryParse(
|
|
||||||
parts[0],
|
|
||||||
System.Globalization.NumberStyles.Float,
|
|
||||||
System.Globalization.CultureInfo.InvariantCulture,
|
|
||||||
out double s
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return (int)(s * 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ParseQrcKrc(List<Lyricify.Lyrics.Models.ILineInfo>? lines)
|
|
||||||
{
|
|
||||||
lines = lines?.Where(x => x.Text != string.Empty).ToList();
|
|
||||||
List<LyricsLine> lyricsLines = [];
|
|
||||||
|
|
||||||
if (lines != null && lines.Count > 0)
|
|
||||||
{
|
|
||||||
lyricsLines = [];
|
|
||||||
for (int lineIndex = 0; lineIndex < lines.Count; lineIndex++)
|
|
||||||
{
|
|
||||||
var lineRead = lines[lineIndex];
|
|
||||||
var lineWrite = new LyricsLine
|
|
||||||
{
|
|
||||||
StartMs = lineRead.StartTime ?? 0,
|
|
||||||
EndMs = lineRead.EndTime ?? 0,
|
|
||||||
OriginalText = lineRead.Text,
|
|
||||||
LyricsChars = [],
|
|
||||||
};
|
|
||||||
|
|
||||||
var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables;
|
|
||||||
if (syllables != null)
|
|
||||||
{
|
|
||||||
int startIndex = 0;
|
|
||||||
for (
|
|
||||||
int syllableIndex = 0;
|
|
||||||
syllableIndex < syllables.Count;
|
|
||||||
syllableIndex++
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var syllable = syllables[syllableIndex];
|
|
||||||
var charTiming = new LyricsChar
|
|
||||||
{
|
|
||||||
StartMs = syllable.StartTime,
|
|
||||||
EndMs = syllable.EndTime,
|
|
||||||
Text = syllable.Text,
|
|
||||||
StartIndex = startIndex,
|
|
||||||
};
|
|
||||||
lineWrite.LyricsChars.Add(charTiming);
|
|
||||||
startIndex += syllable.Text.Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lyricsLines.Add(lineWrite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LyricsDataArr.Add(new LyricsData(lyricsLines));
|
|
||||||
}
|
|
||||||
|
|
||||||
[GeneratedRegex(@"\[(\d*):(\d*)(\.|\:)(\d*)\]")]
|
|
||||||
private static partial Regex LrcRegex();
|
|
||||||
[GeneratedRegex(@"(\[|\<)(\d*):(\d*)\.(\d*)(\]|\>)([^\[\]\<\>]*)")]
|
|
||||||
private static partial Regex SyllableRegex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +1,67 @@
|
|||||||
using BetterLyrics.WinUI3.Models;
|
using BetterLyrics.WinUI3.Models;
|
||||||
using F23.StringSimilarity;
|
using F23.StringSimilarity;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
{
|
{
|
||||||
public static class MetadataComparer
|
public static partial class MetadataComparer
|
||||||
{
|
{
|
||||||
// 权重配置 (总和 1.0)
|
|
||||||
private const double WeightTitle = 0.40;
|
private const double WeightTitle = 0.40;
|
||||||
private const double WeightArtist = 0.40;
|
private const double WeightArtist = 0.40;
|
||||||
private const double WeightAlbum = 0.10;
|
private const double WeightAlbum = 0.10;
|
||||||
private const double WeightDuration = 0.10;
|
private const double WeightDuration = 0.10;
|
||||||
|
|
||||||
// 实例化算法 (JaroWinkler 适合短字符串匹配)
|
// JaroWinkler 适合短字符串匹配
|
||||||
private static readonly JaroWinkler _algo = new JaroWinkler();
|
private static readonly JaroWinkler _algo = new();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 计算 SongInfo 和 LyricsSearchResult 的相似度 (0-100)
|
|
||||||
/// </summary>
|
|
||||||
public static int CalculateScore(SongInfo local, LyricsSearchResult remote)
|
public static int CalculateScore(SongInfo local, LyricsSearchResult remote)
|
||||||
{
|
{
|
||||||
if (local == null || remote == null) return 0;
|
if (local == null || remote == null) return 0;
|
||||||
|
|
||||||
// 1. 标题相似度
|
double totalScore = 0;
|
||||||
double titleScore = GetStringSimilarity(local.Title, remote.Title);
|
|
||||||
|
|
||||||
// 2. 艺术家相似度 (需要处理数组顺序)
|
bool localHasMetadata = !string.IsNullOrWhiteSpace(local.Title);
|
||||||
double artistScore = GetArtistSimilarity(local.Artists, remote.Artists);
|
bool remoteHasMetadata = !string.IsNullOrWhiteSpace(remote.Title);
|
||||||
|
|
||||||
// 3. 专辑相似度
|
if (localHasMetadata && remoteHasMetadata)
|
||||||
double albumScore = GetStringSimilarity(local.Album, remote.Album);
|
{
|
||||||
|
double titleScore = GetStringSimilarity(local.Title, remote.Title);
|
||||||
|
double artistScore = GetArtistSimilarity(local.Artists, remote.Artists);
|
||||||
|
double albumScore = GetStringSimilarity(local.Album, remote.Album);
|
||||||
|
double durationScore = GetDurationSimilarity(local.DurationMs, remote.Duration);
|
||||||
|
|
||||||
// 4. 时长相似度 (基于毫秒 vs 秒的转换和容差)
|
totalScore = (titleScore * WeightTitle) +
|
||||||
double durationScore = GetDurationSimilarity(local.DurationMs, remote.Duration);
|
(artistScore * WeightArtist) +
|
||||||
|
(albumScore * WeightAlbum) +
|
||||||
|
(durationScore * WeightDuration);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string? localQuery = localHasMetadata
|
||||||
|
? $"{local.Title} {string.Join(" ", local.Artists ?? [])}"
|
||||||
|
: Path.GetFileNameWithoutExtension(local.LinkedFileName);
|
||||||
|
|
||||||
// 5. 加权汇总
|
string remoteQuery = remoteHasMetadata
|
||||||
double totalScore = (titleScore * WeightTitle) +
|
? $"{remote.Title} {string.Join(" ", remote.Artists ?? [])}"
|
||||||
(artistScore * WeightArtist) +
|
: Path.GetFileNameWithoutExtension(remote.Reference);
|
||||||
(albumScore * WeightAlbum) +
|
|
||||||
(durationScore * WeightDuration);
|
string fp1 = CreateSortedFingerprint(localQuery);
|
||||||
|
string fp2 = CreateSortedFingerprint(remoteQuery);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(fp1) || string.IsNullOrWhiteSpace(fp2))
|
||||||
|
totalScore = 0;
|
||||||
|
else
|
||||||
|
totalScore = _algo.Similarity(fp1, fp2);
|
||||||
|
}
|
||||||
|
|
||||||
return (int)Math.Round(totalScore * 100);
|
return (int)Math.Round(totalScore * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double GetStringSimilarity(string? s1, string? s2)
|
private static double GetStringSimilarity(string? s1, string? s2)
|
||||||
{
|
{
|
||||||
// 归一化:转小写,去空白
|
|
||||||
s1 = s1?.Trim().ToLowerInvariant() ?? "";
|
s1 = s1?.Trim().ToLowerInvariant() ?? "";
|
||||||
s2 = s2?.Trim().ToLowerInvariant() ?? "";
|
s2 = s2?.Trim().ToLowerInvariant() ?? "";
|
||||||
|
|
||||||
@@ -63,8 +76,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
if (localArtists == null || localArtists.Length == 0) return 0.0;
|
if (localArtists == null || localArtists.Length == 0) return 0.0;
|
||||||
if (remoteArtists == null || remoteArtists.Length == 0) return 0.0;
|
if (remoteArtists == null || remoteArtists.Length == 0) return 0.0;
|
||||||
|
|
||||||
// 技巧:将艺术家数组排序并连接,避免顺序不同导致的不匹配
|
// 将艺术家数组排序并连接,避免顺序不同导致的不匹配
|
||||||
// 例如: ["Jay-Z", "Linkin Park"] 和 ["Linkin Park", "Jay-Z"] 应该是一样的
|
|
||||||
var s1 = string.Join(" ", localArtists.OrderBy(a => a).Select(a => a.Trim().ToLowerInvariant()));
|
var s1 = string.Join(" ", localArtists.OrderBy(a => a).Select(a => a.Trim().ToLowerInvariant()));
|
||||||
var s2 = string.Join(" ", remoteArtists.OrderBy(a => a).Select(a => a.Trim().ToLowerInvariant()));
|
var s2 = string.Join(" ", remoteArtists.OrderBy(a => a).Select(a => a.Trim().ToLowerInvariant()));
|
||||||
|
|
||||||
@@ -78,7 +90,6 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
double localSeconds = localMs / 1000.0;
|
double localSeconds = localMs / 1000.0;
|
||||||
double diff = Math.Abs(localSeconds - remoteSeconds.Value);
|
double diff = Math.Abs(localSeconds - remoteSeconds.Value);
|
||||||
|
|
||||||
// 容差逻辑:
|
|
||||||
// 差距 <= 3秒:100% 相似
|
// 差距 <= 3秒:100% 相似
|
||||||
// 差距 >= 20秒:0% 相似
|
// 差距 >= 20秒:0% 相似
|
||||||
// 中间线性插值
|
// 中间线性插值
|
||||||
@@ -89,8 +100,24 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
if (diff <= PerfectTolerance) return 1.0;
|
if (diff <= PerfectTolerance) return 1.0;
|
||||||
if (diff >= MaxTolerance) return 0.0;
|
if (diff >= MaxTolerance) return 0.0;
|
||||||
|
|
||||||
// 线性递减公式
|
|
||||||
return 1.0 - ((diff - PerfectTolerance) / (MaxTolerance - PerfectTolerance));
|
return 1.0 - ((diff - PerfectTolerance) / (MaxTolerance - PerfectTolerance));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string CreateSortedFingerprint(string? input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input)) return "";
|
||||||
|
|
||||||
|
input = input.ToLowerInvariant();
|
||||||
|
|
||||||
|
string cleaned = NonWordCharactersRegex().Replace(input, " ");
|
||||||
|
|
||||||
|
var tokens = cleaned.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.OrderBy(t => t); // 排序
|
||||||
|
|
||||||
|
return string.Join(" ", tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex(@"[\p{P}\p{S}]")]
|
||||||
|
private static partial Regex NonWordCharactersRegex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,23 +12,23 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
/// <param name="value">要保存的值</param>
|
/// <param name="value">要保存的值</param>
|
||||||
public static void Save(string resource, string key, string value)
|
public static void Save(string resource, string key, string value)
|
||||||
{
|
{
|
||||||
var vault = new PasswordVault();
|
|
||||||
|
|
||||||
// 删除旧值(避免重复存储)
|
// 删除旧值(避免重复存储)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var vault = new PasswordVault();
|
||||||
|
|
||||||
var oldCredential = vault.Retrieve(resource, key);
|
var oldCredential = vault.Retrieve(resource, key);
|
||||||
if (oldCredential != null)
|
if (oldCredential != null)
|
||||||
{
|
{
|
||||||
vault.Remove(oldCredential);
|
vault.Remove(oldCredential);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vault.Add(new PasswordCredential(resource, key, value));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// 没有旧值就忽略
|
// 没有旧值就忽略
|
||||||
}
|
}
|
||||||
|
|
||||||
vault.Add(new PasswordCredential(resource, key, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -39,9 +39,10 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
/// <returns>存储的值,若不存在则返回 null</returns>
|
/// <returns>存储的值,若不存在则返回 null</returns>
|
||||||
public static string? Get(string resource, string key)
|
public static string? Get(string resource, string key)
|
||||||
{
|
{
|
||||||
var vault = new PasswordVault();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var vault = new PasswordVault();
|
||||||
|
|
||||||
var credential = vault.Retrieve(resource, key);
|
var credential = vault.Retrieve(resource, key);
|
||||||
credential.RetrievePassword();
|
credential.RetrievePassword();
|
||||||
return credential.Password;
|
return credential.Password;
|
||||||
@@ -57,9 +58,10 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void Delete(string resource, string key)
|
public static void Delete(string resource, string key)
|
||||||
{
|
{
|
||||||
var vault = new PasswordVault();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var vault = new PasswordVault();
|
||||||
|
|
||||||
var credential = vault.Retrieve(resource, key);
|
var credential = vault.Retrieve(resource, key);
|
||||||
vault.Remove(credential);
|
vault.Remove(credential);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
public static string KugouLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "kugou");
|
public static string KugouLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "kugou");
|
||||||
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "amll-ttml-db");
|
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "amll-ttml-db");
|
||||||
public static string AppleMusicCacheDirectory => Path.Combine(LyricsCacheDirectory, "apple-music");
|
public static string AppleMusicCacheDirectory => Path.Combine(LyricsCacheDirectory, "apple-music");
|
||||||
|
public static string LocalMusicCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-music");
|
||||||
|
public static string LocalLrcCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-lrc");
|
||||||
|
public static string LocalEslrcCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-eslrc");
|
||||||
|
public static string LocalTtmlCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-ttml");
|
||||||
public static string AmllTtmlDbIndexPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-index.jsonl");
|
public static string AmllTtmlDbIndexPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-index.jsonl");
|
||||||
public static string AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
|
public static string AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
|
||||||
|
|
||||||
@@ -65,6 +69,10 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
|
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
|
||||||
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
|
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
|
||||||
Directory.CreateDirectory(AppleMusicCacheDirectory);
|
Directory.CreateDirectory(AppleMusicCacheDirectory);
|
||||||
|
Directory.CreateDirectory(LocalMusicCacheDirectory);
|
||||||
|
Directory.CreateDirectory(LocalLrcCacheDirectory);
|
||||||
|
Directory.CreateDirectory(LocalEslrcCacheDirectory);
|
||||||
|
Directory.CreateDirectory(LocalTtmlCacheDirectory);
|
||||||
|
|
||||||
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Helper
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
{
|
{
|
||||||
@@ -9,13 +8,13 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||||
|
|
||||||
public const string PinyinCode = "zh-pinyin";
|
public const string PinyinCode = "zh-cmn-pinyin";
|
||||||
public const string JyutpingCode = "zh-jyutping";
|
public const string JyutpingCode = "zh-yue-jyutping";
|
||||||
public const string RomajiCode = "ja-romaji";
|
public const string RomanCode = "ja-latin";
|
||||||
|
|
||||||
public static bool IsPhoneticCode(string code)
|
public static bool IsPhoneticCode(string code)
|
||||||
{
|
{
|
||||||
return code == PinyinCode || code == JyutpingCode || code == RomajiCode;
|
return code == PinyinCode || code == JyutpingCode || code == RomanCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetDisplayName(string code)
|
public static string GetDisplayName(string code)
|
||||||
@@ -26,7 +25,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return _resourceService.GetLocalizedString("Pinyin");
|
return _resourceService.GetLocalizedString("Pinyin");
|
||||||
case JyutpingCode:
|
case JyutpingCode:
|
||||||
return _resourceService.GetLocalizedString("Jyutping");
|
return _resourceService.GetLocalizedString("Jyutping");
|
||||||
case RomajiCode:
|
case RomanCode:
|
||||||
return _resourceService.GetLocalizedString("Romaji");
|
return _resourceService.GetLocalizedString("Romaji");
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(code));
|
throw new ArgumentOutOfRangeException(nameof(code));
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using BetterLyrics.WinUI3.Constants;
|
using BetterLyrics.WinUI3.Constants;
|
||||||
using Lyricify.Lyrics.Providers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
@@ -49,7 +48,8 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
PlayerID.Edge => PlayerName.Edge,
|
PlayerID.Edge => PlayerName.Edge,
|
||||||
PlayerID.BetterLyrics => PlayerName.BetterLyrics,
|
PlayerID.BetterLyrics => PlayerName.BetterLyrics,
|
||||||
PlayerID.BetterLyricsDebug => PlayerName.BetterLyricsDebug,
|
PlayerID.BetterLyricsDebug => PlayerName.BetterLyricsDebug,
|
||||||
PlayerID.SaltPlayerForWindows => PlayerName.SaltPlayerForWindows,
|
PlayerID.SaltPlayerForWindowsMS => PlayerName.SaltPlayerForWindowsMS,
|
||||||
|
PlayerID.SaltPlayerForWindowsSteam => PlayerName.SaltPlayerForWindowsSteam,
|
||||||
PlayerID.MoeKoeMusic => PlayerName.MoeKoeMusic,
|
PlayerID.MoeKoeMusic => PlayerName.MoeKoeMusic,
|
||||||
PlayerID.MoeKoeMusicAlternative => PlayerName.MoeKoeMusic,
|
PlayerID.MoeKoeMusicAlternative => PlayerName.MoeKoeMusic,
|
||||||
PlayerID.Listen1 => PlayerName.Listen1,
|
PlayerID.Listen1 => PlayerName.Listen1,
|
||||||
@@ -76,7 +76,8 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
PlayerID.Edge => PathHelper.EdgeLogoPath,
|
PlayerID.Edge => PathHelper.EdgeLogoPath,
|
||||||
PlayerID.BetterLyrics => PathHelper.LogoPath,
|
PlayerID.BetterLyrics => PathHelper.LogoPath,
|
||||||
PlayerID.BetterLyricsDebug => PathHelper.LogoPath,
|
PlayerID.BetterLyricsDebug => PathHelper.LogoPath,
|
||||||
PlayerID.SaltPlayerForWindows => PathHelper.SaltPlayerForWindowsLogoPath,
|
PlayerID.SaltPlayerForWindowsMS => PathHelper.SaltPlayerForWindowsLogoPath,
|
||||||
|
PlayerID.SaltPlayerForWindowsSteam => PathHelper.SaltPlayerForWindowsLogoPath,
|
||||||
PlayerID.MoeKoeMusic => PathHelper.MoeKoeMusicLogoPath,
|
PlayerID.MoeKoeMusic => PathHelper.MoeKoeMusicLogoPath,
|
||||||
PlayerID.MoeKoeMusicAlternative => PathHelper.MoeKoeMusicLogoPath,
|
PlayerID.MoeKoeMusicAlternative => PathHelper.MoeKoeMusicLogoPath,
|
||||||
PlayerID.Listen1 => PathHelper.Listen1LogoPath,
|
PlayerID.Listen1 => PathHelper.Listen1LogoPath,
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
{
|
{
|
||||||
public partial class SpectrumAnalyzer : IDisposable
|
public partial class SpectrumAnalyzer : IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly object _lock = new();
|
||||||
private WasapiLoopbackCapture? _capture;
|
private WasapiLoopbackCapture? _capture;
|
||||||
|
|
||||||
private int _sampleRate = 48000;
|
private int _sampleRate = 48000;
|
||||||
@@ -28,7 +29,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
private float[]? _currentSpectrum;
|
private float[]? _currentSpectrum;
|
||||||
public float[]? SmoothSpectrum { get; private set; }
|
public float[]? SmoothSpectrum { get; private set; }
|
||||||
|
|
||||||
public int BarCount { get; set; } = 16;
|
public int BarCount { get; set; } = 64;
|
||||||
public int Sensitivity { get; set; } = 100;
|
public int Sensitivity { get; set; } = 100;
|
||||||
public float SmoothingFactor { get; set; } = 0.95f;
|
public float SmoothingFactor { get; set; } = 0.95f;
|
||||||
public bool IsCapturing { get; private set; } = false;
|
public bool IsCapturing { get; private set; } = false;
|
||||||
@@ -121,12 +122,15 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
Array.Copy(_spectrumRightData, 0, _spectrumData, _spectrumLeftData.Length, _spectrumRightData.Length);
|
Array.Copy(_spectrumRightData, 0, _spectrumData, _spectrumLeftData.Length, _spectrumRightData.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < BarCount; i++)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
int index = (int)((float)i / BarCount * _spectrumData.Length);
|
for (int i = 0; i < BarCount; i++)
|
||||||
if (index < _spectrumData.Length)
|
|
||||||
{
|
{
|
||||||
_currentSpectrum[i] = _spectrumData[index] * 250f * Sensitivity;
|
int index = (int)((float)i / BarCount * _spectrumData.Length);
|
||||||
|
if (index < _spectrumData.Length)
|
||||||
|
{
|
||||||
|
_currentSpectrum[i] = _spectrumData[index] * 250f * Sensitivity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,10 +143,13 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < BarCount; i++)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
SmoothSpectrum[i] = SmoothSpectrum[i] * SmoothingFactor +
|
for (int i = 0; i < BarCount; i++)
|
||||||
_currentSpectrum[i] * (1 - SmoothingFactor);
|
{
|
||||||
|
SmoothSpectrum[i] = SmoothSpectrum[i] * SmoothingFactor +
|
||||||
|
_currentSpectrum[i] * (1 - SmoothingFactor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.Windows.AppNotifications;
|
||||||
|
using Microsoft.Windows.AppNotifications.Builder;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Helper
|
||||||
|
{
|
||||||
|
public class ToastHelper
|
||||||
|
{
|
||||||
|
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||||
|
|
||||||
|
public static void ShowToast(string localizedTitleKey, string? description, InfoBarSeverity severity)
|
||||||
|
{
|
||||||
|
AppNotification notification = new AppNotificationBuilder()
|
||||||
|
.AddText(_resourceService.GetLocalizedString(localizedTitleKey))
|
||||||
|
.AddText(description)
|
||||||
|
.BuildNotification();
|
||||||
|
|
||||||
|
AppNotificationManager.Default.Show(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,8 +24,10 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
|
|
||||||
public bool IsTransitioning => _isTransitioning;
|
public bool IsTransitioning => _isTransitioning;
|
||||||
public T Value => _currentValue;
|
public T Value => _currentValue;
|
||||||
|
public T StartValue => _startValue;
|
||||||
public T TargetValue => _targetValue;
|
public T TargetValue => _targetValue;
|
||||||
public EasingType? EasingType => _easingType;
|
public EasingType? EasingType => _easingType;
|
||||||
|
public double Progress => _progress;
|
||||||
|
|
||||||
public ValueTransition(T initialValue, double durationSeconds, Func<T, T, double, T>? interpolator = null, EasingType? easingType = null, double delaySeconds = 0)
|
public ValueTransition(T initialValue, double durationSeconds, Func<T, T, double, T>? interpolator = null, EasingType? easingType = null, double delaySeconds = 0)
|
||||||
{
|
{
|
||||||
@@ -57,7 +59,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
|
|
||||||
public void SetDuration(double seconds)
|
public void SetDuration(double seconds)
|
||||||
{
|
{
|
||||||
if (seconds <= 0)
|
if (seconds < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(seconds), "Duration must be positive.");
|
throw new ArgumentOutOfRangeException(nameof(seconds), "Duration must be positive.");
|
||||||
_durationSeconds = seconds;
|
_durationSeconds = seconds;
|
||||||
}
|
}
|
||||||
@@ -145,7 +147,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Func<T, T, double, T> GetInterpolatorByEasingType(EasingType type)
|
private Func<T, T, double, T> GetInterpolatorByEasingType(EasingType? type)
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(double))
|
if (typeof(T) == typeof(double))
|
||||||
{
|
{
|
||||||
@@ -193,6 +195,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
t = EasingHelper.Linear(t);
|
t = EasingHelper.Linear(t);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
t = EasingHelper.EaseInOutQuad(t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return (T)(object)(s + (e - s) * t);
|
return (T)(object)(s + (e - s) * t);
|
||||||
@@ -201,7 +204,7 @@ namespace BetterLyrics.WinUI3.Helper
|
|||||||
throw new NotSupportedException($"Easing type {type} is not supported for type {typeof(T)}.");
|
throw new NotSupportedException($"Easing type {type} is not supported for type {typeof(T)}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetEasingType(EasingType easingType)
|
public void SetEasingType(EasingType? easingType)
|
||||||
{
|
{
|
||||||
_easingType = easingType;
|
_easingType = easingType;
|
||||||
_interpolator = GetInterpolatorByEasingType(easingType);
|
_interpolator = GetInterpolatorByEasingType(easingType);
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
using DevWinUI;
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
using Microsoft.UI.Xaml.Media.Imaging;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Vanara.PInvoke;
|
using Vanara.PInvoke;
|
||||||
using Vanara.Windows.Shell;
|
using Vanara.Windows.Shell;
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Vanara.PInvoke;
|
using Vanara.PInvoke;
|
||||||
using Vanara.Windows.Shell;
|
|
||||||
using Windows.ApplicationModel.Core;
|
using Windows.ApplicationModel.Core;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
using WinRT.Interop;
|
using WinRT.Interop;
|
||||||
@@ -44,7 +43,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
|||||||
|
|
||||||
public static void CloseWindow<T>()
|
public static void CloseWindow<T>()
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(LyricsWindow))
|
if (typeof(T) == typeof(NowPlayingWindow))
|
||||||
{
|
{
|
||||||
EnsureDockModeReleased();
|
EnsureDockModeReleased();
|
||||||
}
|
}
|
||||||
@@ -56,6 +55,15 @@ namespace BetterLyrics.WinUI3.Hooks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void MinimizeWindow<T>()
|
||||||
|
{
|
||||||
|
var window = _activeWindows.Find(w => w is T);
|
||||||
|
if (window is Window w)
|
||||||
|
{
|
||||||
|
w.Minimize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static T? GetWindow<T>()
|
public static T? GetWindow<T>()
|
||||||
{
|
{
|
||||||
foreach (var window in _activeWindows)
|
foreach (var window in _activeWindows)
|
||||||
@@ -92,12 +100,13 @@ namespace BetterLyrics.WinUI3.Hooks
|
|||||||
public static void OpenOrShowWindow<T>()
|
public static void OpenOrShowWindow<T>()
|
||||||
{
|
{
|
||||||
var window = _activeWindows.Find(w => w is T);
|
var window = _activeWindows.Find(w => w is T);
|
||||||
|
//window = null;
|
||||||
if (window == null)
|
if (window == null)
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(LyricsWindow))
|
if (typeof(T) == typeof(NowPlayingWindow))
|
||||||
{
|
{
|
||||||
window = new LyricsWindow();
|
window = new NowPlayingWindow();
|
||||||
((LyricsWindow)window).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
((NowPlayingWindow)window).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
||||||
}
|
}
|
||||||
else if (typeof(T) == typeof(SettingsWindow))
|
else if (typeof(T) == typeof(SettingsWindow))
|
||||||
{
|
{
|
||||||
@@ -115,16 +124,21 @@ namespace BetterLyrics.WinUI3.Hooks
|
|||||||
{
|
{
|
||||||
window = new LyricsWindowSwitchWindow();
|
window = new LyricsWindowSwitchWindow();
|
||||||
}
|
}
|
||||||
|
else if (typeof(T) == typeof(SystemTrayWindow))
|
||||||
|
{
|
||||||
|
window = new SystemTrayWindow();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Unsupported window type", nameof(T));
|
throw new ArgumentException("Unsupported window type", nameof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackWindow(window);
|
TrackWindow(window);
|
||||||
var castedWindow = (Window)window;
|
var castedWindow = (Window)window;
|
||||||
castedWindow.Restore();
|
castedWindow.Restore();
|
||||||
castedWindow.Activate();
|
castedWindow.Activate();
|
||||||
|
|
||||||
if (typeof(T) == typeof(LyricsWindow))
|
if (typeof(T) == typeof(NowPlayingWindow))
|
||||||
{
|
{
|
||||||
_liveStatesService.InitLyricsWindowStatus();
|
_liveStatesService.InitLyricsWindowStatus();
|
||||||
|
|
||||||
@@ -132,7 +146,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
|||||||
_defaultWindowStyle.Add(hwnd, castedWindow.GetWindowStyle());
|
_defaultWindowStyle.Add(hwnd, castedWindow.GetWindowStyle());
|
||||||
_defaultExtendedWindowStyle.Add(hwnd, castedWindow.GetExtendedWindowStyle());
|
_defaultExtendedWindowStyle.Add(hwnd, castedWindow.GetExtendedWindowStyle());
|
||||||
|
|
||||||
var lyricsWindow = (LyricsWindow)window;
|
var lyricsWindow = (NowPlayingWindow)window;
|
||||||
lyricsWindow.ViewModel.InitShortcuts();
|
lyricsWindow.ViewModel.InitShortcuts();
|
||||||
lyricsWindow.ViewModel.InitFgWindowWatcher();
|
lyricsWindow.ViewModel.InitFgWindowWatcher();
|
||||||
|
|
||||||
@@ -186,7 +200,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
|||||||
|
|
||||||
private static void EnsureDockModeReleased()
|
private static void EnsureDockModeReleased()
|
||||||
{
|
{
|
||||||
SetIsWorkArea<LyricsWindow>(false);
|
SetIsWorkArea<NowPlayingWindow>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TrackWindow(object window)
|
private static void TrackWindow(object window)
|
||||||
@@ -292,9 +306,9 @@ namespace BetterLyrics.WinUI3.Hooks
|
|||||||
|
|
||||||
public static void SetTitleBarArea<T>(TitleBarArea titleBarArea)
|
public static void SetTitleBarArea<T>(TitleBarArea titleBarArea)
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(LyricsWindow))
|
if (typeof(T) == typeof(NowPlayingWindow))
|
||||||
{
|
{
|
||||||
LyricsWindow? lyricsWindow = GetWindow<LyricsWindow>();
|
NowPlayingWindow? lyricsWindow = GetWindow<NowPlayingWindow>();
|
||||||
lyricsWindow?.SetTitleBarArea(titleBarArea);
|
lyricsWindow?.SetTitleBarArea(titleBarArea);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -395,7 +409,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
|||||||
|
|
||||||
_setLyricsWindowVisibilityByPlayingStatusTimer.Debounce(() =>
|
_setLyricsWindowVisibilityByPlayingStatusTimer.Debounce(() =>
|
||||||
{
|
{
|
||||||
var window = GetWindow<LyricsWindow>();
|
var window = GetWindow<NowPlayingWindow>();
|
||||||
if (window == null) return;
|
if (window == null) return;
|
||||||
|
|
||||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.CurrentIsPlaying)
|
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.CurrentIsPlaying)
|
||||||
@@ -403,23 +417,23 @@ namespace BetterLyrics.WinUI3.Hooks
|
|||||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||||
{
|
{
|
||||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
|
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
|
||||||
SetIsWorkArea<LyricsWindow>(false);
|
SetIsWorkArea<NowPlayingWindow>(false);
|
||||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
|
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
|
||||||
}
|
}
|
||||||
HideWindow<LyricsWindow>();
|
HideWindow<NowPlayingWindow>();
|
||||||
}
|
}
|
||||||
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.CurrentIsPlaying)
|
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.CurrentIsPlaying)
|
||||||
{
|
{
|
||||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||||
{
|
{
|
||||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
|
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
|
||||||
SetIsWorkArea<LyricsWindow>(true);
|
SetIsWorkArea<NowPlayingWindow>(true);
|
||||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
|
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
|
||||||
}
|
}
|
||||||
OpenOrShowWindow<LyricsWindow>();
|
OpenOrShowWindow<NowPlayingWindow>();
|
||||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||||
{
|
{
|
||||||
MoveAndResize<LyricsWindow>(_liveStatesService.LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
|
MoveAndResize<NowPlayingWindow>(_liveStatesService.LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, Constants.Time.DebounceTimeout);
|
}, Constants.Time.DebounceTimeout);
|
||||||
|
|||||||
138
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs
Normal file
138
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Windows.UI;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Logic
|
||||||
|
{
|
||||||
|
public class LyricsAnimator
|
||||||
|
{
|
||||||
|
private readonly double _defaultScale = 0.75f;
|
||||||
|
private readonly double _highlightedScale = 1.0f;
|
||||||
|
|
||||||
|
public void UpdateLines(
|
||||||
|
IList<RenderLyricsLine>? lines,
|
||||||
|
int startIndex,
|
||||||
|
int endIndex,
|
||||||
|
int playingLineIndex,
|
||||||
|
double canvasHeight,
|
||||||
|
double targetYScrollOffset,
|
||||||
|
double playingLineTopOffsetFactor,
|
||||||
|
LyricsEffectSettings lyricsEffect,
|
||||||
|
ValueTransition<double> canvasYScrollTransition,
|
||||||
|
Color bgColor,
|
||||||
|
Color fgColor,
|
||||||
|
TimeSpan elapsedTime,
|
||||||
|
bool isMouseScrolling,
|
||||||
|
bool isLayoutChanged,
|
||||||
|
bool isPlayingLineChanged,
|
||||||
|
bool isMouseScrollingChanged
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (lines == null) return;
|
||||||
|
|
||||||
|
var currentPlayingLine = lines.ElementAtOrDefault(playingLineIndex);
|
||||||
|
if (currentPlayingLine == null) return;
|
||||||
|
|
||||||
|
for (int i = startIndex; i <= endIndex + 1; i++)
|
||||||
|
{
|
||||||
|
var line = lines.ElementAtOrDefault(i);
|
||||||
|
if (line == null) continue;
|
||||||
|
|
||||||
|
if (isLayoutChanged || isPlayingLineChanged || isMouseScrollingChanged)
|
||||||
|
{
|
||||||
|
int lineCountDelta = i - playingLineIndex;
|
||||||
|
int absLineCountDelta = Math.Abs(lineCountDelta);
|
||||||
|
double distanceFromPlayingLine = Math.Abs(line.OriginalPosition.Y - currentPlayingLine.OriginalPosition.Y);
|
||||||
|
|
||||||
|
double distanceFactor = 0;
|
||||||
|
if (lineCountDelta < 0)
|
||||||
|
{
|
||||||
|
distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * playingLineTopOffsetFactor), 0, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * (1 - playingLineTopOffsetFactor)), 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
double yScrollDuration;
|
||||||
|
double yScrollDelay;
|
||||||
|
|
||||||
|
if (lineCountDelta < 0)
|
||||||
|
{
|
||||||
|
yScrollDuration =
|
||||||
|
canvasYScrollTransition.DurationSeconds +
|
||||||
|
distanceFactor * (lyricsEffect.LyricsScrollTopDuration / 1000.0 - canvasYScrollTransition.DurationSeconds);
|
||||||
|
yScrollDelay = distanceFactor * lyricsEffect.LyricsScrollTopDelay / 1000.0;
|
||||||
|
}
|
||||||
|
else if (lineCountDelta == 0)
|
||||||
|
{
|
||||||
|
yScrollDuration = canvasYScrollTransition.DurationSeconds;
|
||||||
|
yScrollDelay = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yScrollDuration =
|
||||||
|
canvasYScrollTransition.DurationSeconds +
|
||||||
|
distanceFactor * (lyricsEffect.LyricsScrollBottomDuration / 1000.0 - canvasYScrollTransition.DurationSeconds);
|
||||||
|
yScrollDelay = distanceFactor * lyricsEffect.LyricsScrollBottomDelay / 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
line.BlurAmountTransition.SetDuration(yScrollDuration);
|
||||||
|
line.BlurAmountTransition.SetDelay(yScrollDelay);
|
||||||
|
line.BlurAmountTransition.StartTransition(isMouseScrolling ? 0 : (lyricsEffect.IsLyricsBlurEffectEnabled ? (5 * distanceFactor) : 0));
|
||||||
|
|
||||||
|
line.ScaleTransition.SetDuration(yScrollDuration);
|
||||||
|
line.ScaleTransition.SetDelay(yScrollDelay);
|
||||||
|
line.ScaleTransition.StartTransition(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale));
|
||||||
|
|
||||||
|
line.PhoneticOpacityTransition.SetDuration(yScrollDuration);
|
||||||
|
line.PhoneticOpacityTransition.SetDelay(yScrollDelay);
|
||||||
|
line.PhoneticOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.6 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||||
|
|
||||||
|
line.PlayedOriginalOpacityTransition.SetDuration(yScrollDuration);
|
||||||
|
line.PlayedOriginalOpacityTransition.SetDelay(yScrollDelay);
|
||||||
|
line.PlayedOriginalOpacityTransition.StartTransition(absLineCountDelta == 0 ? 1 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||||
|
|
||||||
|
line.UnplayedOriginalOpacityTransition.SetDuration(yScrollDuration);
|
||||||
|
line.UnplayedOriginalOpacityTransition.SetDelay(yScrollDelay);
|
||||||
|
line.UnplayedOriginalOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.3 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||||
|
|
||||||
|
line.TranslatedOpacityTransition.SetDuration(yScrollDuration);
|
||||||
|
line.TranslatedOpacityTransition.SetDelay(yScrollDelay);
|
||||||
|
line.TranslatedOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.6 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||||
|
|
||||||
|
line.ColorTransition.SetDuration(yScrollDuration);
|
||||||
|
line.ColorTransition.SetDelay(yScrollDelay);
|
||||||
|
line.ColorTransition.StartTransition(absLineCountDelta == 0 ? fgColor : bgColor);
|
||||||
|
|
||||||
|
line.AngleTransition.SetEasingType(canvasYScrollTransition.EasingType);
|
||||||
|
line.AngleTransition.SetDuration(yScrollDuration);
|
||||||
|
line.AngleTransition.SetDelay(yScrollDelay);
|
||||||
|
line.AngleTransition.StartTransition(lyricsEffect.IsFanLyricsEnabled ?
|
||||||
|
Math.PI * (lyricsEffect.FanLyricsAngle / 180.0) * distanceFactor * (i > playingLineIndex ? 1 : -1) : 0);
|
||||||
|
|
||||||
|
line.YOffsetTransition.SetEasingType(canvasYScrollTransition.EasingType);
|
||||||
|
line.YOffsetTransition.SetDuration(yScrollDuration);
|
||||||
|
line.YOffsetTransition.SetDelay(yScrollDelay);
|
||||||
|
// 设计之初是当 isLayoutChanged 为真时 jumpTo
|
||||||
|
// 但考虑到动画视觉,强制使用动画
|
||||||
|
line.YOffsetTransition.StartTransition(targetYScrollOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
line.AngleTransition.Update(elapsedTime);
|
||||||
|
line.ScaleTransition.Update(elapsedTime);
|
||||||
|
line.BlurAmountTransition.Update(elapsedTime);
|
||||||
|
line.PhoneticOpacityTransition.Update(elapsedTime);
|
||||||
|
line.PlayedOriginalOpacityTransition.Update(elapsedTime);
|
||||||
|
line.UnplayedOriginalOpacityTransition.Update(elapsedTime);
|
||||||
|
line.TranslatedOpacityTransition.Update(elapsedTime);
|
||||||
|
line.YOffsetTransition.Update(elapsedTime);
|
||||||
|
line.ColorTransition.Update(elapsedTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using BetterLyrics.WinUI3.Models.Settings;
|
||||||
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Windows.Foundation;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Logic
|
||||||
|
{
|
||||||
|
public class LyricsLayoutManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 重排歌词,Y 轴从 0 刻度开始算
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceCreator"></param>
|
||||||
|
/// <param name="lyricsData"></param>
|
||||||
|
/// <param name="status"></param>
|
||||||
|
/// <param name="appSettings"></param>
|
||||||
|
/// <param name="canvasWidth"></param>
|
||||||
|
/// <param name="canvasHeight"></param>
|
||||||
|
/// <param name="lyricsWidth"></param>
|
||||||
|
/// <param name="lyricsHeight"></param>
|
||||||
|
public static void MeasureAndArrange(
|
||||||
|
ICanvasAnimatedControl resourceCreator,
|
||||||
|
IList<RenderLyricsLine>? lines,
|
||||||
|
LyricsWindowStatus status,
|
||||||
|
AppSettings appSettings,
|
||||||
|
double canvasWidth,
|
||||||
|
double canvasHeight,
|
||||||
|
double lyricsWidth,
|
||||||
|
double lyricsHeight)
|
||||||
|
{
|
||||||
|
if (lines == null || resourceCreator == null) return;
|
||||||
|
|
||||||
|
// 计算字体大小
|
||||||
|
int originalFontSize, phoneticFontSize, translatedFontSize;
|
||||||
|
var style = status.LyricsStyleSettings;
|
||||||
|
|
||||||
|
if (style.IsDynamicLyricsFontSize)
|
||||||
|
{
|
||||||
|
var lyricsLayoutMetrics = LyricsLayoutHelper.CalculateLayout(canvasWidth, canvasHeight);
|
||||||
|
|
||||||
|
phoneticFontSize = (int)lyricsLayoutMetrics.TransliterationSize;
|
||||||
|
originalFontSize = (int)lyricsLayoutMetrics.MainLyricsSize;
|
||||||
|
translatedFontSize = (int)lyricsLayoutMetrics.TranslationSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
phoneticFontSize = style.PhoneticLyricsFontSize;
|
||||||
|
originalFontSize = style.OriginalLyricsFontSize;
|
||||||
|
translatedFontSize = style.TranslatedLyricsFontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fontWeight = style.LyricsFontWeight;
|
||||||
|
|
||||||
|
// 排版
|
||||||
|
double currentY = 0;
|
||||||
|
double actualWidth = 0;
|
||||||
|
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
if (line == null) continue;
|
||||||
|
|
||||||
|
line.RecreateTextLayout(
|
||||||
|
resourceCreator,
|
||||||
|
appSettings.TranslationSettings.IsChineseRomanizationEnabled || appSettings.TranslationSettings.IsJapaneseRomanizationEnabled,
|
||||||
|
appSettings.TranslationSettings.IsTranslationEnabled,
|
||||||
|
phoneticFontSize, originalFontSize, translatedFontSize,
|
||||||
|
fontWeight,
|
||||||
|
style.LyricsCJKFontFamily, style.LyricsWesternFontFamily,
|
||||||
|
lyricsWidth, lyricsHeight, style.LyricsAlignmentType
|
||||||
|
);
|
||||||
|
|
||||||
|
line.RecreateTextGeometry();
|
||||||
|
|
||||||
|
// 左上角坐标
|
||||||
|
line.TopLeftPosition = new Vector2(0, (float)currentY);
|
||||||
|
// 注音层
|
||||||
|
line.PhoneticPosition = line.TopLeftPosition;
|
||||||
|
if (line.PhoneticCanvasTextLayout != null)
|
||||||
|
{
|
||||||
|
currentY += line.PhoneticCanvasTextLayout.LayoutBounds.Height;
|
||||||
|
// 间距
|
||||||
|
currentY += (line.PhoneticCanvasTextLayout.LayoutBounds.Height / line.PhoneticCanvasTextLayout.LineCount) * 0.1;
|
||||||
|
|
||||||
|
actualWidth = Math.Max(actualWidth, line.PhoneticCanvasTextLayout.LayoutBounds.Width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原文层
|
||||||
|
line.OriginalPosition = new Vector2(0, (float)currentY);
|
||||||
|
if (line.OriginalCanvasTextLayout != null)
|
||||||
|
{
|
||||||
|
currentY += line.OriginalCanvasTextLayout.LayoutBounds.Height;
|
||||||
|
|
||||||
|
actualWidth = Math.Max(actualWidth, line.OriginalCanvasTextLayout.LayoutBounds.Width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 翻译层
|
||||||
|
if (line.TranslatedCanvasTextLayout != null)
|
||||||
|
{
|
||||||
|
// 间距
|
||||||
|
currentY += (line.TranslatedCanvasTextLayout.LayoutBounds.Height / line.TranslatedCanvasTextLayout.LineCount) * 0.1;
|
||||||
|
}
|
||||||
|
line.TranslatedPosition = new Vector2(0, (float)currentY);
|
||||||
|
if (line.TranslatedCanvasTextLayout != null)
|
||||||
|
{
|
||||||
|
currentY += line.TranslatedCanvasTextLayout.LayoutBounds.Height;
|
||||||
|
|
||||||
|
actualWidth = Math.Max(actualWidth, line.TranslatedCanvasTextLayout.LayoutBounds.Width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右下角坐标
|
||||||
|
line.BottomRightPosition = new Vector2(0 + (float)actualWidth, (float)currentY);
|
||||||
|
|
||||||
|
// 行间距
|
||||||
|
if (line.OriginalCanvasTextLayout != null)
|
||||||
|
{
|
||||||
|
currentY += (line.OriginalCanvasTextLayout.LayoutBounds.Height / line.OriginalCanvasTextLayout.LineCount) * style.LyricsLineSpacingFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新中心点
|
||||||
|
line.UpdateCenterPosition(lyricsWidth, style.LyricsAlignmentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算为了让当前歌词行的竖直几何中心点对齐到 0(原点),画布应该移动的距离(从画布最初始状态计算的值)
|
||||||
|
/// </summary>
|
||||||
|
public static double? CalculateTargetScrollOffset(
|
||||||
|
IList<RenderLyricsLine>? lines,
|
||||||
|
int playingLineIndex)
|
||||||
|
{
|
||||||
|
if (lines == null || lines.Count == 0) return null;
|
||||||
|
|
||||||
|
var currentLine = lines.ElementAtOrDefault(playingLineIndex);
|
||||||
|
var firstLine = lines.FirstOrDefault();
|
||||||
|
|
||||||
|
if (currentLine?.OriginalCanvasTextLayout == null || firstLine == null) return null;
|
||||||
|
|
||||||
|
return -currentLine.OriginalPosition.Y + firstLine.OriginalPosition.Y
|
||||||
|
- (currentLine.BottomRightPosition.Y - currentLine.TopLeftPosition.Y) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算当前屏幕可见的行范围
|
||||||
|
/// 返回值: (StartVisibleIndex, EndVisibleIndex)
|
||||||
|
/// </summary>
|
||||||
|
public static (int Start, int End) CalculateVisibleRange(
|
||||||
|
IList<RenderLyricsLine>? lines,
|
||||||
|
double currentScrollOffset,
|
||||||
|
double lyricsY,
|
||||||
|
double lyricsHeight,
|
||||||
|
double canvasHeight,
|
||||||
|
double playingLineTopOffsetFactor
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (lines == null || lines.Count == 0) return (-1, -1);
|
||||||
|
|
||||||
|
double offset = currentScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
|
||||||
|
|
||||||
|
int start = FindFirstVisibleLine(lines, offset, lyricsY);
|
||||||
|
int end = FindLastVisibleLine(lines, offset, lyricsY, lyricsHeight, canvasHeight);
|
||||||
|
|
||||||
|
// 修正边界情况
|
||||||
|
if (start != -1 && end == -1)
|
||||||
|
{
|
||||||
|
end = lines.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (int Start, int End) CalculateMaxRange(IList<RenderLyricsLine>? lines)
|
||||||
|
{
|
||||||
|
if (lines == null || lines.Count == 0) return (-1, -1);
|
||||||
|
|
||||||
|
return (0, lines.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double CalculateActualHeight(IList<RenderLyricsLine>? lines)
|
||||||
|
{
|
||||||
|
if (lines == null || lines.Count == 0) return 0;
|
||||||
|
|
||||||
|
return lines.Last().BottomRightPosition.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FindMouseHoverLineIndex(
|
||||||
|
IList<RenderLyricsLine>? lines,
|
||||||
|
bool isMouseInLyricsArea,
|
||||||
|
Point mousePosition,
|
||||||
|
double currentScrollOffset,
|
||||||
|
double lyricsY,
|
||||||
|
double lyricsHeight,
|
||||||
|
double playingLineTopOffsetFactor
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!isMouseInLyricsArea) return -1;
|
||||||
|
|
||||||
|
if (lines == null || lines.Count == 0) return -1;
|
||||||
|
|
||||||
|
double offset = currentScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
|
||||||
|
|
||||||
|
int left = 0, right = lines.Count - 1, result = -1;
|
||||||
|
while (left <= right)
|
||||||
|
{
|
||||||
|
int mid = (left + right) / 2;
|
||||||
|
var line = lines[mid];
|
||||||
|
if (line.OriginalCanvasTextLayout == null) break;
|
||||||
|
double value = offset + line.BottomRightPosition.Y;
|
||||||
|
if (value >= mousePosition.Y) { result = mid; right = mid - 1; }
|
||||||
|
else { left = mid + 1; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int FindFirstVisibleLine(IList<RenderLyricsLine> lines, double offset, double lyricsY)
|
||||||
|
{
|
||||||
|
int left = 0, right = lines.Count - 1, result = -1;
|
||||||
|
while (left <= right)
|
||||||
|
{
|
||||||
|
int mid = (left + right) / 2;
|
||||||
|
var line = lines[mid];
|
||||||
|
if (line.OriginalCanvasTextLayout == null) break;
|
||||||
|
double value = offset + line.BottomRightPosition.Y;
|
||||||
|
// 理论上说应该使用下面这一行来精确计算视野内的首个可见行,但是考虑到动画视觉效果,还是注释掉了
|
||||||
|
//if (value >= lyricsY) { result = mid; right = mid - 1; }
|
||||||
|
if (value >= 0) { result = mid; right = mid - 1; }
|
||||||
|
else { left = mid + 1; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int FindLastVisibleLine(IList<RenderLyricsLine> lines, double offset, double lyricsY, double lyricsHeight, double canvasHeight)
|
||||||
|
{
|
||||||
|
int left = 0, right = lines.Count - 1, result = -1;
|
||||||
|
while (left <= right)
|
||||||
|
{
|
||||||
|
int mid = (left + right) / 2;
|
||||||
|
var line = lines[mid];
|
||||||
|
if (line.OriginalCanvasTextLayout == null) break;
|
||||||
|
double value = offset + line.BottomRightPosition.Y;
|
||||||
|
// 同理
|
||||||
|
//if (value >= lyricsY + lyricsHeight) { result = mid; right = mid - 1; }
|
||||||
|
if (value >= canvasHeight) { result = mid; right = mid - 1; }
|
||||||
|
else { left = mid + 1; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Logic
|
||||||
|
{
|
||||||
|
public class LyricsSynchronizer
|
||||||
|
{
|
||||||
|
private int _lastFoundIndex = 0;
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_lastFoundIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetCurrentLineIndex(double currentTimeMs, LyricsData? lyricsData)
|
||||||
|
{
|
||||||
|
if (lyricsData == null || lyricsData.LyricsLines.Count == 0) return 0;
|
||||||
|
var lines = lyricsData.LyricsLines;
|
||||||
|
|
||||||
|
// Cache hit
|
||||||
|
if (IsTimeInLine(currentTimeMs, lines, _lastFoundIndex)) return _lastFoundIndex;
|
||||||
|
if (_lastFoundIndex + 1 < lines.Count && IsTimeInLine(currentTimeMs, lines, _lastFoundIndex + 1))
|
||||||
|
{
|
||||||
|
_lastFoundIndex++;
|
||||||
|
return _lastFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache miss
|
||||||
|
for (int i = 0; i < lines.Count; i++)
|
||||||
|
{
|
||||||
|
if (IsTimeInLine(currentTimeMs, lines, i))
|
||||||
|
{
|
||||||
|
_lastFoundIndex = i;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default
|
||||||
|
return Math.Min(_lastFoundIndex, lines.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinePlaybackState GetLinePlayingProgress(
|
||||||
|
double currentTimeMs,
|
||||||
|
LyricsLine line,
|
||||||
|
LyricsLine? nextLine,
|
||||||
|
double songDurationMs,
|
||||||
|
bool isForceWordByWord)
|
||||||
|
{
|
||||||
|
var state = new LinePlaybackState { SyllableStartIndex = 0, SyllableLength = 0, SyllableProgress = 0 };
|
||||||
|
|
||||||
|
if (line == null) return state;
|
||||||
|
|
||||||
|
double lineEndMs;
|
||||||
|
if (line.EndMs != null) lineEndMs = line.EndMs.Value;
|
||||||
|
else if (nextLine != null) lineEndMs = nextLine.StartMs;
|
||||||
|
else lineEndMs = songDurationMs;
|
||||||
|
|
||||||
|
// 还没到
|
||||||
|
if (currentTimeMs < line.StartMs) return state;
|
||||||
|
|
||||||
|
// 过了
|
||||||
|
if (currentTimeMs > lineEndMs)
|
||||||
|
{
|
||||||
|
state.SyllableProgress = 1f;
|
||||||
|
state.SyllableStartIndex = Math.Max(0, line.OriginalText.Length - 1);
|
||||||
|
state.SyllableLength = 1;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 逐字
|
||||||
|
if (line.LyricsSyllables != null && line.LyricsSyllables.Count > 1)
|
||||||
|
{
|
||||||
|
return CalculateSyllableProgress(currentTimeMs, line, lineEndMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 强制逐字
|
||||||
|
if (isForceWordByWord && line.OriginalText.Length > 0)
|
||||||
|
{
|
||||||
|
return CalculateSimulatedProgress(currentTimeMs, line, lineEndMs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 普通行
|
||||||
|
state.SyllableStartIndex = line.OriginalText.Length;
|
||||||
|
state.SyllableProgress = 1f;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinePlaybackState CalculateSyllableProgress(double time, LyricsLine line, double lineEndMs)
|
||||||
|
{
|
||||||
|
var state = new LinePlaybackState();
|
||||||
|
int count = line.LyricsSyllables.Count;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var timing = line.LyricsSyllables[i];
|
||||||
|
var nextTiming = (i + 1 < count) ? line.LyricsSyllables[i + 1] : null;
|
||||||
|
|
||||||
|
double timingEndMs = timing.EndMs ?? nextTiming?.StartMs ?? lineEndMs;
|
||||||
|
|
||||||
|
// 在当前字范围内
|
||||||
|
if (time >= timing.StartMs && time <= timingEndMs)
|
||||||
|
{
|
||||||
|
state.SyllableStartIndex = timing.StartIndex;
|
||||||
|
state.SyllableLength = timing.Text.Length;
|
||||||
|
state.SyllableProgress = (timingEndMs > timing.StartMs)
|
||||||
|
? (time - timing.StartMs) / (timingEndMs - timing.StartMs)
|
||||||
|
: 0;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
// 在空隙中 (已过当前字,未到下个字)
|
||||||
|
else if (time > timingEndMs && (nextTiming == null || time < nextTiming.StartMs))
|
||||||
|
{
|
||||||
|
state.SyllableProgress = 1f; // 保持上个字满进度
|
||||||
|
state.SyllableStartIndex = timing.StartIndex;
|
||||||
|
state.SyllableLength = timing.Text.Length;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinePlaybackState CalculateSimulatedProgress(double time, LyricsLine line, double lineEndMs)
|
||||||
|
{
|
||||||
|
var state = new LinePlaybackState();
|
||||||
|
int textLength = line.OriginalText.Length;
|
||||||
|
|
||||||
|
double progress = (time - line.StartMs) / (lineEndMs - line.StartMs);
|
||||||
|
progress = Math.Clamp(progress, 0, 1);
|
||||||
|
|
||||||
|
double charFloatIndex = progress * textLength;
|
||||||
|
int charIndex = (int)charFloatIndex;
|
||||||
|
|
||||||
|
state.SyllableStartIndex = Math.Clamp(charIndex, 0, textLength - 1);
|
||||||
|
state.SyllableLength = 1;
|
||||||
|
state.SyllableProgress = charFloatIndex - charIndex;
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsTimeInLine(double time, IList<LyricsLine> lines, int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= lines.Count) return false;
|
||||||
|
var line = lines[index];
|
||||||
|
var nextLine = (index + 1 < lines.Count) ? lines[index + 1] : null;
|
||||||
|
if (time < line.StartMs) return false;
|
||||||
|
if (nextLine != null && time >= nextLine.StartMs) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Windows.UI;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Models
|
||||||
|
{
|
||||||
|
public struct AlbumArtThemeColors
|
||||||
|
{
|
||||||
|
public Color BgFontColor;
|
||||||
|
public Color FgFontColor;
|
||||||
|
public Color StrokeFontColor;
|
||||||
|
public Color EnvColor;
|
||||||
|
|
||||||
|
public Color AccentColor1;
|
||||||
|
public Color AccentColor2;
|
||||||
|
public Color AccentColor3;
|
||||||
|
public Color AccentColor4;
|
||||||
|
|
||||||
|
public ElementTheme ThemeType;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace BetterLyrics.WinUI3.Models
|
||||||
|
{
|
||||||
|
public class ExtendedFontFamily
|
||||||
|
{
|
||||||
|
public string FontFamily { get; set; } = "";
|
||||||
|
public string LocalizedFontFamily { get; set; } = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace BetterLyrics.WinUI3.Models
|
||||||
|
{
|
||||||
|
public struct LinePlaybackState
|
||||||
|
{
|
||||||
|
public int SyllableStartIndex;
|
||||||
|
public int SyllableLength;
|
||||||
|
public double SyllableProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using BetterLyrics.WinUI3.Helper;
|
using BetterLyrics.WinUI3.Helper;
|
||||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using DevWinUI;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -17,8 +18,9 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
get => field ?? LanguageHelper.DetectLanguageCode(WrappedOriginalText);
|
get => field ?? LanguageHelper.DetectLanguageCode(WrappedOriginalText);
|
||||||
set => field = value;
|
set => field = value;
|
||||||
}
|
}
|
||||||
|
public bool AutoGenerated { get; set; } = false;
|
||||||
public string WrappedOriginalText => string.Join(StringHelper.NewLine, LyricsLines.Select(line => line.OriginalText));
|
public string WrappedOriginalText => string.Join(StringHelper.NewLine, LyricsLines.Select(line => line.OriginalText));
|
||||||
public bool IsWordByWord => LyricsLines.Any(x => x.LyricsChars.Count != 0);
|
public bool IsWordByWord => LyricsLines.Any(x => x.LyricsSyllables.Count != 0);
|
||||||
|
|
||||||
public LyricsData()
|
public LyricsData()
|
||||||
{
|
{
|
||||||
@@ -46,7 +48,7 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTranslatedText(LyricsData translationData, string separator, int toleranceMs = 0)
|
public void SetTranslatedText(LyricsData translationData, int toleranceMs = 0)
|
||||||
{
|
{
|
||||||
foreach (var line in LyricsLines)
|
foreach (var line in LyricsLines)
|
||||||
{
|
{
|
||||||
@@ -67,7 +69,7 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPhoneticText(LyricsData phoneticData, string separator, int toleranceMs = 0)
|
public void SetPhoneticText(LyricsData phoneticData, int toleranceMs = 0)
|
||||||
{
|
{
|
||||||
foreach (var line in LyricsLines)
|
foreach (var line in LyricsLines)
|
||||||
{
|
{
|
||||||
@@ -88,7 +90,7 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTranslation(string translation, string separator)
|
public void SetTranslation(string translation)
|
||||||
{
|
{
|
||||||
List<string> translationArr = translation.Split(StringHelper.NewLine).ToList();
|
List<string> translationArr = translation.Split(StringHelper.NewLine).ToList();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -130,14 +132,23 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LyricsData GetNotfoundPlaceholder(int durationMs)
|
public static LyricsData GetNotfoundPlaceholder()
|
||||||
{
|
{
|
||||||
return new LyricsData([new LyricsLine
|
return new LyricsData([new LyricsLine
|
||||||
{
|
{
|
||||||
StartMs = 0,
|
StartMs = 0,
|
||||||
EndMs = durationMs,
|
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||||
OriginalText = _resourceService.GetLocalizedString("LyricsNotFound"),
|
OriginalText = _resourceService.GetLocalizedString("LyricsNotFound"),
|
||||||
LyricsChars = [],
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LyricsData GetParseErrorPlaceholder()
|
||||||
|
{
|
||||||
|
return new LyricsData([new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = 0,
|
||||||
|
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||||
|
OriginalText = _resourceService.GetLocalizedString("LyricsParseError"),
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,10 +159,7 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
{
|
{
|
||||||
StartMs = 0,
|
StartMs = 0,
|
||||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||||
PhoneticText = "",
|
|
||||||
OriginalText = "● ● ●",
|
OriginalText = "● ● ●",
|
||||||
TranslatedText = "",
|
|
||||||
LyricsChars = [],
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -168,5 +176,6 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Models
|
||||||
|
{
|
||||||
|
public struct LyricsLayoutMetrics
|
||||||
|
{
|
||||||
|
public float MainLyricsSize;
|
||||||
|
public float TranslationSize;
|
||||||
|
public float TransliterationSize;
|
||||||
|
|
||||||
|
public float SongTitleSize;
|
||||||
|
public float ArtistNameSize;
|
||||||
|
public float AlbumNameSize;
|
||||||
|
|
||||||
|
public Thickness AlbumArtPadding;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,64 +6,17 @@ using BetterLyrics.WinUI3.Helper;
|
|||||||
using Microsoft.Graphics.Canvas.Geometry;
|
using Microsoft.Graphics.Canvas.Geometry;
|
||||||
using Microsoft.Graphics.Canvas.Text;
|
using Microsoft.Graphics.Canvas.Text;
|
||||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
|
using Microsoft.UI;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Windows.UI;
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models
|
namespace BetterLyrics.WinUI3.Models
|
||||||
{
|
{
|
||||||
public class LyricsLine
|
public class LyricsLine
|
||||||
{
|
{
|
||||||
private const double _animationDuration = 0.3;
|
public List<LyricsSyllable> LyricsSyllables { get; set; } = [];
|
||||||
public ValueTransition<double> AngleTransition { get; set; } = new(
|
|
||||||
initialValue: 0,
|
|
||||||
durationSeconds: _animationDuration,
|
|
||||||
easingType: EasingType.EaseInOutQuad
|
|
||||||
);
|
|
||||||
public ValueTransition<double> BlurAmountTransition { get; set; } = new(
|
|
||||||
initialValue: 0,
|
|
||||||
durationSeconds: _animationDuration,
|
|
||||||
easingType: EasingType.EaseInOutQuad
|
|
||||||
);
|
|
||||||
public ValueTransition<double> HighlightOpacityTransition { get; set; } = new(
|
|
||||||
initialValue: 0,
|
|
||||||
durationSeconds: _animationDuration,
|
|
||||||
easingType: EasingType.EaseInOutQuad
|
|
||||||
);
|
|
||||||
public ValueTransition<double> OpacityTransition { get; set; } = new(
|
|
||||||
initialValue: 0,
|
|
||||||
durationSeconds: _animationDuration,
|
|
||||||
easingType: EasingType.EaseInOutQuad
|
|
||||||
);
|
|
||||||
public ValueTransition<double> ScaleTransition { get; set; } = new(
|
|
||||||
initialValue: 0,
|
|
||||||
durationSeconds: _animationDuration,
|
|
||||||
easingType: EasingType.EaseInOutQuad
|
|
||||||
);
|
|
||||||
public ValueTransition<double> YOffsetTransition { get; set; } = new(
|
|
||||||
initialValue: 0,
|
|
||||||
durationSeconds: 0.5,
|
|
||||||
easingType: EasingType.EaseInOutQuad
|
|
||||||
);
|
|
||||||
|
|
||||||
public CanvasTextLayout? OriginalCanvasTextLayout { get; private set; }
|
|
||||||
public CanvasTextLayout? TranslatedCanvasTextLayout { get; private set; }
|
|
||||||
public CanvasTextLayout? PhoneticCanvasTextLayout { get; private set; }
|
|
||||||
|
|
||||||
public Vector2 CenterPosition { get; private set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 原文位置
|
|
||||||
/// </summary>
|
|
||||||
public Vector2 OriginalPosition { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 译文位置
|
|
||||||
/// </summary>
|
|
||||||
public Vector2 TranslatedPosition { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 注音位置
|
|
||||||
/// </summary>
|
|
||||||
public Vector2 PhoneticPosition { get; set; }
|
|
||||||
|
|
||||||
public List<LyricsChar> LyricsChars { get; set; } = [];
|
|
||||||
|
|
||||||
public int? DurationMs => EndMs - StartMs;
|
public int? DurationMs => EndMs - StartMs;
|
||||||
public int? EndMs { get; set; }
|
public int? EndMs { get; set; }
|
||||||
@@ -82,123 +35,5 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string PhoneticText { get; set; } = "";
|
public string PhoneticText { get; set; } = "";
|
||||||
|
|
||||||
public CanvasGeometry? OriginalCanvasGeometry { get; private set; }
|
|
||||||
public CanvasGeometry? TranslatedCanvasGeometry { get; private set; }
|
|
||||||
public CanvasGeometry? PhoneticCanvasGeometry { get; private set; }
|
|
||||||
|
|
||||||
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
|
|
||||||
{
|
|
||||||
if (OriginalCanvasTextLayout == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double centerY = OriginalPosition.Y + (OriginalCanvasTextLayout?.LayoutBounds.Height ?? 0) / 2;
|
|
||||||
|
|
||||||
CenterPosition = type switch
|
|
||||||
{
|
|
||||||
TextAlignmentType.Left => new Vector2(OriginalPosition.X, (float)centerY),
|
|
||||||
TextAlignmentType.Center => new Vector2((float)(OriginalPosition.X + maxWidth / 2.0), (float)centerY),
|
|
||||||
TextAlignmentType.Right => new Vector2((float)(OriginalPosition.X + maxWidth), (float)centerY),
|
|
||||||
_ => throw new System.ArgumentOutOfRangeException(nameof(type), type, null),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DisposeTextLayout()
|
|
||||||
{
|
|
||||||
PhoneticCanvasTextLayout?.Dispose();
|
|
||||||
PhoneticCanvasTextLayout = null;
|
|
||||||
|
|
||||||
OriginalCanvasTextLayout?.Dispose();
|
|
||||||
OriginalCanvasTextLayout = null;
|
|
||||||
|
|
||||||
TranslatedCanvasTextLayout?.Dispose();
|
|
||||||
TranslatedCanvasTextLayout = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecreateTextLayout(
|
|
||||||
ICanvasAnimatedControl control,
|
|
||||||
bool createPhonetic, bool createTranslated,
|
|
||||||
int phoneticTextFontSize, int originalTextFontSize, int translatedTextFontSize,
|
|
||||||
LyricsFontWeight fontWeight,
|
|
||||||
string fontFamilyCJK, string fontFamilyWestern,
|
|
||||||
double maxWidth, double maxHeight, TextAlignmentType type)
|
|
||||||
{
|
|
||||||
DisposeTextLayout();
|
|
||||||
|
|
||||||
if (createPhonetic && PhoneticText != "")
|
|
||||||
{
|
|
||||||
PhoneticCanvasTextLayout = new CanvasTextLayout(control, PhoneticText, new CanvasTextFormat
|
|
||||||
{
|
|
||||||
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
|
||||||
VerticalAlignment = CanvasVerticalAlignment.Top,
|
|
||||||
FontSize = phoneticTextFontSize,
|
|
||||||
FontWeight = fontWeight.ToFontWeight(),
|
|
||||||
}, (float)maxWidth, (float)maxHeight)
|
|
||||||
{
|
|
||||||
HorizontalAlignment = type.ToCanvasHorizontalAlignment(),
|
|
||||||
};
|
|
||||||
PhoneticCanvasTextLayout.SetFontFamily(PhoneticText, fontFamilyCJK, fontFamilyWestern);
|
|
||||||
}
|
|
||||||
|
|
||||||
OriginalCanvasTextLayout = new CanvasTextLayout(control, OriginalText, new CanvasTextFormat
|
|
||||||
{
|
|
||||||
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
|
||||||
VerticalAlignment = CanvasVerticalAlignment.Top,
|
|
||||||
FontSize = originalTextFontSize,
|
|
||||||
FontWeight = fontWeight.ToFontWeight(),
|
|
||||||
}, (float)maxWidth, (float)maxHeight)
|
|
||||||
{
|
|
||||||
HorizontalAlignment = type.ToCanvasHorizontalAlignment()
|
|
||||||
};
|
|
||||||
OriginalCanvasTextLayout.SetFontFamily(OriginalText, fontFamilyCJK, fontFamilyWestern);
|
|
||||||
|
|
||||||
if (createTranslated && TranslatedText != "")
|
|
||||||
{
|
|
||||||
TranslatedCanvasTextLayout = new CanvasTextLayout(control, TranslatedText, new CanvasTextFormat
|
|
||||||
{
|
|
||||||
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
|
||||||
VerticalAlignment = CanvasVerticalAlignment.Top,
|
|
||||||
FontSize = translatedTextFontSize,
|
|
||||||
FontWeight = fontWeight.ToFontWeight(),
|
|
||||||
}, (float)maxWidth, (float)maxHeight)
|
|
||||||
{
|
|
||||||
HorizontalAlignment = type.ToCanvasHorizontalAlignment()
|
|
||||||
};
|
|
||||||
TranslatedCanvasTextLayout.SetFontFamily(TranslatedText, fontFamilyCJK, fontFamilyWestern);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DisposeTextGeometry()
|
|
||||||
{
|
|
||||||
PhoneticCanvasGeometry?.Dispose();
|
|
||||||
PhoneticCanvasGeometry = null;
|
|
||||||
|
|
||||||
OriginalCanvasGeometry?.Dispose();
|
|
||||||
OriginalCanvasGeometry = null;
|
|
||||||
|
|
||||||
TranslatedCanvasGeometry?.Dispose();
|
|
||||||
TranslatedCanvasGeometry = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecreateTextGeometry()
|
|
||||||
{
|
|
||||||
DisposeTextGeometry();
|
|
||||||
|
|
||||||
if (PhoneticCanvasTextLayout != null)
|
|
||||||
{
|
|
||||||
PhoneticCanvasGeometry = CanvasGeometry.CreateText(PhoneticCanvasTextLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OriginalCanvasTextLayout != null)
|
|
||||||
{
|
|
||||||
OriginalCanvasGeometry = CanvasGeometry.CreateText(OriginalCanvasTextLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TranslatedCanvasTextLayout != null)
|
|
||||||
{
|
|
||||||
TranslatedCanvasGeometry = CanvasGeometry.CreateText(TranslatedCanvasTextLayout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
public partial class LyricsSearchProviderInfo : ObservableRecipient
|
public partial class LyricsSearchProviderInfo : ObservableRecipient
|
||||||
{
|
{
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsEnabled { get; set; }
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsEnabled { get; set; }
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider Provider { get; set; }
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider Provider { get; set; }
|
||||||
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsMatchingThresholdOverwritten { get; set; } = false;
|
||||||
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int MatchingThreshold { get; set; } = 0;
|
||||||
|
|
||||||
public LyricsSearchProviderInfo() { }
|
public LyricsSearchProviderInfo() { }
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using NTextCat.Commons;
|
using NTextCat.Commons;
|
||||||
using System;
|
using System;
|
||||||
@@ -10,8 +11,17 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
public LyricsSearchProvider Provider { get; set; }
|
public LyricsSearchProvider Provider { get; set; }
|
||||||
|
|
||||||
public string? Raw { get; set; }
|
public string? Raw { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 翻译也可能位于 <see cref="Raw"/>
|
||||||
|
/// </summary>
|
||||||
public string? Translation { get; set; }
|
public string? Translation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 音译也可能位于 <see cref="Raw"/>
|
||||||
|
/// </summary>
|
||||||
|
public string? Transliteration { get; set; }
|
||||||
|
|
||||||
public string? Title { get; set; }
|
public string? Title { get; set; }
|
||||||
public string[]? Artists { get; set; }
|
public string[]? Artists { get; set; }
|
||||||
public string? Album { get; set; }
|
public string? Album { get; set; }
|
||||||
@@ -19,6 +29,8 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
[ObservableProperty] public partial int MatchPercentage { get; set; } = -1;
|
[ObservableProperty] public partial int MatchPercentage { get; set; } = -1;
|
||||||
[ObservableProperty] public partial string Reference { get; set; } = "about:blank";
|
[ObservableProperty] public partial string Reference { get; set; } = "about:blank";
|
||||||
|
|
||||||
|
public string? SelfPath { get; set; }
|
||||||
|
|
||||||
public bool IsFound => !string.IsNullOrEmpty(Raw);
|
public bool IsFound => !string.IsNullOrEmpty(Raw);
|
||||||
|
|
||||||
public LyricsSearchProvider? ProviderIfFound => IsFound ? Provider : null;
|
public LyricsSearchProvider? ProviderIfFound => IsFound ? Provider : null;
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models
|
namespace BetterLyrics.WinUI3.Models
|
||||||
{
|
{
|
||||||
public class LyricsChar
|
public class LyricsSyllable
|
||||||
{
|
{
|
||||||
public int? EndMs { get; set; }
|
public int? EndMs { get; set; }
|
||||||
public int StartIndex { get; set; }
|
public int StartIndex { get; set; }
|
||||||
public int StartMs { get; set; }
|
public int StartMs { get; set; }
|
||||||
public string Text { get; set; } = string.Empty;
|
public string Text { get; set; } = string.Empty;
|
||||||
|
public int? DurationMs => EndMs - StartMs;
|
||||||
|
public bool IsLongDuration => DurationMs >= 700;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,11 +30,13 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
[ObservableProperty] public partial LyricsStyleSettings LyricsStyleSettings { get; set; } = new();
|
[ObservableProperty] public partial LyricsStyleSettings LyricsStyleSettings { get; set; } = new();
|
||||||
[ObservableProperty] public partial LyricsEffectSettings LyricsEffectSettings { get; set; } = new(500, 500, 500, EasingType.EaseInOutQuad);
|
[ObservableProperty] public partial LyricsEffectSettings LyricsEffectSettings { get; set; } = new(500, 500, 500, EasingType.EaseInOutQuad);
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsBackgroundSettings LyricsBackgroundSettings { get; set; } = new();
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsBackgroundSettings LyricsBackgroundSettings { get; set; } = new();
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial AlbumArtLayoutSettings AlbumArtLayoutSettings { get; set; } = new();
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial AlbumArtAreaStyleSettings AlbumArtLayoutSettings { get; set; } = new();
|
||||||
|
[ObservableProperty] public partial AlbumArtAreaEffectSettings AlbumArtAreaEffectSettings { get; set; } = new();
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAdaptToEnvironment { get; set; } = false;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAdaptToEnvironment { get; set; } = false;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial WindowPixelSampleMode EnvironmentSampleMode { get; set; } = WindowPixelSampleMode.WindowEdge;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial WindowPixelSampleMode EnvironmentSampleMode { get; set; } = WindowPixelSampleMode.WindowEdge;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoShowOrHideWindow { get; set; } = false;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoShowOrHideWindow { get; set; } = false;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TitleBarArea TitleBarArea { get; set; } = TitleBarArea.Top;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TitleBarArea TitleBarArea { get; set; } = TitleBarArea.Top;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowX { get; set; } = 100;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowX { get; set; } = 100;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowY { get; set; } = 100;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowY { get; set; } = 100;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowWidth { get; set; } = 800;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowWidth { get; set; } = 800;
|
||||||
@@ -64,7 +66,7 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
newValue.PropertyChanged += OldLyricsBackgroundSettings_PropertyChanged;
|
newValue.PropertyChanged += OldLyricsBackgroundSettings_PropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnAlbumArtLayoutSettingsChanged(AlbumArtLayoutSettings oldValue, AlbumArtLayoutSettings newValue)
|
partial void OnAlbumArtLayoutSettingsChanged(AlbumArtAreaStyleSettings oldValue, AlbumArtAreaStyleSettings newValue)
|
||||||
{
|
{
|
||||||
oldValue.PropertyChanged -= OldAlbumArtLayoutSettings_PropertyChanged;
|
oldValue.PropertyChanged -= OldAlbumArtLayoutSettings_PropertyChanged;
|
||||||
newValue.PropertyChanged += OldAlbumArtLayoutSettings_PropertyChanged;
|
newValue.PropertyChanged += OldAlbumArtLayoutSettings_PropertyChanged;
|
||||||
@@ -102,7 +104,7 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
|
|
||||||
public void UpdateMonitorNameAndBounds()
|
public void UpdateMonitorNameAndBounds()
|
||||||
{
|
{
|
||||||
var lyricsWindow = WindowHook.GetWindow<LyricsWindow>();
|
var lyricsWindow = WindowHook.GetWindow<NowPlayingWindow>();
|
||||||
if (lyricsWindow == null) return;
|
if (lyricsWindow == null) return;
|
||||||
|
|
||||||
var mointor = MonitorHook.GetMonitorInfoExFromWindow(lyricsWindow);
|
var mointor = MonitorHook.GetMonitorInfoExFromWindow(lyricsWindow);
|
||||||
@@ -183,19 +185,24 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
MonitorBounds = this.MonitorBounds,
|
MonitorBounds = this.MonitorBounds,
|
||||||
DemoMonitorBounds = this.DemoMonitorBounds,
|
DemoMonitorBounds = this.DemoMonitorBounds,
|
||||||
DockPlacement = this.DockPlacement,
|
DockPlacement = this.DockPlacement,
|
||||||
|
|
||||||
LyricsStyleSettings = (LyricsStyleSettings)this.LyricsStyleSettings.Clone(),
|
LyricsStyleSettings = (LyricsStyleSettings)this.LyricsStyleSettings.Clone(),
|
||||||
LyricsEffectSettings = (LyricsEffectSettings)this.LyricsEffectSettings.Clone(),
|
LyricsEffectSettings = (LyricsEffectSettings)this.LyricsEffectSettings.Clone(),
|
||||||
LyricsBackgroundSettings = (LyricsBackgroundSettings)this.LyricsBackgroundSettings.Clone(),
|
LyricsBackgroundSettings = (LyricsBackgroundSettings)this.LyricsBackgroundSettings.Clone(),
|
||||||
AlbumArtLayoutSettings = (AlbumArtLayoutSettings)this.AlbumArtLayoutSettings.Clone(),
|
AlbumArtLayoutSettings = (AlbumArtAreaStyleSettings)this.AlbumArtLayoutSettings.Clone(),
|
||||||
|
AlbumArtAreaEffectSettings = (AlbumArtAreaEffectSettings)this.AlbumArtAreaEffectSettings.Clone(),
|
||||||
|
|
||||||
IsAdaptToEnvironment = this.IsAdaptToEnvironment,
|
IsAdaptToEnvironment = this.IsAdaptToEnvironment,
|
||||||
EnvironmentSampleMode = this.EnvironmentSampleMode,
|
EnvironmentSampleMode = this.EnvironmentSampleMode,
|
||||||
AutoShowOrHideWindow = this.AutoShowOrHideWindow,
|
AutoShowOrHideWindow = this.AutoShowOrHideWindow,
|
||||||
TitleBarArea = this.TitleBarArea,
|
TitleBarArea = this.TitleBarArea,
|
||||||
|
|
||||||
WindowX = this.WindowX,
|
WindowX = this.WindowX,
|
||||||
WindowY = this.WindowY,
|
WindowY = this.WindowY,
|
||||||
WindowWidth = this.WindowWidth,
|
WindowWidth = this.WindowWidth,
|
||||||
WindowHeight = this.WindowHeight,
|
WindowHeight = this.WindowHeight,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
// 2025/6/23 by Zhe Fang
|
// 2025/6/23 by Zhe Fang
|
||||||
|
|
||||||
using BetterLyrics.WinUI3.Collections;
|
using BetterLyrics.WinUI3.Collections;
|
||||||
using BetterLyrics.WinUI3.Constants;
|
|
||||||
using BetterLyrics.WinUI3.Enums;
|
using BetterLyrics.WinUI3.Enums;
|
||||||
using BetterLyrics.WinUI3.Helper;
|
using BetterLyrics.WinUI3.Helper;
|
||||||
using BetterLyrics.WinUI3.Hooks;
|
|
||||||
using BetterLyrics.WinUI3.ViewModels;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
|
||||||
using Microsoft.UI.Xaml.Media.Imaging;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models
|
namespace BetterLyrics.WinUI3.Models
|
||||||
{
|
{
|
||||||
@@ -38,6 +32,7 @@ namespace BetterLyrics.WinUI3.Models
|
|||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; } = [.. Enum.GetValues<AlbumArtSearchProvider>().Select(p => new AlbumArtSearchProviderInfo(p, true))];
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; } = [.. Enum.GetValues<AlbumArtSearchProvider>().Select(p => new AlbumArtSearchProviderInfo(p, true))];
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchType LyricsSearchType { get; set; } = LyricsSearchType.Sequential;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchType LyricsSearchType { get; set; } = LyricsSearchType.Sequential;
|
||||||
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int MatchingThreshold { get; set; } = 0;
|
||||||
|
|
||||||
public string LogoPath => PlayerIDHelper.GetLogoPath(Provider);
|
public string LogoPath => PlayerIDHelper.GetLogoPath(Provider);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using Microsoft.Graphics.Canvas.Geometry;
|
||||||
|
using Microsoft.Graphics.Canvas.Text;
|
||||||
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
|
using Microsoft.UI;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using Windows.UI;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Models
|
||||||
|
{
|
||||||
|
public class RenderLyricsLine : LyricsLine
|
||||||
|
{
|
||||||
|
public double AnimationDuration { get; set; } = 0.3;
|
||||||
|
public ValueTransition<double> AngleTransition { get; set; }
|
||||||
|
public ValueTransition<double> BlurAmountTransition { get; set; }
|
||||||
|
public ValueTransition<double> PhoneticOpacityTransition { get; set; }
|
||||||
|
public ValueTransition<double> PlayedOriginalOpacityTransition { get; set; }
|
||||||
|
public ValueTransition<double> UnplayedOriginalOpacityTransition { get; set; }
|
||||||
|
public ValueTransition<double> TranslatedOpacityTransition { get; set; }
|
||||||
|
public ValueTransition<double> ScaleTransition { get; set; }
|
||||||
|
public ValueTransition<double> YOffsetTransition { get; set; }
|
||||||
|
public ValueTransition<Color> ColorTransition { get; set; }
|
||||||
|
|
||||||
|
public CanvasTextLayout? OriginalCanvasTextLayout { get; private set; }
|
||||||
|
public CanvasTextLayout? TranslatedCanvasTextLayout { get; private set; }
|
||||||
|
public CanvasTextLayout? PhoneticCanvasTextLayout { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 原文坐标(相对于坐标原点)
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 OriginalPosition { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 译文坐标(相对于坐标原点)
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 TranslatedPosition { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 注音坐标(相对于坐标原点)
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 PhoneticPosition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 顶部坐标(相对于坐标原点)
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 TopLeftPosition { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 中心坐标(相对于坐标原点)
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 CenterPosition { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 底部坐标(相对于坐标原点)
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 BottomRightPosition { get; set; }
|
||||||
|
|
||||||
|
public CanvasGeometry? OriginalCanvasGeometry { get; private set; }
|
||||||
|
public CanvasGeometry? TranslatedCanvasGeometry { get; private set; }
|
||||||
|
public CanvasGeometry? PhoneticCanvasGeometry { get; private set; }
|
||||||
|
|
||||||
|
public RenderLyricsLine()
|
||||||
|
{
|
||||||
|
AngleTransition = new(
|
||||||
|
initialValue: 0,
|
||||||
|
durationSeconds: AnimationDuration,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
BlurAmountTransition = new(
|
||||||
|
initialValue: 0,
|
||||||
|
durationSeconds: AnimationDuration,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
PhoneticOpacityTransition = new(
|
||||||
|
initialValue: 0,
|
||||||
|
durationSeconds: AnimationDuration,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
PlayedOriginalOpacityTransition = new(
|
||||||
|
initialValue: 0,
|
||||||
|
durationSeconds: AnimationDuration,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
UnplayedOriginalOpacityTransition = new(
|
||||||
|
initialValue: 0,
|
||||||
|
durationSeconds: AnimationDuration,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
TranslatedOpacityTransition = new(
|
||||||
|
initialValue: 0,
|
||||||
|
durationSeconds: AnimationDuration,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
ScaleTransition = new(
|
||||||
|
initialValue: 0,
|
||||||
|
durationSeconds: AnimationDuration,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
YOffsetTransition = new(
|
||||||
|
initialValue: 0,
|
||||||
|
durationSeconds: AnimationDuration,
|
||||||
|
easingType: EasingType.EaseInOutSine
|
||||||
|
);
|
||||||
|
ColorTransition = new(
|
||||||
|
initialValue: Colors.Transparent,
|
||||||
|
durationSeconds: 0.3f,
|
||||||
|
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
|
||||||
|
{
|
||||||
|
if (OriginalCanvasTextLayout == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double centerY = (TopLeftPosition.Y + BottomRightPosition.Y) / 2;
|
||||||
|
|
||||||
|
CenterPosition = type switch
|
||||||
|
{
|
||||||
|
TextAlignmentType.Left => new Vector2(0, (float)centerY),
|
||||||
|
TextAlignmentType.Center => new Vector2((float)(0 + maxWidth / 2.0), (float)centerY),
|
||||||
|
TextAlignmentType.Right => new Vector2((float)(0 + maxWidth), (float)centerY),
|
||||||
|
_ => throw new System.ArgumentOutOfRangeException(nameof(type), type, null),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisposeTextLayout()
|
||||||
|
{
|
||||||
|
PhoneticCanvasTextLayout?.Dispose();
|
||||||
|
PhoneticCanvasTextLayout = null;
|
||||||
|
|
||||||
|
OriginalCanvasTextLayout?.Dispose();
|
||||||
|
OriginalCanvasTextLayout = null;
|
||||||
|
|
||||||
|
TranslatedCanvasTextLayout?.Dispose();
|
||||||
|
TranslatedCanvasTextLayout = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecreateTextLayout(
|
||||||
|
ICanvasAnimatedControl control,
|
||||||
|
bool createPhonetic, bool createTranslated,
|
||||||
|
int phoneticTextFontSize, int originalTextFontSize, int translatedTextFontSize,
|
||||||
|
LyricsFontWeight fontWeight,
|
||||||
|
string fontFamilyCJK, string fontFamilyWestern,
|
||||||
|
double maxWidth, double maxHeight, TextAlignmentType type)
|
||||||
|
{
|
||||||
|
DisposeTextLayout();
|
||||||
|
|
||||||
|
if (createPhonetic && PhoneticText != "")
|
||||||
|
{
|
||||||
|
PhoneticCanvasTextLayout = new CanvasTextLayout(control, PhoneticText, new CanvasTextFormat
|
||||||
|
{
|
||||||
|
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = CanvasVerticalAlignment.Top,
|
||||||
|
FontSize = phoneticTextFontSize,
|
||||||
|
FontWeight = fontWeight.ToFontWeight(),
|
||||||
|
}, (float)maxWidth, (float)maxHeight)
|
||||||
|
{
|
||||||
|
HorizontalAlignment = type.ToCanvasHorizontalAlignment(),
|
||||||
|
};
|
||||||
|
PhoneticCanvasTextLayout.SetFontFamily(PhoneticText, fontFamilyCJK, fontFamilyWestern);
|
||||||
|
}
|
||||||
|
|
||||||
|
OriginalCanvasTextLayout = new CanvasTextLayout(control, OriginalText, new CanvasTextFormat
|
||||||
|
{
|
||||||
|
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = CanvasVerticalAlignment.Top,
|
||||||
|
FontSize = originalTextFontSize,
|
||||||
|
FontWeight = fontWeight.ToFontWeight(),
|
||||||
|
}, (float)maxWidth, (float)maxHeight)
|
||||||
|
{
|
||||||
|
HorizontalAlignment = type.ToCanvasHorizontalAlignment()
|
||||||
|
};
|
||||||
|
OriginalCanvasTextLayout.SetFontFamily(OriginalText, fontFamilyCJK, fontFamilyWestern);
|
||||||
|
|
||||||
|
if (createTranslated && TranslatedText != "")
|
||||||
|
{
|
||||||
|
TranslatedCanvasTextLayout = new CanvasTextLayout(control, TranslatedText, new CanvasTextFormat
|
||||||
|
{
|
||||||
|
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = CanvasVerticalAlignment.Top,
|
||||||
|
FontSize = translatedTextFontSize,
|
||||||
|
FontWeight = fontWeight.ToFontWeight(),
|
||||||
|
}, (float)maxWidth, (float)maxHeight)
|
||||||
|
{
|
||||||
|
HorizontalAlignment = type.ToCanvasHorizontalAlignment()
|
||||||
|
};
|
||||||
|
TranslatedCanvasTextLayout.SetFontFamily(TranslatedText, fontFamilyCJK, fontFamilyWestern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisposeTextGeometry()
|
||||||
|
{
|
||||||
|
PhoneticCanvasGeometry?.Dispose();
|
||||||
|
PhoneticCanvasGeometry = null;
|
||||||
|
|
||||||
|
OriginalCanvasGeometry?.Dispose();
|
||||||
|
OriginalCanvasGeometry = null;
|
||||||
|
|
||||||
|
TranslatedCanvasGeometry?.Dispose();
|
||||||
|
TranslatedCanvasGeometry = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecreateTextGeometry()
|
||||||
|
{
|
||||||
|
DisposeTextGeometry();
|
||||||
|
|
||||||
|
if (PhoneticCanvasTextLayout != null)
|
||||||
|
{
|
||||||
|
PhoneticCanvasGeometry = CanvasGeometry.CreateText(PhoneticCanvasTextLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OriginalCanvasTextLayout != null)
|
||||||
|
{
|
||||||
|
OriginalCanvasGeometry = CanvasGeometry.CreateText(OriginalCanvasTextLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TranslatedCanvasTextLayout != null)
|
||||||
|
{
|
||||||
|
TranslatedCanvasGeometry = CanvasGeometry.CreateText(TranslatedCanvasTextLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Models.Settings
|
||||||
|
{
|
||||||
|
public partial class AlbumArtAreaEffectSettings : ObservableRecipient, ICloneable
|
||||||
|
{
|
||||||
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial ImageSwitchType ImageSwitchType { get; set; } = ImageSwitchType.Slide;
|
||||||
|
|
||||||
|
public object Clone()
|
||||||
|
{
|
||||||
|
return new AlbumArtAreaEffectSettings()
|
||||||
|
{
|
||||||
|
ImageSwitchType = this.ImageSwitchType,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,35 +4,39 @@ using System;
|
|||||||
|
|
||||||
namespace BetterLyrics.WinUI3.Models.Settings
|
namespace BetterLyrics.WinUI3.Models.Settings
|
||||||
{
|
{
|
||||||
public partial class AlbumArtLayoutSettings : ObservableRecipient, ICloneable
|
public partial class AlbumArtAreaStyleSettings : ObservableRecipient, ICloneable
|
||||||
{
|
{
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType SongInfoAlignmentType { get; set; } = TextAlignmentType.Left;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType SongInfoAlignmentType { get; set; } = TextAlignmentType.Left;
|
||||||
|
|
||||||
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAutoCoverImageHeight { get; set; } = true;
|
||||||
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageHeight { get; set; } = 128;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageRadius { get; set; } = 12; // 12 % of the cover image size
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageRadius { get; set; } = 12; // 12 % of the cover image size
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageShadowAmount { get; set; } = 12;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageShadowAmount { get; set; } = 12;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAutoSongInfoFontSize { get; set; } = true;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAutoSongInfoFontSize { get; set; } = true;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SongInfoFontSize { get; set; } = 18;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SongInfoFontSize { get; set; } = 18;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowTitle { get; set; } = true;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowTitle { get; set; } = true;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowArtists { get; set; } = true;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowArtists { get; set; } = true;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowAlbum { get; set; } = false;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowAlbum { get; set; } = false;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int AlbumArtSize { get; set; } = 64;
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoAlbumArtSize { get; set; } = true;
|
|
||||||
|
|
||||||
public AlbumArtLayoutSettings() { }
|
public AlbumArtAreaStyleSettings() { }
|
||||||
|
|
||||||
public object Clone()
|
public object Clone()
|
||||||
{
|
{
|
||||||
return new AlbumArtLayoutSettings
|
return new AlbumArtAreaStyleSettings
|
||||||
{
|
{
|
||||||
SongInfoAlignmentType = this.SongInfoAlignmentType,
|
SongInfoAlignmentType = this.SongInfoAlignmentType,
|
||||||
|
|
||||||
|
IsAutoCoverImageHeight = this.IsAutoCoverImageHeight,
|
||||||
|
CoverImageHeight = this.CoverImageHeight,
|
||||||
CoverImageRadius = this.CoverImageRadius,
|
CoverImageRadius = this.CoverImageRadius,
|
||||||
CoverImageShadowAmount = this.CoverImageShadowAmount,
|
CoverImageShadowAmount = this.CoverImageShadowAmount,
|
||||||
|
|
||||||
IsAutoSongInfoFontSize = this.IsAutoSongInfoFontSize,
|
IsAutoSongInfoFontSize = this.IsAutoSongInfoFontSize,
|
||||||
SongInfoFontSize = this.SongInfoFontSize,
|
SongInfoFontSize = this.SongInfoFontSize,
|
||||||
ShowTitle = this.ShowTitle,
|
ShowTitle = this.ShowTitle,
|
||||||
ShowArtists = this.ShowArtists,
|
ShowArtists = this.ShowArtists,
|
||||||
ShowAlbum = this.ShowAlbum,
|
ShowAlbum = this.ShowAlbum,
|
||||||
AlbumArtSize = this.AlbumArtSize,
|
|
||||||
AutoAlbumArtSize = this.AutoAlbumArtSize,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,18 +12,14 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
|||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsPureColorOverlayEnabled { get; set; } = false;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsPureColorOverlayEnabled { get; set; } = false;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PureColorOverlayOpacity { get; set; } = 100; // 100 % = 1.0
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PureColorOverlayOpacity { get; set; } = 100; // 100 % = 1.0
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsCoverOverlayEnabled { get; set; } = false;
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverOverlayBlurAmount { get; set; } = 100; // 100 % of the cover image size
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverOverlayOpacity { get; set; } = 100; // 100 % = 1.0
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverOverlaySpeed { get; set; } = 50; // 50 % of the base rotate speed
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverAcrylicEffectAmount { get; set; } = 0;
|
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsFluidOverlayEnabled { get; set; } = true;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsFluidOverlayEnabled { get; set; } = true;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int FluidOverlayOpacity { get; set; } = 100;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int FluidOverlayOpacity { get; set; } = 100;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial PaletteGeneratorType PaletteGeneratorType { get; set; } = PaletteGeneratorType.MedianCut;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial PaletteGeneratorType PaletteGeneratorType { get; set; } = PaletteGeneratorType.MedianCut;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsSpectrumOverlayEnabled { get; set; } = false;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsSpectrumOverlayEnabled { get; set; } = false;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial SpectrumPlacement SpectrumPlacement { get; set; } = SpectrumPlacement.Bottom;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial SpectrumPlacement SpectrumPlacement { get; set; } = SpectrumPlacement.Bottom;
|
||||||
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial SpectrumStyle SpectrumStyle { get; set; } = SpectrumStyle.Bar;
|
||||||
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SpectrumCount { get; set; } = 128;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsSnowFlakeOverlayEnabled { get; set; } = false;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsSnowFlakeOverlayEnabled { get; set; } = false;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SnowFlakeOverlayAmount { get; set; } = 10;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SnowFlakeOverlayAmount { get; set; } = 10;
|
||||||
@@ -43,12 +39,6 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
|||||||
IsPureColorOverlayEnabled = this.IsPureColorOverlayEnabled,
|
IsPureColorOverlayEnabled = this.IsPureColorOverlayEnabled,
|
||||||
PureColorOverlayOpacity = this.PureColorOverlayOpacity,
|
PureColorOverlayOpacity = this.PureColorOverlayOpacity,
|
||||||
|
|
||||||
IsCoverOverlayEnabled = this.IsCoverOverlayEnabled,
|
|
||||||
CoverOverlayBlurAmount = this.CoverOverlayBlurAmount,
|
|
||||||
CoverOverlayOpacity = this.CoverOverlayOpacity,
|
|
||||||
CoverOverlaySpeed = this.CoverOverlaySpeed,
|
|
||||||
CoverAcrylicEffectAmount = this.CoverAcrylicEffectAmount,
|
|
||||||
|
|
||||||
IsFluidOverlayEnabled = this.IsFluidOverlayEnabled,
|
IsFluidOverlayEnabled = this.IsFluidOverlayEnabled,
|
||||||
FluidOverlayOpacity = this.FluidOverlayOpacity,
|
FluidOverlayOpacity = this.FluidOverlayOpacity,
|
||||||
PaletteGeneratorType = this.PaletteGeneratorType,
|
PaletteGeneratorType = this.PaletteGeneratorType,
|
||||||
|
|||||||
@@ -6,25 +6,10 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
|||||||
{
|
{
|
||||||
public partial class LyricsEffectSettings : ObservableRecipient, ICloneable
|
public partial class LyricsEffectSettings : ObservableRecipient, ICloneable
|
||||||
{
|
{
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsBlurAmount { get; set; } = 5;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsBlurEffectEnabled { get; set; } = true;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsLineFadeEnabled { get; set; } = true;
|
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsGlowEffectEnabled { get; set; } = true;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsGlowEffectEnabled { get; set; } = true;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LineRenderingType LyricsGlowEffectScope { get; set; } = LineRenderingType.CurrentChar;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsScaleEffectEnabled { get; set; } = true;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsGlowEffectAmount { get; set; } = 8;
|
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsShadowEnabled { get; set; } = false;
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LineRenderingType LyricsShadowScope { get; set; } = LineRenderingType.LineStartToCurrentChar;
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsShadowAmount { get; set; } = 8;
|
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LineRenderingType OriginalLyricsHighlightScope { get; set; } = LineRenderingType.LineStartToCurrentChar;
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PhoneticLyricsHighlightAmount { get; set; } = 60; // 100% 是上界
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int OriginalLyricsHighlightAmount { get; set; } = 100; // 100% 是上界
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int TranslatedLyricsHighlightAmount { get; set; } = 60; // 100% 是上界
|
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationEnabled { get; set; } = true;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationEnabled { get; set; } = true;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFloatAmount { get; set; } = 1;
|
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial EasingType LyricsScrollEasingType { get; set; }
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial EasingType LyricsScrollEasingType { get; set; }
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollDuration { get; set; }
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollDuration { get; set; }
|
||||||
@@ -33,8 +18,6 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
|||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollTopDelay { get; set; } = 0;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollTopDelay { get; set; } = 0;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollBottomDelay { get; set; } = 0;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollBottomDelay { get; set; } = 0;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsVerticalEdgeOpacity { get; set; } = 0; // 0% opacity
|
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsFanLyricsEnabled { get; set; } = false;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsFanLyricsEnabled { get; set; } = false;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int FanLyricsAngle { get; set; } = 30;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int FanLyricsAngle { get; set; } = 30;
|
||||||
|
|
||||||
@@ -56,25 +39,10 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
|||||||
{
|
{
|
||||||
return new LyricsEffectSettings(this.LyricsScrollTopDuration, this.LyricsScrollDuration, this.LyricsScrollBottomDuration, this.LyricsScrollEasingType)
|
return new LyricsEffectSettings(this.LyricsScrollTopDuration, this.LyricsScrollDuration, this.LyricsScrollBottomDuration, this.LyricsScrollEasingType)
|
||||||
{
|
{
|
||||||
LyricsBlurAmount = this.LyricsBlurAmount,
|
IsLyricsBlurEffectEnabled = this.IsLyricsBlurEffectEnabled,
|
||||||
|
|
||||||
IsLyricsLineFadeEnabled = this.IsLyricsLineFadeEnabled,
|
|
||||||
|
|
||||||
IsLyricsGlowEffectEnabled = this.IsLyricsGlowEffectEnabled,
|
IsLyricsGlowEffectEnabled = this.IsLyricsGlowEffectEnabled,
|
||||||
LyricsGlowEffectScope = this.LyricsGlowEffectScope,
|
IsLyricsScaleEffectEnabled = this.IsLyricsScaleEffectEnabled,
|
||||||
LyricsGlowEffectAmount = this.LyricsGlowEffectAmount,
|
|
||||||
|
|
||||||
IsLyricsShadowEnabled = this.IsLyricsShadowEnabled,
|
|
||||||
LyricsShadowScope = this.LyricsShadowScope,
|
|
||||||
LyricsShadowAmount = this.LyricsShadowAmount,
|
|
||||||
|
|
||||||
OriginalLyricsHighlightScope = this.OriginalLyricsHighlightScope,
|
|
||||||
PhoneticLyricsHighlightAmount = this.PhoneticLyricsHighlightAmount,
|
|
||||||
OriginalLyricsHighlightAmount = this.OriginalLyricsHighlightAmount,
|
|
||||||
TranslatedLyricsHighlightAmount = this.TranslatedLyricsHighlightAmount,
|
|
||||||
|
|
||||||
IsLyricsFloatAnimationEnabled = this.IsLyricsFloatAnimationEnabled,
|
IsLyricsFloatAnimationEnabled = this.IsLyricsFloatAnimationEnabled,
|
||||||
LyricsFloatAmount = this.LyricsFloatAmount,
|
|
||||||
|
|
||||||
LyricsScrollEasingType = this.LyricsScrollEasingType,
|
LyricsScrollEasingType = this.LyricsScrollEasingType,
|
||||||
LyricsScrollDuration = this.LyricsScrollDuration,
|
LyricsScrollDuration = this.LyricsScrollDuration,
|
||||||
@@ -83,8 +51,6 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
|||||||
LyricsScrollTopDelay = this.LyricsScrollTopDelay,
|
LyricsScrollTopDelay = this.LyricsScrollTopDelay,
|
||||||
LyricsScrollBottomDelay = this.LyricsScrollBottomDelay,
|
LyricsScrollBottomDelay = this.LyricsScrollBottomDelay,
|
||||||
|
|
||||||
LyricsVerticalEdgeOpacity = this.LyricsVerticalEdgeOpacity,
|
|
||||||
|
|
||||||
IsFanLyricsEnabled = this.IsFanLyricsEnabled,
|
IsFanLyricsEnabled = this.IsFanLyricsEnabled,
|
||||||
FanLyricsAngle = this.FanLyricsAngle,
|
FanLyricsAngle = this.FanLyricsAngle,
|
||||||
|
|
||||||
|
|||||||
@@ -14,21 +14,27 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
|||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PhoneticLyricsFontSize { get; set; } = 12;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PhoneticLyricsFontSize { get; set; } = 12;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int OriginalLyricsFontSize { get; set; } = 24;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int OriginalLyricsFontSize { get; set; } = 24;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int TranslatedLyricsFontSize { get; set; } = 12;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int TranslatedLyricsFontSize { get; set; } = 12;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType LyricsAlignmentType { get; set; } = TextAlignmentType.Left;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType LyricsAlignmentType { get; set; } = TextAlignmentType.Left;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsBgFontOpacity { get; set; } = 30; // 30% opacity
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFontStrokeWidth { get; set; } = 0;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFontStrokeWidth { get; set; } = 0;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomBgFontColor { get; set; } = Colors.White;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomBgFontColor { get; set; } = Colors.White;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomFgFontColor { get; set; } = Colors.White;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomFgFontColor { get; set; } = Colors.White;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomStrokeFontColor { get; set; } = Colors.White;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomStrokeFontColor { get; set; } = Colors.White;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsBgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsBgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsFgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsFgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsStrokeFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsStrokeFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontWeight LyricsFontWeight { get; set; } = LyricsFontWeight.Bold;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontWeight LyricsFontWeight { get; set; } = LyricsFontWeight.Bold;
|
||||||
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double LyricsLineSpacingFactor { get; set; } = 0.5;
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double LyricsLineSpacingFactor { get; set; } = 0.5;
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsTranslationSeparator { get; set; } = StringHelper.NewLine;
|
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsCJKFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsCJKFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
|
||||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsWesternFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsWesternFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
|
||||||
|
|
||||||
|
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PlayingLineTopOffset { get; set; } = 50; // 50 %
|
||||||
|
|
||||||
public LyricsStyleSettings() { }
|
public LyricsStyleSettings() { }
|
||||||
|
|
||||||
public object Clone()
|
public object Clone()
|
||||||
@@ -40,7 +46,6 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
|||||||
OriginalLyricsFontSize = this.OriginalLyricsFontSize,
|
OriginalLyricsFontSize = this.OriginalLyricsFontSize,
|
||||||
TranslatedLyricsFontSize = this.TranslatedLyricsFontSize,
|
TranslatedLyricsFontSize = this.TranslatedLyricsFontSize,
|
||||||
LyricsAlignmentType = this.LyricsAlignmentType,
|
LyricsAlignmentType = this.LyricsAlignmentType,
|
||||||
LyricsBgFontOpacity = this.LyricsBgFontOpacity,
|
|
||||||
LyricsFontStrokeWidth = this.LyricsFontStrokeWidth,
|
LyricsFontStrokeWidth = this.LyricsFontStrokeWidth,
|
||||||
LyricsCustomBgFontColor = this.LyricsCustomBgFontColor,
|
LyricsCustomBgFontColor = this.LyricsCustomBgFontColor,
|
||||||
LyricsCustomFgFontColor = this.LyricsCustomFgFontColor,
|
LyricsCustomFgFontColor = this.LyricsCustomFgFontColor,
|
||||||
@@ -50,9 +55,10 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
|||||||
LyricsStrokeFontColorType = this.LyricsStrokeFontColorType,
|
LyricsStrokeFontColorType = this.LyricsStrokeFontColorType,
|
||||||
LyricsFontWeight = this.LyricsFontWeight,
|
LyricsFontWeight = this.LyricsFontWeight,
|
||||||
LyricsLineSpacingFactor = this.LyricsLineSpacingFactor,
|
LyricsLineSpacingFactor = this.LyricsLineSpacingFactor,
|
||||||
LyricsTranslationSeparator = this.LyricsTranslationSeparator,
|
|
||||||
LyricsCJKFontFamily = this.LyricsCJKFontFamily,
|
LyricsCJKFontFamily = this.LyricsCJKFontFamily,
|
||||||
LyricsWesternFontFamily = this.LyricsWesternFontFamily,
|
LyricsWesternFontFamily = this.LyricsWesternFontFamily,
|
||||||
|
|
||||||
|
PlayingLineTopOffset = this.PlayingLineTopOffset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
||||||
|
{
|
||||||
|
public partial class LyricsParser
|
||||||
|
{
|
||||||
|
[GeneratedRegex(@"\[(\d*):(\d*)(\.|\:)(\d*)\]")]
|
||||||
|
private static partial Regex LrcRegex();
|
||||||
|
[GeneratedRegex(@"(\[|\<)(\d*):(\d*)\.(\d*)(\]|\>)([^\[\]\<\>]*)")]
|
||||||
|
private static partial Regex SyllableRegex();
|
||||||
|
|
||||||
|
private void ParseLrc(string raw, bool single)
|
||||||
|
{
|
||||||
|
var lines = raw.Split(["\r\n", "\n"], StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var lrcLines = new List<LyricsLine>();
|
||||||
|
|
||||||
|
// 支持 [mm:ss.xx]字、<mm:ss.xx>字,毫秒两位或三位
|
||||||
|
var syllableRegex = SyllableRegex();
|
||||||
|
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
var matches = syllableRegex.Matches(line);
|
||||||
|
var syllables = new List<LyricsSyllable>();
|
||||||
|
|
||||||
|
int startIndex = 0;
|
||||||
|
for (int i = 0; i < matches.Count; i++)
|
||||||
|
{
|
||||||
|
var match = matches[i];
|
||||||
|
int min = int.Parse(match.Groups[2].Value);
|
||||||
|
int sec = int.Parse(match.Groups[3].Value);
|
||||||
|
int ms = int.Parse(match.Groups[4].Value.PadRight(3, '0'));
|
||||||
|
int totalMs = min * 60_000 + sec * 1000 + ms;
|
||||||
|
string text = match.Groups[6].Value;
|
||||||
|
|
||||||
|
syllables.Add(new LyricsSyllable { StartMs = totalMs, Text = text, StartIndex = startIndex });
|
||||||
|
startIndex += text.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syllables.Count > 1)
|
||||||
|
{
|
||||||
|
lrcLines.Add(new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = syllables[0].StartMs,
|
||||||
|
OriginalText = string.Concat(syllables.Select(s => s.Text)),
|
||||||
|
LyricsSyllables = syllables
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 普通LRC行
|
||||||
|
Regex? bracketRegex = LrcRegex();
|
||||||
|
var bracketMatches = bracketRegex.Matches(line);
|
||||||
|
|
||||||
|
string content = line;
|
||||||
|
int? lineStartTime = null;
|
||||||
|
if (bracketMatches.Count > 0)
|
||||||
|
{
|
||||||
|
var match = bracketMatches[0];
|
||||||
|
int min = int.Parse(match.Groups[1].Value);
|
||||||
|
int sec = int.Parse(match.Groups[2].Value);
|
||||||
|
int ms = int.Parse(match.Groups[4].Value.PadRight(3, '0'));
|
||||||
|
lineStartTime = min * 60_000 + sec * 1000 + ms;
|
||||||
|
content = bracketRegex!.Replace(line, "").Trim();
|
||||||
|
if (content == "//") content = "";
|
||||||
|
lrcLines.Add(new LyricsLine { StartMs = lineStartTime.Value, OriginalText = content });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (single)
|
||||||
|
{
|
||||||
|
LyricsDataArr.Add(new LyricsData(lrcLines));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 按时间分组
|
||||||
|
var grouped = lrcLines.GroupBy(l => l.StartMs).OrderBy(g => g.Key).ToList();
|
||||||
|
int languageCount = 0;
|
||||||
|
if (grouped != null && grouped.Count > 0)
|
||||||
|
{
|
||||||
|
// 计算最大语言数量
|
||||||
|
languageCount = grouped.Max(g => g.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化每种语言的歌词列表
|
||||||
|
int langStartIndex = LyricsDataArr.Count;
|
||||||
|
for (int i = 0; i < languageCount; i++) LyricsDataArr.Add(new LyricsData());
|
||||||
|
|
||||||
|
// 遍历每个时间分组
|
||||||
|
if (grouped != null)
|
||||||
|
{
|
||||||
|
foreach (var group in grouped)
|
||||||
|
{
|
||||||
|
var linesInGroup = group.ToList();
|
||||||
|
for (int langIdx = 0; langIdx < languageCount; langIdx++)
|
||||||
|
{
|
||||||
|
// 只添加有对应行的语言,否则跳过
|
||||||
|
if (langIdx < linesInGroup.Count)
|
||||||
|
{
|
||||||
|
var lyricsLine = linesInGroup[langIdx];
|
||||||
|
LyricsDataArr[langStartIndex + langIdx].LyricsLines.Add(lyricsLine);
|
||||||
|
}
|
||||||
|
// 没有翻译行则不补原文,直接跳过
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
||||||
|
{
|
||||||
|
public partial class LyricsParser
|
||||||
|
{
|
||||||
|
private void ParseQrcKrc(List<Lyricify.Lyrics.Models.ILineInfo>? lines)
|
||||||
|
{
|
||||||
|
lines = lines?.Where(x => x.Text != string.Empty).ToList();
|
||||||
|
List<LyricsLine> lyricsLines = [];
|
||||||
|
|
||||||
|
if (lines != null && lines.Count > 0)
|
||||||
|
{
|
||||||
|
lyricsLines = [];
|
||||||
|
for (int lineIndex = 0; lineIndex < lines.Count; lineIndex++)
|
||||||
|
{
|
||||||
|
var lineRead = lines[lineIndex];
|
||||||
|
var lineWrite = new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = lineRead.StartTime ?? 0,
|
||||||
|
EndMs = lineRead.EndTime ?? 0,
|
||||||
|
OriginalText = lineRead.Text,
|
||||||
|
LyricsSyllables = [],
|
||||||
|
};
|
||||||
|
|
||||||
|
var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables;
|
||||||
|
if (syllables != null)
|
||||||
|
{
|
||||||
|
int startIndex = 0;
|
||||||
|
for (
|
||||||
|
int syllableIndex = 0;
|
||||||
|
syllableIndex < syllables.Count;
|
||||||
|
syllableIndex++
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var syllable = syllables[syllableIndex];
|
||||||
|
var charTiming = new LyricsSyllable
|
||||||
|
{
|
||||||
|
StartMs = syllable.StartTime,
|
||||||
|
EndMs = syllable.EndTime,
|
||||||
|
Text = syllable.Text,
|
||||||
|
StartIndex = startIndex,
|
||||||
|
};
|
||||||
|
lineWrite.LyricsSyllables.Add(charTiming);
|
||||||
|
startIndex += syllable.Text.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lyricsLines.Add(lineWrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LyricsDataArr.Add(new LyricsData(lyricsLines));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
||||||
|
{
|
||||||
|
public partial class LyricsParser
|
||||||
|
{
|
||||||
|
private void ParseTtml(string raw)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<LyricsLine> originalLines = [];
|
||||||
|
List<LyricsLine> translationLines = [];
|
||||||
|
List<LyricsLine> romanLines = [];
|
||||||
|
|
||||||
|
var xdoc = XDocument.Parse(raw, LoadOptions.PreserveWhitespace);
|
||||||
|
var body = xdoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "body");
|
||||||
|
if (body == null) return;
|
||||||
|
var ps = body.Descendants().Where(e => e.Name.LocalName == "p");
|
||||||
|
foreach (var p in ps)
|
||||||
|
{
|
||||||
|
// 句级时间
|
||||||
|
string? pBegin = p.Attribute("begin")?.Value;
|
||||||
|
string? pEnd = p.Attribute("end")?.Value;
|
||||||
|
int pStartMs = ParseTtmlTime(pBegin);
|
||||||
|
int pEndMs = ParseTtmlTime(pEnd);
|
||||||
|
|
||||||
|
// 只获取一级span
|
||||||
|
var spans = p.Elements()
|
||||||
|
.Where(s => s.Name.LocalName == "span")
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var originalTextSpans = spans
|
||||||
|
.Where(s => s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value == null)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 处理原文span后的空白
|
||||||
|
for (int i = 0; i < originalTextSpans.Count; i++)
|
||||||
|
{
|
||||||
|
var span = originalTextSpans[i];
|
||||||
|
var nextNode = span.NodesAfterSelf().FirstOrDefault();
|
||||||
|
if (nextNode is XText textNode)
|
||||||
|
{
|
||||||
|
span.Value += textNode.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 拼接空白字符后的原文
|
||||||
|
string originalText = string.Concat(originalTextSpans.Select(s => s.Value));
|
||||||
|
|
||||||
|
var originalCharTimings = new List<LyricsSyllable>();
|
||||||
|
int originalStartIndex = 0;
|
||||||
|
foreach (var span in originalTextSpans)
|
||||||
|
{
|
||||||
|
string? sBegin = span.Attribute("begin")?.Value;
|
||||||
|
string? sEnd = span.Attribute("end")?.Value;
|
||||||
|
int sStartMs = ParseTtmlTime(sBegin);
|
||||||
|
int sEndMs = ParseTtmlTime(sEnd);
|
||||||
|
originalCharTimings.Add(new LyricsSyllable
|
||||||
|
{
|
||||||
|
StartMs = sStartMs,
|
||||||
|
EndMs = sEndMs,
|
||||||
|
StartIndex = originalStartIndex,
|
||||||
|
Text = span.Value
|
||||||
|
});
|
||||||
|
originalStartIndex += span.Value.Length;
|
||||||
|
}
|
||||||
|
if (originalTextSpans.Count == 0)
|
||||||
|
{
|
||||||
|
originalText = p.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
originalLines.Add(new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = pStartMs,
|
||||||
|
EndMs = pEndMs,
|
||||||
|
OriginalText = originalText,
|
||||||
|
LyricsSyllables = originalCharTimings,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 解析 x-role
|
||||||
|
ParseTtmlXRole(spans, translationLines, "x-translation", pStartMs, pEndMs);
|
||||||
|
ParseTtmlXRole(spans, romanLines, "x-roman", pStartMs, pEndMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
LyricsDataArr.Add(new LyricsData(originalLines));
|
||||||
|
if (translationLines.Count > 0)
|
||||||
|
{
|
||||||
|
LyricsDataArr.Add(new LyricsData(translationLines));
|
||||||
|
}
|
||||||
|
if (romanLines.Count > 0)
|
||||||
|
{
|
||||||
|
LyricsDataArr.Add(new LyricsData(romanLines) { LanguageCode = PhoneticHelper.RomanCode });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 解析失败,忽略
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseTtmlXRole(List<XElement> sourceSpans, List<LyricsLine> saveLyricsLines, string xRole, int pStartMs, int? pEndMs)
|
||||||
|
{
|
||||||
|
var textSpans = sourceSpans
|
||||||
|
.Where(s => s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value == xRole)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
string text = string.Concat(textSpans.Select(s => s.Value));
|
||||||
|
var charTimings = new List<LyricsSyllable>();
|
||||||
|
int startIndex = 0;
|
||||||
|
foreach (var span in textSpans)
|
||||||
|
{
|
||||||
|
string? sBegin = span.Attribute("begin")?.Value;
|
||||||
|
string? sEnd = span.Attribute("end")?.Value;
|
||||||
|
int sStartMs = ParseTtmlTime(sBegin);
|
||||||
|
int sEndMs = ParseTtmlTime(sEnd);
|
||||||
|
charTimings.Add(new LyricsSyllable
|
||||||
|
{
|
||||||
|
StartMs = sStartMs,
|
||||||
|
EndMs = sEndMs,
|
||||||
|
StartIndex = startIndex,
|
||||||
|
Text = span.Value
|
||||||
|
});
|
||||||
|
startIndex += span.Value.Length;
|
||||||
|
}
|
||||||
|
if (textSpans.Count > 0)
|
||||||
|
{
|
||||||
|
saveLyricsLines.Add(new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = pStartMs,
|
||||||
|
EndMs = pEndMs,
|
||||||
|
OriginalText = text,
|
||||||
|
LyricsSyllables = charTimings,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ParseTtmlTime(string? t)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(t))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
t = t.Trim();
|
||||||
|
|
||||||
|
// 支持 "1.000s"
|
||||||
|
if (t.EndsWith("s"))
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
double.TryParse(
|
||||||
|
t.TrimEnd('s'),
|
||||||
|
System.Globalization.NumberStyles.Float,
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture,
|
||||||
|
out double seconds
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return (int)(seconds * 1000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var parts = t.Split(':');
|
||||||
|
if (parts.Length == 3)
|
||||||
|
{
|
||||||
|
// hh:mm:ss.xxx
|
||||||
|
int h = int.Parse(parts[0]);
|
||||||
|
int m = int.Parse(parts[1]);
|
||||||
|
double s = double.Parse(
|
||||||
|
parts[2],
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture
|
||||||
|
);
|
||||||
|
return (int)((h * 3600 + m * 60 + s) * 1000);
|
||||||
|
}
|
||||||
|
else if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
// mm:ss.xxx
|
||||||
|
int m = int.Parse(parts[0]);
|
||||||
|
double s = double.Parse(
|
||||||
|
parts[1],
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture
|
||||||
|
);
|
||||||
|
return (int)((m * 60 + s) * 1000);
|
||||||
|
}
|
||||||
|
else if (parts.Length == 1)
|
||||||
|
{
|
||||||
|
// ss.xxx
|
||||||
|
if (
|
||||||
|
double.TryParse(
|
||||||
|
parts[0],
|
||||||
|
System.Globalization.NumberStyles.Float,
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture,
|
||||||
|
out double s
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return (int)(s * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
// 2025/6/23 by Zhe Fang
|
||||||
|
|
||||||
|
using BetterLyrics.WinUI3.Enums;
|
||||||
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
|
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||||
|
using Lyricify.Lyrics.Parsers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
||||||
|
{
|
||||||
|
public partial class LyricsParser
|
||||||
|
{
|
||||||
|
private static readonly ILogger<LyricsParser> _logger = Ioc.Default.GetRequiredService<ILogger<LyricsParser>>();
|
||||||
|
|
||||||
|
public List<LyricsData> LyricsDataArr { get; private set; } = [];
|
||||||
|
|
||||||
|
public void Parse(SongInfo? songInfo, LyricsSearchResult? lyricsSearchResult)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("LyricsParser.Parse");
|
||||||
|
LyricsDataArr = [];
|
||||||
|
if (string.IsNullOrWhiteSpace(lyricsSearchResult?.Raw))
|
||||||
|
{
|
||||||
|
LyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (lyricsSearchResult.Raw.DetectFormat())
|
||||||
|
{
|
||||||
|
case LyricsFormat.Lrc:
|
||||||
|
case LyricsFormat.Eslrc:
|
||||||
|
ParseLrc(lyricsSearchResult.Raw, lyricsSearchResult.Provider.IsRemote());
|
||||||
|
break;
|
||||||
|
case LyricsFormat.Qrc:
|
||||||
|
ParseQrcKrc(QrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
||||||
|
break;
|
||||||
|
case LyricsFormat.Krc:
|
||||||
|
ParseQrcKrc(KrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
||||||
|
break;
|
||||||
|
case LyricsFormat.Ttml:
|
||||||
|
ParseTtml(lyricsSearchResult.Raw);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LyricsDataArr.Count == 0)
|
||||||
|
{
|
||||||
|
LyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LoadTranslation(lyricsSearchResult);
|
||||||
|
LoadTransliteration(lyricsSearchResult);
|
||||||
|
GenerateTransliterationLyricsData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadTranslation(LyricsSearchResult? lyricsSearchResult)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(lyricsSearchResult?.Translation))
|
||||||
|
{
|
||||||
|
switch (lyricsSearchResult.Provider)
|
||||||
|
{
|
||||||
|
case LyricsSearchProvider.QQ:
|
||||||
|
case LyricsSearchProvider.Kugou:
|
||||||
|
case LyricsSearchProvider.Netease:
|
||||||
|
ParseLrc(lyricsSearchResult.Translation, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadTransliteration(LyricsSearchResult? lyricsSearchResult)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(lyricsSearchResult?.Transliteration))
|
||||||
|
{
|
||||||
|
switch (lyricsSearchResult.Provider)
|
||||||
|
{
|
||||||
|
case LyricsSearchProvider.Netease:
|
||||||
|
ParseLrc(lyricsSearchResult.Transliteration, true);
|
||||||
|
LyricsDataArr.LastOrDefault()?.LanguageCode = PhoneticHelper.RomanCode;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在音译不存在的情况下生成音译歌词
|
||||||
|
/// </summary>
|
||||||
|
private void GenerateTransliterationLyricsData()
|
||||||
|
{
|
||||||
|
var main = LyricsDataArr.FirstOrDefault();
|
||||||
|
if (main != null)
|
||||||
|
{
|
||||||
|
string? languageCode = main.LanguageCode;
|
||||||
|
if (languageCode == LanguageHelper.ChineseCode)
|
||||||
|
{
|
||||||
|
if (!LyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.PinyinCode))
|
||||||
|
{
|
||||||
|
LyricsDataArr.Add(new LyricsData
|
||||||
|
{
|
||||||
|
LanguageCode = PhoneticHelper.PinyinCode,
|
||||||
|
AutoGenerated = true,
|
||||||
|
LyricsLines = main.LyricsLines.Select(line => new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = line.StartMs,
|
||||||
|
EndMs = line.EndMs,
|
||||||
|
OriginalText = PhoneticHelper.ToPinyin(line.OriginalText),
|
||||||
|
LyricsSyllables = line.LyricsSyllables.Select(c => new LyricsSyllable
|
||||||
|
{
|
||||||
|
StartMs = c.StartMs,
|
||||||
|
EndMs = c.EndMs,
|
||||||
|
Text = PhoneticHelper.ToPinyin(c.Text),
|
||||||
|
StartIndex = c.StartIndex
|
||||||
|
}).ToList()
|
||||||
|
}).ToList()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!LyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.JyutpingCode))
|
||||||
|
{
|
||||||
|
LyricsDataArr.Add(new LyricsData
|
||||||
|
{
|
||||||
|
LanguageCode = PhoneticHelper.JyutpingCode,
|
||||||
|
AutoGenerated = true,
|
||||||
|
LyricsLines = main.LyricsLines.Select(line => new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = line.StartMs,
|
||||||
|
EndMs = line.EndMs,
|
||||||
|
OriginalText = PhoneticHelper.ToJyutping(line.OriginalText),
|
||||||
|
LyricsSyllables = line.LyricsSyllables.Select(c => new LyricsSyllable
|
||||||
|
{
|
||||||
|
StartMs = c.StartMs,
|
||||||
|
EndMs = c.EndMs,
|
||||||
|
Text = PhoneticHelper.ToJyutping(c.Text),
|
||||||
|
StartIndex = c.StartIndex
|
||||||
|
}).ToList()
|
||||||
|
}).ToList()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (languageCode == LanguageHelper.JapaneseCode)
|
||||||
|
{
|
||||||
|
if (!LyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.RomanCode))
|
||||||
|
{
|
||||||
|
LyricsDataArr.Add(new LyricsData
|
||||||
|
{
|
||||||
|
LanguageCode = PhoneticHelper.RomanCode,
|
||||||
|
AutoGenerated = true,
|
||||||
|
LyricsLines = main.LyricsLines.Select(line => new LyricsLine
|
||||||
|
{
|
||||||
|
StartMs = line.StartMs,
|
||||||
|
EndMs = line.EndMs,
|
||||||
|
OriginalText = PhoneticHelper.ToRomaji(line.OriginalText),
|
||||||
|
LyricsSyllables = line.LyricsSyllables.Select(c => new LyricsSyllable
|
||||||
|
{
|
||||||
|
StartMs = c.StartMs,
|
||||||
|
EndMs = c.EndMs,
|
||||||
|
Text = PhoneticHelper.ToRomaji(c.Text),
|
||||||
|
StartIndex = c.StartIndex
|
||||||
|
}).ToList()
|
||||||
|
}).ToList()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using BetterLyrics.WinUI3.Helper;
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
|
using BetterLyrics.WinUI3.Helper;
|
||||||
|
using BetterLyrics.WinUI3.Models;
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -67,7 +69,7 @@ namespace BetterLyrics.WinUI3.Providers
|
|||||||
_client.DefaultRequestHeaders.Add("Accept-Language", $"{_language},en;q=0.9");
|
_client.DefaultRequestHeaders.Add("Accept-Language", $"{_language},en;q=0.9");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> GetLyricsAsync(string id)
|
private async Task<string?> GetLyricsAsync(string id)
|
||||||
{
|
{
|
||||||
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/songs/{id}";
|
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/songs/{id}";
|
||||||
var url = apiUrl + $"?include[songs]=lyrics,syllable-lyrics&l={_language}";
|
var url = apiUrl + $"?include[songs]=lyrics,syllable-lyrics&l={_language}";
|
||||||
@@ -109,9 +111,14 @@ namespace BetterLyrics.WinUI3.Providers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> SearchSongInfoAsync(string artist, string title)
|
public async Task<LyricsSearchResult> SearchSongInfoAsync(Models.SongInfo songInfo)
|
||||||
{
|
{
|
||||||
var query = $"{artist} {title}";
|
LyricsSearchResult lyricsSearchResult = new()
|
||||||
|
{
|
||||||
|
Provider = Enums.LyricsSearchProvider.AppleMusic
|
||||||
|
};
|
||||||
|
|
||||||
|
var query = $"{songInfo.DisplayArtists} {songInfo.Title}";
|
||||||
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/search";
|
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/search";
|
||||||
var url = apiUrl + $"?term={WebUtility.UrlEncode(query)}&types=songs&limit=1&l={_language}";
|
var url = apiUrl + $"?term={WebUtility.UrlEncode(query)}&types=songs&limit=1&l={_language}";
|
||||||
var resp = await _client.GetStringAsync(url);
|
var resp = await _client.GetStringAsync(url);
|
||||||
@@ -120,9 +127,26 @@ namespace BetterLyrics.WinUI3.Providers
|
|||||||
if (results.TryGetProperty("songs", out var songs) && songs.GetProperty("data").GetArrayLength() > 0)
|
if (results.TryGetProperty("songs", out var songs) && songs.GetProperty("data").GetArrayLength() > 0)
|
||||||
{
|
{
|
||||||
var song = songs.GetProperty("data")[0];
|
var song = songs.GetProperty("data")[0];
|
||||||
return song.GetProperty("id").ToString();
|
|
||||||
|
var id = song.GetProperty("id").ToString();
|
||||||
|
|
||||||
|
var attr = song.GetProperty("attributes");
|
||||||
|
|
||||||
|
lyricsSearchResult.Title = attr.GetProperty("name").ToString();
|
||||||
|
lyricsSearchResult.Artists = attr.GetProperty("artistName").ToString().SplitByCommonSplitter();
|
||||||
|
lyricsSearchResult.Album = attr.GetProperty("albumName").ToString();
|
||||||
|
lyricsSearchResult.Duration = attr.GetProperty("durationInMillis").GetInt32() / 1000.0;
|
||||||
|
|
||||||
|
lyricsSearchResult.Reference = $"https://music.apple.com/song/{id}";
|
||||||
|
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||||
|
|
||||||
|
if (id != null)
|
||||||
|
{
|
||||||
|
lyricsSearchResult.Raw = await GetLyricsAsync(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return string.Empty;
|
|
||||||
|
return lyricsSearchResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
using BetterLyrics.WinUI3.Extensions;
|
||||||
|
using Microsoft.Graphics.Canvas;
|
||||||
|
using Microsoft.Graphics.Canvas.Effects;
|
||||||
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices.WindowsRuntime;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Windows.Storage;
|
||||||
|
using Windows.UI;
|
||||||
|
|
||||||
|
namespace BetterLyrics.WinUI3.Renderer
|
||||||
|
{
|
||||||
|
public partial class FluidBackgroundRenderer : IDisposable
|
||||||
|
{
|
||||||
|
private PixelShaderEffect? _fluidEffect;
|
||||||
|
private float _timeAccumulator = 0f;
|
||||||
|
private Vector3 _c1, _c2, _c3, _c4;
|
||||||
|
|
||||||
|
public bool IsEnabled { get; set; } = false;
|
||||||
|
public double Opacity { get; set; } = 1.0;
|
||||||
|
|
||||||
|
public bool EnableLightWave { get; set; } = false;
|
||||||
|
|
||||||
|
public async Task LoadResourcesAsync()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uri = new Uri("ms-appx:///Assets/FluidEffect.bin");
|
||||||
|
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);
|
||||||
|
|
||||||
|
using (var stream = await file.OpenReadAsync())
|
||||||
|
{
|
||||||
|
var buffer = new Windows.Storage.Streams.Buffer((uint)stream.Size);
|
||||||
|
await stream.ReadAsync(buffer, (uint)stream.Size, Windows.Storage.Streams.InputStreamOptions.None);
|
||||||
|
byte[] bytes = buffer.ToArray();
|
||||||
|
|
||||||
|
_fluidEffect = new PixelShaderEffect(bytes);
|
||||||
|
|
||||||
|
_fluidEffect.Properties["EnableLightWave"] = EnableLightWave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[FluidRenderer] Load Failed: {ex.Message}");
|
||||||
|
_fluidEffect = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateColors(Color c1, Color c2, Color c3, Color c4)
|
||||||
|
{
|
||||||
|
_c1 = c1.ToVector3RGB();
|
||||||
|
_c2 = c2.ToVector3RGB();
|
||||||
|
_c3 = c3.ToVector3RGB();
|
||||||
|
_c4 = c4.ToVector3RGB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(TimeSpan deltaTime)
|
||||||
|
{
|
||||||
|
if (_fluidEffect == null || !IsEnabled) return;
|
||||||
|
|
||||||
|
_timeAccumulator += (float)deltaTime.TotalSeconds;
|
||||||
|
|
||||||
|
_fluidEffect.Properties["iTime"] = _timeAccumulator;
|
||||||
|
|
||||||
|
_fluidEffect.Properties["color1"] = _c1;
|
||||||
|
_fluidEffect.Properties["color2"] = _c2;
|
||||||
|
_fluidEffect.Properties["color3"] = _c3;
|
||||||
|
_fluidEffect.Properties["color4"] = _c4;
|
||||||
|
|
||||||
|
_fluidEffect.Properties["EnableLightWave"] = EnableLightWave;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||||
|
{
|
||||||
|
if (_fluidEffect == null || !IsEnabled || Opacity <= 0) return;
|
||||||
|
|
||||||
|
float pixelWidth = control.ConvertDipsToPixels((float)control.Size.Width, CanvasDpiRounding.Round);
|
||||||
|
float pixelHeight = control.ConvertDipsToPixels((float)control.Size.Height, CanvasDpiRounding.Round);
|
||||||
|
|
||||||
|
_fluidEffect.Properties["Width"] = pixelWidth;
|
||||||
|
_fluidEffect.Properties["Height"] = pixelHeight;
|
||||||
|
|
||||||
|
if (Opacity >= 1.0)
|
||||||
|
{
|
||||||
|
ds.DrawImage(_fluidEffect);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (var opacityEffect = new OpacityEffect
|
||||||
|
{
|
||||||
|
Source = _fluidEffect,
|
||||||
|
Opacity = (float)Opacity
|
||||||
|
})
|
||||||
|
{
|
||||||
|
ds.DrawImage(opacityEffect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_fluidEffect?.Dispose();
|
||||||
|
_fluidEffect = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user