mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-13 03:34:55 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
509079e8c7 | ||
|
|
a29e5c98f8 | ||
|
|
78a6ba8e1f | ||
|
|
352ceca81d |
@@ -5,52 +5,62 @@
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
|
||||
xmlns:uap18="http://schemas.microsoft.com/appx/manifest/uap/windows10/18"
|
||||
IgnorableNamespaces="uap rescap uap18">
|
||||
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.7.0" />
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.9.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"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>BetterLyrics</DisplayName>
|
||||
<PublisherDisplayName>founchoo</PublisherDisplayName>
|
||||
<Logo>Images\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
<Properties>
|
||||
<DisplayName>BetterLyrics</DisplayName>
|
||||
<PublisherDisplayName>founchoo</PublisherDisplayName>
|
||||
<Logo>Images\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.19041.0" MaxVersionTested="10.0.26100.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.26100.0" />
|
||||
</Dependencies>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.19041.0" MaxVersionTested="10.0.26100.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.26100.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="en-US"/>
|
||||
<Resource Language="zh-CN"/>
|
||||
<Resource Language="zh-TW"/>
|
||||
<Resource Language="ja-JP"/>
|
||||
<Resource Language="ko-KR"/>
|
||||
</Resources>
|
||||
<Resources>
|
||||
<Resource Language="en-US"/>
|
||||
<Resource Language="zh-CN"/>
|
||||
<Resource Language="zh-TW"/>
|
||||
<Resource Language="ja-JP"/>
|
||||
<Resource Language="ko-KR"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="BetterLyrics"
|
||||
Description="BetterLyrics.WinUI3 (Package)"
|
||||
BackgroundColor="transparent"
|
||||
Square150x150Logo="Images\Square150x150Logo.png"
|
||||
Square44x44Logo="Images\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" Square71x71Logo="Images\SmallTile.png" Square310x310Logo="Images\LargeTile.png"/>
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="BetterLyrics"
|
||||
Description="BetterLyrics.WinUI3 (Package)"
|
||||
BackgroundColor="transparent"
|
||||
Square150x150Logo="Images\Square150x150Logo.png"
|
||||
Square44x44Logo="Images\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" Square71x71Logo="Images\SmallTile.png" Square310x310Logo="Images\LargeTile.png"/>
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap5:Extension
|
||||
Category="windows.startupTask">
|
||||
<uap5:StartupTask
|
||||
TaskId="AutoStartup"
|
||||
Enabled="false"
|
||||
DisplayName="BetterLyrics" />
|
||||
</uap5:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
<x:Double x:Key="SettingsCardSpacing">4</x:Double>
|
||||
|
||||
<!-- Style (inc. the correct spacing) of a section header -->
|
||||
<!-- Style -->
|
||||
<Style
|
||||
x:Key="SettingsSectionHeaderTextBlockStyle"
|
||||
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
||||
@@ -63,14 +63,16 @@
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
<Style x:Key="TitleBarButtonStyle" TargetType="Button">
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="CornerRadius" Value="0" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="16,0" />
|
||||
<Setter Property="Padding" Value="16,9,16,11" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style x:Key="GhostButtonStyle" TargetType="Button">
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Padding" Value="8" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
@@ -86,6 +88,7 @@
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="8" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BetterInAppLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
@@ -17,6 +12,11 @@ using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
using Serilog;
|
||||
using ShadowViewer.Controls;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3
|
||||
{
|
||||
@@ -30,6 +30,9 @@ namespace BetterLyrics.WinUI3
|
||||
public static DispatcherQueueTimer? DispatcherQueueTimer { get; private set; }
|
||||
public static ResourceLoader? ResourceLoader { get; private set; }
|
||||
|
||||
public NotificationPanel? LyricsWindowNotificationPanel { get; set; }
|
||||
public NotificationPanel? SettingsWindowNotificationPanel { get; set; }
|
||||
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
@@ -42,7 +45,7 @@ namespace BetterLyrics.WinUI3
|
||||
AppInfo.EnsureDirectories();
|
||||
ConfigureServices();
|
||||
|
||||
_logger = Ioc.Default.GetService<ILogger<App>>()!;
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<App>>();
|
||||
|
||||
UnhandledException += App_UnhandledException;
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
@@ -54,6 +57,7 @@ namespace BetterLyrics.WinUI3
|
||||
{
|
||||
WindowHelper.OpenOrShowWindow<LyricsWindow>();
|
||||
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (lyricsWindow == null) return;
|
||||
|
||||
string[] commandLineArguments = Environment.GetCommandLineArgs();
|
||||
if (commandLineArguments.Length > 1)
|
||||
@@ -67,10 +71,11 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
lyricsWindow.AutoSelectLyricsMode();
|
||||
}
|
||||
|
||||
private static void ConfigureServices()
|
||||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Debug()
|
||||
.MinimumLevel.Is(Serilog.Events.LogEventLevel.Verbose)
|
||||
.WriteTo.File(AppInfo.LogFilePattern, rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
|
||||
@@ -87,6 +92,7 @@ namespace BetterLyrics.WinUI3
|
||||
.AddSingleton<IPlaybackService, PlaybackService>()
|
||||
.AddSingleton<IMusicSearchService, MusicSearchService>()
|
||||
.AddSingleton<ILibWatcherService, LibWatcherService>()
|
||||
.AddSingleton<ILibreTranslateService, LibreTranslateService>()
|
||||
// ViewModels
|
||||
.AddSingleton<LyricsWindowViewModel>()
|
||||
.AddSingleton<SettingsWindowViewModel>()
|
||||
@@ -94,7 +100,6 @@ namespace BetterLyrics.WinUI3
|
||||
.AddSingleton<SettingsPageViewModel>()
|
||||
.AddSingleton<LyricsPageViewModel>()
|
||||
.AddSingleton<LyricsRendererViewModel>()
|
||||
.AddSingleton<LyricsSettingsControlViewModel>()
|
||||
.BuildServiceProvider()
|
||||
);
|
||||
}
|
||||
@@ -107,7 +112,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void CurrentDomain_FirstChanceException(object? sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
|
||||
{
|
||||
//_logger.LogError(e.Exception, "CurrentDomain_FirstChanceException");
|
||||
_logger.LogError(e.Exception, "CurrentDomain_FirstChanceException");
|
||||
}
|
||||
|
||||
private void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
|
||||
@@ -117,7 +122,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
|
||||
{
|
||||
//_logger.LogError(e.Exception, "TaskScheduler_UnobservedTaskException");
|
||||
_logger.LogError(e.Exception, "TaskScheduler_UnobservedTaskException");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,16 +40,18 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
|
||||
<PackageReference Include="iTunesSearch" Version="1.0.44" />
|
||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="ShadowViewer.Controls.Notification" Version="1.2.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.6" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.6" />
|
||||
|
||||
@@ -4,12 +4,17 @@ namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum EasingType
|
||||
{
|
||||
EaseInOutQuad,
|
||||
EaseInQuad,
|
||||
EaseOutQuad,
|
||||
EaseInOutExpo,
|
||||
Linear,
|
||||
SmoothStep,
|
||||
SmootherStep,
|
||||
EaseInOutSine,
|
||||
EaseInOutQuad,
|
||||
EaseInOutCubic,
|
||||
EaseInOutQuart,
|
||||
EaseInOutQuint,
|
||||
EaseInOutExpo,
|
||||
EaseInOutCirc,
|
||||
EaseInOutBack,
|
||||
EaseInOutElastic,
|
||||
EaseInOutBounce,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum LyricsAlignmentType
|
||||
{
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,33 @@ namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public static LyricsFormat? DetectFormat(this string content)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
return null;
|
||||
|
||||
// TTML
|
||||
if (content.StartsWith("<?xml") && System.Text.RegularExpressions.Regex.IsMatch(content, @"<tt(:\w+)?\b"))
|
||||
{
|
||||
return LyricsFormat.Ttml;
|
||||
}
|
||||
// 检测标准LRC和增强型LRC
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(content, @"\[\d{1,2}:\d{2}")
|
||||
|| System.Text.RegularExpressions.Regex.IsMatch(content, @"<\d{1,2}:\d{2}\.\d{2,3}>"))
|
||||
// KRC: 检测主内容格式 [start,duration]<offset,duration,0>字...
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(
|
||||
content,
|
||||
@"^\[\d+,\d+\](<\d+,\d+,0>.+)+",
|
||||
System.Text.RegularExpressions.RegexOptions.Multiline))
|
||||
{
|
||||
return LyricsFormat.Krc;
|
||||
}
|
||||
// QRC: 检测主内容格式 [start,duration]字(offset,duration)
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(
|
||||
content,
|
||||
@"^\[\d+,\d+\].*?\(\d+,\d+\)",
|
||||
System.Text.RegularExpressions.RegexOptions.Multiline))
|
||||
{
|
||||
return LyricsFormat.Qrc;
|
||||
}
|
||||
// 标准LRC和增强型LRC
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(content, @"\[\d{1,2}:\d{2}") ||
|
||||
System.Text.RegularExpressions.Regex.IsMatch(content, @"<\d{1,2}:\d{2}\.\d{2,3}>"))
|
||||
{
|
||||
return LyricsFormat.Lrc;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum TextAlignmentType
|
||||
{
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
public static class LyricsAlignmentTypeExtensions
|
||||
{
|
||||
public static CanvasHorizontalAlignment ToCanvasHorizontalAlignment(this TextAlignmentType alignmentType)
|
||||
{
|
||||
return alignmentType switch
|
||||
{
|
||||
TextAlignmentType.Left => CanvasHorizontalAlignment.Left,
|
||||
TextAlignmentType.Center => CanvasHorizontalAlignment.Center,
|
||||
TextAlignmentType.Right => CanvasHorizontalAlignment.Right,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(alignmentType), alignmentType, null),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Events
|
||||
{
|
||||
public class MediaSourceProvidersInfoEventArgs(List<MediaSourceProviderInfo> sessionIds):EventArgs
|
||||
{
|
||||
public List<MediaSourceProviderInfo> MediaSourceProviersInfo { get; set; } = sessionIds;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
@@ -17,15 +18,18 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
private T _currentValue;
|
||||
private float _durationSeconds;
|
||||
private readonly EasingType? _easingType;
|
||||
private EasingType? _easingType;
|
||||
private Func<T, T, float, T> _interpolator;
|
||||
private bool _isTransitioning;
|
||||
private float _progress;
|
||||
private T _startValue;
|
||||
private T _targetValue;
|
||||
|
||||
public float DurationSeconds => _durationSeconds;
|
||||
|
||||
public bool IsTransitioning => _isTransitioning;
|
||||
public T Value => _currentValue;
|
||||
public T TargetValue => _targetValue;
|
||||
|
||||
public ValueTransition(T initialValue, float durationSeconds, Func<T, T, float, T>? interpolator = null, EasingType? easingType = null)
|
||||
{
|
||||
@@ -44,16 +48,16 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
else if (easingType.HasValue)
|
||||
{
|
||||
_easingType = easingType;
|
||||
_interpolator = GetInterpolatorByEasingType(easingType.Value);
|
||||
_interpolator = GetInterpolatorByEasingType(_easingType.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_interpolator = GetInterpolatorByEasingType(EasingType.Linear);
|
||||
_easingType = EasingType.Linear;
|
||||
_easingType = EasingType.SmoothStep;
|
||||
_interpolator = GetInterpolatorByEasingType(_easingType.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void JumpTo(T value)
|
||||
private void JumpTo(T value)
|
||||
{
|
||||
_currentValue = value;
|
||||
_startValue = value;
|
||||
@@ -71,8 +75,14 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
_isTransitioning = false;
|
||||
}
|
||||
|
||||
public void StartTransition(T targetValue)
|
||||
public void StartTransition(T targetValue, bool jumpTo = false)
|
||||
{
|
||||
if (jumpTo)
|
||||
{
|
||||
JumpTo(targetValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetValue.Equals(_currentValue))
|
||||
{
|
||||
_startValue = _currentValue;
|
||||
@@ -82,6 +92,12 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Equals(double x, double y, double tolerance)
|
||||
{
|
||||
var diff = Math.Abs(x - y);
|
||||
return diff <= tolerance || diff <= Math.Max(Math.Abs(x), Math.Abs(y)) * tolerance;
|
||||
}
|
||||
|
||||
public void Update(TimeSpan elapsedTime)
|
||||
{
|
||||
if (!_isTransitioning) return;
|
||||
@@ -110,26 +126,41 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
float t = progress;
|
||||
switch (type)
|
||||
{
|
||||
case EasingType.EaseInOutExpo:
|
||||
t = EasingHelper.EaseInOutExpo(t);
|
||||
case EasingType.EaseInOutSine:
|
||||
t = EasingHelper.EaseInOutSine(t);
|
||||
break;
|
||||
case EasingType.EaseInOutQuad:
|
||||
t = EasingHelper.EaseInOutQuad(t);
|
||||
break;
|
||||
case EasingType.EaseInQuad:
|
||||
t = EasingHelper.EaseInQuad(t);
|
||||
case EasingType.EaseInOutCubic:
|
||||
t = EasingHelper.EaseInOutCubic(t);
|
||||
break;
|
||||
case EasingType.EaseOutQuad:
|
||||
t = EasingHelper.EaseOutQuad(t);
|
||||
case EasingType.EaseInOutQuart:
|
||||
t = EasingHelper.EaseInOutQuart(t);
|
||||
break;
|
||||
case EasingType.Linear:
|
||||
t = EasingHelper.Linear(t);
|
||||
case EasingType.EaseInOutQuint:
|
||||
t = EasingHelper.EaseInOutQuint(t);
|
||||
break;
|
||||
case EasingType.EaseInOutExpo:
|
||||
t = EasingHelper.EaseInOutExpo(t);
|
||||
break;
|
||||
case EasingType.EaseInOutCirc:
|
||||
t = EasingHelper.EaseInOutCirc(t);
|
||||
break;
|
||||
case EasingType.EaseInOutBack:
|
||||
t = EasingHelper.EaseInOutBack(t);
|
||||
break;
|
||||
case EasingType.EaseInOutElastic:
|
||||
t = EasingHelper.EaseInOutElastic(t);
|
||||
break;
|
||||
case EasingType.EaseInOutBounce:
|
||||
t = EasingHelper.EaseInOutBounce(t);
|
||||
break;
|
||||
case EasingType.SmoothStep:
|
||||
t = EasingHelper.SmoothStep(t);
|
||||
break;
|
||||
case EasingType.SmootherStep:
|
||||
t = EasingHelper.SmootherStep(t);
|
||||
case EasingType.Linear:
|
||||
t = EasingHelper.Linear(t);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -139,5 +170,11 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
throw new NotSupportedException($"Easing type {type} is not supported for type {typeof(T)}.");
|
||||
}
|
||||
|
||||
public void SetEasingType(EasingType easingType)
|
||||
{
|
||||
_easingType = easingType;
|
||||
_interpolator = GetInterpolatorByEasingType(easingType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
@@ -26,31 +28,40 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public const string GithubUrl = "https://github.com/jayfunc/BetterLyrics";
|
||||
|
||||
public static string CacheFolder => ApplicationData.Current.LocalCacheFolder.Path;
|
||||
|
||||
|
||||
public const string UnlockWindowTag = "UnlockWindow";
|
||||
|
||||
public static string AmllTtmlDbIndexPath => Path.Combine(CacheFolder, "amll-ttml-db-index.json");
|
||||
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(CacheFolder, "amll-ttml-db-lyrics");
|
||||
|
||||
public static string AssetsFolder => Path.Combine(Package.Current.InstalledPath, "Assets");
|
||||
public static string CacheFolder => ApplicationData.Current.LocalCacheFolder.Path;
|
||||
public static string KugouLyricsCacheDirectory => Path.Combine(CacheFolder, "kugou-lyrics");
|
||||
public static string LogDirectory => Path.Combine(CacheFolder, "logs");
|
||||
public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt");
|
||||
|
||||
public static string LrcLibLyricsCacheDirectory => Path.Combine(CacheFolder, "lrclib-lyrics");
|
||||
public static string NeteaseLyricsCacheDirectory => Path.Combine(CacheFolder, "netease-lyrics");
|
||||
public static string QQLyricsCacheDirectory => Path.Combine(CacheFolder, "qq-lyrics");
|
||||
public static string KugouLyricsCacheDirectory => Path.Combine(CacheFolder, "kugou-lyrics");
|
||||
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(CacheFolder, "amll-ttml-db-lyrics");
|
||||
|
||||
public static string iTunesAlbumArtCacheDirectory => Path.Combine(CacheFolder, "itunes-album-art");
|
||||
|
||||
|
||||
private static string LocalFolder => ApplicationData.Current.LocalFolder.Path;
|
||||
|
||||
public static void EnsureDirectories()
|
||||
{
|
||||
Directory.CreateDirectory(LocalFolder);
|
||||
Directory.CreateDirectory(LogDirectory);
|
||||
|
||||
Directory.CreateDirectory(LrcLibLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(QQLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(KugouLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
|
||||
|
||||
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
||||
}
|
||||
|
||||
public static async Task<DateTime> GetBuildDate()
|
||||
@@ -66,5 +77,39 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
// 返回修改日期
|
||||
return props.DateModified.DateTime;
|
||||
}
|
||||
|
||||
public static List<LanguageInfo> GetAllTranslationLanguagesInfo() =>
|
||||
[
|
||||
new LanguageInfo("ar", "العربية"),
|
||||
new LanguageInfo("az", "Azərbaycan dili"),
|
||||
new LanguageInfo("zh", "中文"),
|
||||
new LanguageInfo("cs", "Čeština"),
|
||||
new LanguageInfo("da", "Dansk"),
|
||||
new LanguageInfo("nl", "Nederlands"),
|
||||
new LanguageInfo("en", "English"),
|
||||
new LanguageInfo("eo", "Esperanto"),
|
||||
new LanguageInfo("fi", "Suomi"),
|
||||
new LanguageInfo("fr", "Français"),
|
||||
new LanguageInfo("de", "Deutsch"),
|
||||
new LanguageInfo("el", "Ελληνικά"),
|
||||
new LanguageInfo("he", "עברית"),
|
||||
new LanguageInfo("hi", "हिन्दी"),
|
||||
new LanguageInfo("hu", "Magyar"),
|
||||
new LanguageInfo("id", "Bahasa Indonesia"),
|
||||
new LanguageInfo("ga", "Gaeilge"),
|
||||
new LanguageInfo("it", "Italiano"),
|
||||
new LanguageInfo("ja", "日本語"),
|
||||
new LanguageInfo("ko", "한국어"),
|
||||
new LanguageInfo("fa", "فارسی"),
|
||||
new LanguageInfo("pl", "Polski"),
|
||||
new LanguageInfo("pt", "Português"),
|
||||
new LanguageInfo("ru", "Русский"),
|
||||
new LanguageInfo("sk", "Slovenčina"),
|
||||
new LanguageInfo("es", "Español"),
|
||||
new LanguageInfo("sv", "Svenska"),
|
||||
new LanguageInfo("tr", "Türkçe"),
|
||||
new LanguageInfo("uk", "Українська"),
|
||||
new LanguageInfo("vi", "Tiếng Việt"),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,5 +81,22 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
return Color.FromArgb(color.A, color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public static Color WithAlpha(this Color color, byte alpha)
|
||||
{
|
||||
return Color.FromArgb(alpha, color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public static Color WithBrightness(this Color color, double brightness)
|
||||
{
|
||||
// 确保亮度因子在合理范围内
|
||||
brightness = Math.Max(0, Math.Min(1, brightness));
|
||||
|
||||
var hsl = CommunityToolkit.WinUI.Helpers.ColorHelper.ToHsl(color);
|
||||
double h = hsl.H;
|
||||
double s = hsl.S;
|
||||
|
||||
return CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(h, s, brightness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
|
||||
// <20>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD>úʹ<C3BA>С
|
||||
var windowManager = WindowManager.Get(window);
|
||||
if (_originalWindowBounds.TryGetValue(hwnd, out var bounds))
|
||||
{
|
||||
windowManager.AppWindow.MoveAndResize(
|
||||
window.AppWindow.MoveAndResize(
|
||||
new Windows.Graphics.RectInt32(
|
||||
(int)bounds.X,
|
||||
(int)bounds.Y,
|
||||
@@ -63,14 +62,13 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
// <20><>¼ԭʼ<D4AD><CABC><EFBFBD><EFBFBD>λ<EFBFBD>úʹ<C3BA>С
|
||||
var windowManager = WindowManager.Get(window);
|
||||
if (!_originalWindowBounds.ContainsKey(hwnd))
|
||||
{
|
||||
_originalWindowBounds[hwnd] = (
|
||||
windowManager.AppWindow.Position.X,
|
||||
windowManager.AppWindow.Position.Y,
|
||||
windowManager.Width,
|
||||
windowManager.Height
|
||||
window.AppWindow.Position.X,
|
||||
window.AppWindow.Position.Y,
|
||||
window.AppWindow.Size.Width,
|
||||
window.AppWindow.Size.Height
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,7 +79,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
int targetY = _settingsService.DesktopWindowTop;
|
||||
|
||||
// <20><><EFBFBD>ô<EFBFBD><C3B4>ڴ<EFBFBD>С<EFBFBD><D0A1>λ<EFBFBD><CEBB>
|
||||
windowManager.AppWindow.MoveAndResize(
|
||||
window.AppWindow.MoveAndResize(
|
||||
new Windows.Graphics.RectInt32(targetX, targetY, targetWidth, targetHeight)
|
||||
);
|
||||
|
||||
@@ -102,7 +100,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static void Lock(Window window)
|
||||
{
|
||||
window.SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
||||
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>ޱ߿<DEB1><DFBF><EFBFBD><EFBFBD><CDB8>
|
||||
window.ToggleWindowStyle(true, WindowStyle.Popup | WindowStyle.Visible);
|
||||
window.ExtendsContentIntoTitleBar = false;
|
||||
|
||||
@@ -10,6 +10,29 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class EasingHelper
|
||||
{
|
||||
public static float EaseInOutSine(float t)
|
||||
{
|
||||
return -(MathF.Cos(MathF.PI * t) - 1f) / 2f;
|
||||
}
|
||||
public static float EaseInOutQuad(float t)
|
||||
{
|
||||
return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
||||
}
|
||||
|
||||
public static float EaseInOutCubic(float t)
|
||||
{
|
||||
return t < 0.5f ? 4 * t * t * t : 1 - MathF.Pow(-2 * t + 2, 3) / 2;
|
||||
}
|
||||
public static float EaseInOutQuart(float t)
|
||||
{
|
||||
return t < 0.5f ? 8 * t * t * t * t : 1 - MathF.Pow(-2 * t + 2, 4) / 2;
|
||||
}
|
||||
|
||||
public static float EaseInOutQuint(float t)
|
||||
{
|
||||
return t < 0.5f ? 16 * t * t * t * t * t : 1 - MathF.Pow(-2 * t + 2, 5) / 2;
|
||||
}
|
||||
|
||||
public static float EaseInOutExpo(float t)
|
||||
{
|
||||
return t == 0
|
||||
@@ -20,25 +43,70 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
: (2 - MathF.Pow(2, -20 * t + 10)) / 2;
|
||||
}
|
||||
|
||||
public static float EaseInOutQuad(float t)
|
||||
public static float EaseInOutCirc(float t)
|
||||
{
|
||||
return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
||||
return t < 0.5f
|
||||
? (1 - MathF.Sqrt(1 - MathF.Pow(2 * t, 2))) / 2
|
||||
: (MathF.Sqrt(1 - MathF.Pow(-2 * t + 2, 2)) + 1) / 2;
|
||||
}
|
||||
|
||||
public static float EaseInQuad(float t) => t * t;
|
||||
|
||||
public static float EaseOutQuad(float t) => t * (2 - t);
|
||||
|
||||
public static float Linear(float t) => t;
|
||||
|
||||
public static float SmootherStep(float t)
|
||||
public static float EaseInOutBack(float t)
|
||||
{
|
||||
return t * t * t * (t * (6 * t - 15) + 10);
|
||||
float c1 = 1.70158f;
|
||||
float c2 = c1 * 1.525f;
|
||||
|
||||
return t < 0.5
|
||||
? (MathF.Pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
|
||||
: (MathF.Pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
|
||||
}
|
||||
|
||||
public static float EaseInOutElastic(float t)
|
||||
{
|
||||
if (t == 0 || t == 1) return t;
|
||||
float p = 0.3f;
|
||||
float s = p / 4;
|
||||
return t < 0.5f
|
||||
? -(MathF.Pow(2, 20 * t - 10) * MathF.Sin((20 * t - 11.125f) * (2 * MathF.PI) / p)) / 2
|
||||
: (MathF.Pow(2, -20 * t + 10) * MathF.Sin((20 * t - 11.125f) * (2 * MathF.PI) / p)) / 2 + 1;
|
||||
}
|
||||
|
||||
private static float EaseOutBounce(float t)
|
||||
{
|
||||
if (t < 4 / 11f)
|
||||
{
|
||||
return (121 * t * t) / 16f;
|
||||
}
|
||||
else if (t < 8 / 11f)
|
||||
{
|
||||
return (363 / 40f * t * t) - (99 / 10f * t) + 17 / 5f;
|
||||
}
|
||||
else if (t < 9 / 10f)
|
||||
{
|
||||
return (4356 / 361f * t * t) - (35442 / 1805f * t) + 16061 / 1805f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (54 / 5f * t * t) - (513 / 25f * t) + 268 / 25f;
|
||||
}
|
||||
}
|
||||
|
||||
public static float EaseInOutBounce(float t)
|
||||
{
|
||||
if (t < 0.5f)
|
||||
{
|
||||
return (1 - EaseOutBounce(1 - 2 * t)) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (1 + EaseOutBounce(2 * t - 1)) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public static float SmoothStep(float t)
|
||||
{
|
||||
return t * t * (3 - 2 * t);
|
||||
return t * t * (3f - 2f * t);
|
||||
}
|
||||
|
||||
public static float Linear(float t) => t;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,5 +171,22 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
await stream.AsStreamForRead().CopyToAsync(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
public static float GetAverageLuminance(CanvasBitmap bitmap)
|
||||
{
|
||||
var pixels = bitmap.GetPixelBytes();
|
||||
double sum = 0;
|
||||
for (int i = 0; i < pixels.Length; i += 4)
|
||||
{
|
||||
// BGRA
|
||||
byte b = pixels[i];
|
||||
byte g = pixels[i + 1];
|
||||
byte r = pixels[i + 2];
|
||||
// 忽略A
|
||||
double y = 0.299 * r + 0.587 * g + 0.114 * b;
|
||||
sum += y / 255.0;
|
||||
}
|
||||
return (float)(sum / (pixels.Length / 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
private List<List<LyricsLine>> _multiLangLyricsLines = [];
|
||||
|
||||
public List<List<LyricsLine>> Parse(string? raw, LyricsFormat? lyricsFormat = null, string? title = null, string? artist = null, int durationMs = 0)
|
||||
public List<List<LyricsLine>> Parse(string? raw, int durationMs)
|
||||
{
|
||||
_multiLangLyricsLines = [];
|
||||
if (raw == null)
|
||||
@@ -27,7 +27,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = durationMs,
|
||||
Text = App.ResourceLoader!.GetString("LyricsNotFound"),
|
||||
OriginalText = App.ResourceLoader!.GetString("LyricsNotFound"),
|
||||
CharTimings = [],
|
||||
},
|
||||
]
|
||||
@@ -35,7 +35,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (lyricsFormat)
|
||||
switch (raw.DetectFormat())
|
||||
{
|
||||
case LyricsFormat.Lrc:
|
||||
case LyricsFormat.Eslrc:
|
||||
@@ -136,7 +136,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
StartMs = start,
|
||||
EndMs = 0, // 稍后统一修正
|
||||
Text = text,
|
||||
OriginalText = text,
|
||||
CharTimings = [],
|
||||
};
|
||||
if (syllables != null && syllables.Count > 0)
|
||||
@@ -217,7 +217,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
StartMs = pStartMs,
|
||||
EndMs = 0,
|
||||
Text = text,
|
||||
OriginalText = text,
|
||||
CharTimings = charTimings,
|
||||
}
|
||||
);
|
||||
@@ -306,7 +306,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
StartMs = lineRead.StartTime ?? 0,
|
||||
EndMs = 0,
|
||||
Text = lineRead.Text,
|
||||
OriginalText = lineRead.Text,
|
||||
CharTimings = [],
|
||||
};
|
||||
|
||||
@@ -389,7 +389,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = linesInSingleLang[0].StartMs,
|
||||
Text = "● ● ●",
|
||||
OriginalText = "● ● ●",
|
||||
CharTimings = [],
|
||||
}
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
App.Current.Exit();
|
||||
}
|
||||
|
||||
public static T GetWindowByWindowType<T>()
|
||||
public static T? GetWindowByWindowType<T>()
|
||||
{
|
||||
foreach (var window in _activeWindows)
|
||||
{
|
||||
@@ -45,7 +45,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return castedWindow;
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException($"No window of type {typeof(T).Name} found.");
|
||||
return default;
|
||||
}
|
||||
public static void OpenOrShowWindow<T>()
|
||||
{
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Messages
|
||||
{
|
||||
public class ShowNotificatonMessage(Notification value) : ValueChangedMessage<Notification>(value) { }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class DetectLanguageResult
|
||||
{
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
|
||||
[JsonPropertyName("language")]
|
||||
public string Language { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class LanguageInfo : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial string Code { get; set; }
|
||||
[ObservableProperty]
|
||||
public partial string Name { get; set; }
|
||||
|
||||
public LanguageInfo(string code, string name)
|
||||
{
|
||||
Code = code;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class LyricsData
|
||||
{
|
||||
public int LanguageIndex { get; set; } = 0;
|
||||
|
||||
public List<LyricsLine> LyricsLines => MultiLangLyricsLines[LanguageIndex];
|
||||
|
||||
public List<List<LyricsLine>> MultiLangLyricsLines { get; set; } = [];
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,12 @@ namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class LyricsLine
|
||||
{
|
||||
public ValueTransition<float> AngleTransition { get; set; } = new(initialValue: 0f, durationSeconds: 0.3f);
|
||||
public ValueTransition<float> BlurAmountTransition { get; set; } = new(initialValue: 0f, durationSeconds: 0.3f);
|
||||
public ValueTransition<float> HighlightOpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: 0.3f);
|
||||
public ValueTransition<float> OpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: 0.3f);
|
||||
public ValueTransition<float> ScaleTransition { get; set; } = new(initialValue: 0.95f, durationSeconds: 0.3f);
|
||||
private const float _animationDuration = 0.3f;
|
||||
public ValueTransition<float> AngleTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration);
|
||||
public ValueTransition<float> BlurAmountTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration);
|
||||
public ValueTransition<float> HighlightOpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration);
|
||||
public ValueTransition<float> OpacityTransition { get; set; } = new(initialValue: 0f, durationSeconds: _animationDuration);
|
||||
public ValueTransition<float> ScaleTransition { get; set; } = new(initialValue: 0.95f, durationSeconds: _animationDuration);
|
||||
|
||||
public CanvasTextLayout? CanvasTextLayout { get; set; }
|
||||
|
||||
@@ -23,11 +24,10 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public List<CharTiming> CharTimings { get; set; } = [];
|
||||
|
||||
public int DurationMs => EndMs - StartMs;
|
||||
|
||||
public int EndMs { get; set; }
|
||||
|
||||
public int StartMs { get; set; }
|
||||
|
||||
public string Text { get; set; } = "";
|
||||
public string DisplayedText { get; set; } = "";
|
||||
public string OriginalText { get; set; } = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class LyricsSearchProviderInfo : ObservableObject
|
||||
public partial class MediaSourceProviderInfo : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial bool IsEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LyricsSearchProvider Provider { get; set; }
|
||||
public partial string Provider { get; set; }
|
||||
|
||||
public LyricsSearchProviderInfo() { }
|
||||
public MediaSourceProviderInfo() { }
|
||||
|
||||
public LyricsSearchProviderInfo(LyricsSearchProvider provider, bool isEnabled)
|
||||
public MediaSourceProviderInfo(string provider, bool isEnabled)
|
||||
{
|
||||
Provider = provider;
|
||||
IsEnabled = isEnabled;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class LyricsSearchProviderInfo : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial bool IsEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LyricsSearchProvider Provider { get; set; }
|
||||
|
||||
public LyricsSearchProviderInfo() { }
|
||||
|
||||
public LyricsSearchProviderInfo(LyricsSearchProvider provider, bool isEnabled)
|
||||
{
|
||||
Provider = provider;
|
||||
IsEnabled = isEnabled;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
@@ -9,7 +11,9 @@ namespace BetterLyrics.WinUI3.Models
|
||||
[ObservableProperty]
|
||||
public partial string? Album { get; set; }
|
||||
|
||||
public byte[]? AlbumArt { get; set; } = null;
|
||||
public SoftwareBitmap? AlbumArtSwBitmap { get; set; } = null;
|
||||
|
||||
public Color? AlbumArtAccentColor { get; set; } = null;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string Artist { get; set; }
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class TranslateResponse
|
||||
{
|
||||
[JsonPropertyName("translatedText")]
|
||||
public string TranslatedText { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Renderer"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
@@ -14,5 +15,15 @@
|
||||
x:Name="LyricsCanvas"
|
||||
Draw="LyricsCanvas_Draw"
|
||||
Update="LyricsCanvas_Update" />
|
||||
<Grid
|
||||
Margin="36"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Visibility="{x:Bind ViewModel.IsTranslating, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<FontIcon
|
||||
x:Name="RotatingIcon"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Renderer
|
||||
{
|
||||
|
||||
@@ -9,8 +9,11 @@ namespace BetterLyrics.WinUI3.Serialization
|
||||
{
|
||||
|
||||
[JsonSerializable(typeof(List<LyricsSearchProviderInfo>))]
|
||||
[JsonSerializable(typeof(List<MediaSourceProviderInfo>))]
|
||||
[JsonSerializable(typeof(List<LocalLyricsFolder>))]
|
||||
[JsonSerializable(typeof(List<string>))]
|
||||
[JsonSerializable(typeof(List<DetectLanguageResult>))]
|
||||
[JsonSerializable(typeof(TranslateResponse))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
internal partial class SourceGenerationContext : JsonSerializerContext { }
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public interface ILibreTranslateService
|
||||
{
|
||||
Task<string> TranslateAsync(string text, CancellationToken? token);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
|
||||
@@ -8,14 +9,14 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public interface IMusicSearchService
|
||||
{
|
||||
byte[]? SearchAlbumArtAsync(string title, string artist);
|
||||
Task <byte[]?> SearchAlbumArtAsync(string title, string artist, string album);
|
||||
|
||||
Task<(string?, LyricsFormat?)> SearchLyricsAsync(
|
||||
Task<string?> SearchLyricsAsync(
|
||||
string title,
|
||||
string artist,
|
||||
string album = "",
|
||||
double durationMs = 0.0,
|
||||
MusicSearchMatchMode matchMode = MusicSearchMatchMode.TitleAndArtist
|
||||
string album,
|
||||
double durationMs,
|
||||
CancellationToken token
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,6 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
event EventHandler<SongInfoChangedEventArgs>? SongInfoChanged;
|
||||
|
||||
bool IsPlaying { get; }
|
||||
|
||||
TimeSpan Position { get; }
|
||||
|
||||
SongInfo? SongInfo { get; }
|
||||
event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,24 +29,41 @@ namespace BetterLyrics.WinUI3.Services
|
||||
int DesktopWindowTop { get; set; }
|
||||
int DesktopWindowWidth { get; set; }
|
||||
int DesktopWindowHeight { get; set; }
|
||||
|
||||
int StandardWindowWidth { get; set; }
|
||||
int StandardWindowHeight { get; set; }
|
||||
int StandardWindowLeft { get; set; }
|
||||
int StandardWindowTop { get; set; }
|
||||
|
||||
bool AutoLockOnDesktopMode { get; set; }
|
||||
|
||||
string LibreTranslateServer { get; set; }
|
||||
int SelectedTargetLanguageIndex { get; set; }
|
||||
// Lyrics lib
|
||||
|
||||
List<LocalLyricsFolder> LocalLyricsFolders { get; set; }
|
||||
|
||||
// Lyrics style and effetc
|
||||
|
||||
LyricsAlignmentType LyricsAlignmentType { get; set; }
|
||||
TextAlignmentType LyricsAlignmentType { get; set; }
|
||||
TextAlignmentType SongInfoAlignmentType { get; set; }
|
||||
|
||||
int LyricsBlurAmount { get; set; }
|
||||
|
||||
Color LyricsCustomFontColor { get; set; }
|
||||
Color LyricsCustomBgFontColor { get; set; }
|
||||
Color LyricsCustomFgFontColor { get; set; }
|
||||
Color LyricsCustomStrokeFontColor { get; set; }
|
||||
|
||||
LyricsFontColorType LyricsFontColorType { get; set; }
|
||||
LyricsFontColorType LyricsBgFontColorType { get; set; }
|
||||
LyricsFontColorType LyricsFgFontColorType { get; set; }
|
||||
LyricsFontColorType LyricsStrokeFontColorType { get; set; }
|
||||
|
||||
int LyricsFontSize { get; set; }
|
||||
|
||||
ElementTheme LyricsBackgroundTheme { get; set; }
|
||||
|
||||
int LyricsFontStrokeWidth { get; set; }
|
||||
|
||||
LyricsFontWeight LyricsFontWeight { get; set; }
|
||||
|
||||
LineRenderingType LyricsGlowEffectScope { get; set; }
|
||||
@@ -55,7 +72,14 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
List<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
|
||||
|
||||
List<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; }
|
||||
|
||||
int LyricsVerticalEdgeOpacity { get; set; }
|
||||
|
||||
bool IgnoreFullscreenWindow { get; set; }
|
||||
|
||||
bool IsTranslationEnabled { get; set; }
|
||||
|
||||
LyricsDisplayType PreferredDisplayType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public class LibreTranslateService : ILibreTranslateService
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public LibreTranslateService(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
public async Task<string> TranslateAsync(string text, CancellationToken? token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
throw new ArgumentException("Text and target language must be provided.");
|
||||
}
|
||||
|
||||
string targetLangCode = AppInfo.GetAllTranslationLanguagesInfo()[_settingsService.SelectedTargetLanguageIndex].Code;
|
||||
|
||||
string originalLangCode = await DetectLanguageCode(text);
|
||||
token?.ThrowIfCancellationRequested();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(originalLangCode) || originalLangCode == targetLangCode)
|
||||
{
|
||||
return text; // No translation needed
|
||||
}
|
||||
|
||||
var url = $"{_settingsService.LibreTranslateServer}/translate";
|
||||
var response = await _httpClient.PostAsync(url, new FormUrlEncodedContent(
|
||||
[
|
||||
new("q", text),
|
||||
new("source", originalLangCode),
|
||||
new("target", targetLangCode),
|
||||
]));
|
||||
token?.ThrowIfCancellationRequested();
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
token?.ThrowIfCancellationRequested();
|
||||
|
||||
var result = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.TranslateResponse);
|
||||
return result?.TranslatedText ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task<string> DetectLanguageCode(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
throw new ArgumentException("Text must be provided.");
|
||||
}
|
||||
var url = $"{_settingsService.LibreTranslateServer}/detect";
|
||||
var response = await _httpClient.PostAsync(url, new FormUrlEncodedContent(
|
||||
[
|
||||
new("q", text),
|
||||
]));
|
||||
response.EnsureSuccessStatusCode();
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
var resultList = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.ListDetectLanguageResult);
|
||||
return resultList?.OrderByDescending(x => x.Confidence).FirstOrDefault()?.Language ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,50 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using iTunesSearch.Library;
|
||||
using Lyricify.Lyrics.Providers.Web.Kugou;
|
||||
using Lyricify.Lyrics.Searchers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using Lyricify.Lyrics.Providers.Web.Kugou;
|
||||
using Lyricify.Lyrics.Searchers;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public class MusicSearchService : IMusicSearchService
|
||||
{
|
||||
private readonly HttpClient _amllTtmlDbHttpClient;
|
||||
|
||||
private readonly HttpClient _lrcLibHttpClient;
|
||||
private readonly HttpClient _iTunesHttpClinet;
|
||||
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MusicSearchService(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_lrcLibHttpClient = new HttpClient();
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<MusicSearchService>>();
|
||||
|
||||
_lrcLibHttpClient = new();
|
||||
_lrcLibHttpClient.DefaultRequestHeaders.Add(
|
||||
"User-Agent",
|
||||
$"{AppInfo.AppName} {AppInfo.AppVersion} ({AppInfo.GithubUrl})"
|
||||
);
|
||||
_amllTtmlDbHttpClient = new HttpClient();
|
||||
_amllTtmlDbHttpClient = new();
|
||||
_iTunesHttpClinet = new();
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadAmllTtmlDbIndexAsync()
|
||||
{
|
||||
const string url = "https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/refs/heads/main/metadata/raw-lyrics-index.jsonl";
|
||||
const string url = "https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/refs/heads/main/metadata/raw-img-index.jsonl";
|
||||
try
|
||||
{
|
||||
using var response = await _amllTtmlDbHttpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
|
||||
@@ -59,7 +67,22 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
}
|
||||
|
||||
public byte[]? SearchAlbumArtAsync(string title, string artist)
|
||||
private static string GuessCountryCode(string album, string artist)
|
||||
{
|
||||
string s = album + artist;
|
||||
if (s.Any(c => c >= 0x4e00 && c <= 0x9fff)) // 中文
|
||||
return "cn";
|
||||
if (s.Any(c => (c >= 0x3040 && c <= 0x30ff) || (c >= 0x31f0 && c <= 0x31ff))) // 日文
|
||||
return "jp";
|
||||
if (s.Any(c => c >= 0xac00 && c <= 0xd7af)) // 韩文
|
||||
return "kr";
|
||||
if (s.Any(c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) // 英文
|
||||
return "us";
|
||||
// 其他情况
|
||||
return "us";
|
||||
}
|
||||
|
||||
public async Task<byte[]?> SearchAlbumArtAsync(string title, string artist, string album)
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalLyricsFolders)
|
||||
{
|
||||
@@ -80,14 +103,63 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
}
|
||||
|
||||
return await SearchiTunesAlbumArtAsync(artist, album);
|
||||
}
|
||||
|
||||
private async Task<byte[]?> SearchiTunesAlbumArtAsync(string artist, string album)
|
||||
{
|
||||
// Source: https://gist.github.com/mcworkaholic/82fbf203e3f1043bbe534b5b2974c0ce
|
||||
try
|
||||
{
|
||||
string format = ".jpg";
|
||||
var cachedAlbumArt = ReadAlbumArtCache(artist, album, format, AppInfo.iTunesAlbumArtCacheDirectory);
|
||||
|
||||
if (cachedAlbumArt != null)
|
||||
{
|
||||
return cachedAlbumArt;
|
||||
}
|
||||
|
||||
// Build the iTunes API URL
|
||||
string url = $"https://itunes.apple.com/search?term=" + artist + "+" + album + "&country=" + GuessCountryCode(album, artist) + "&entity=album";
|
||||
url.Replace(" ", "-");
|
||||
// Make a request to the API
|
||||
|
||||
HttpResponseMessage response = await _iTunesHttpClinet.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Parse the JSON response
|
||||
var data = JsonSerializer.Deserialize(responseBody, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
|
||||
if (data.TryGetProperty("results", out var results) && results.ValueKind == JsonValueKind.Array && results.GetArrayLength() > 0)
|
||||
{
|
||||
// Get the first result
|
||||
var result = results[0];
|
||||
if (result.TryGetProperty("artworkUrl100", out var artworkUrlProp))
|
||||
{
|
||||
string artworkUrl = artworkUrlProp.GetString()?.Replace("100x100bb.jpg", "1200x1200bb.jpg") ?? string.Empty;
|
||||
var fetched = await _iTunesHttpClinet.GetByteArrayAsync(artworkUrl);
|
||||
|
||||
if (fetched != null && fetched.Length > 0)
|
||||
{
|
||||
// Write to cache
|
||||
WriteAlbumArtCache(artist, album, fetched, format, AppInfo.iTunesAlbumArtCacheDirectory);
|
||||
return fetched;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error searching iTunes album art for {Artist} - {Album}", artist, album);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<(string?, LyricsFormat?)> SearchLyricsAsync(
|
||||
string title, string artist, string album = "", double durationMs = 0.0,
|
||||
MusicSearchMatchMode matchMode = MusicSearchMatchMode.TitleArtistAlbumAndDuration
|
||||
)
|
||||
public async Task<string?> SearchLyricsAsync(string title, string artist, string album, double durationMs, CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Searching img for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
|
||||
|
||||
foreach (var provider in _settingsService.LyricsSearchProvidersInfo)
|
||||
{
|
||||
if (!provider.IsEnabled)
|
||||
@@ -101,10 +173,10 @@ namespace BetterLyrics.WinUI3.Services
|
||||
// Check cache first
|
||||
if (provider.Provider.IsRemote())
|
||||
{
|
||||
cachedLyrics = ReadCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
cachedLyrics = ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
if (!string.IsNullOrWhiteSpace(cachedLyrics))
|
||||
{
|
||||
return (cachedLyrics, lyricsFormat);
|
||||
return cachedLyrics;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,16 +198,16 @@ namespace BetterLyrics.WinUI3.Services
|
||||
switch (provider.Provider)
|
||||
{
|
||||
case LyricsSearchProvider.LrcLib:
|
||||
searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000), matchMode);
|
||||
searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
|
||||
break;
|
||||
case LyricsSearchProvider.QQ:
|
||||
searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, matchMode, Searchers.QQMusic);
|
||||
searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
|
||||
break;
|
||||
case LyricsSearchProvider.Kugou:
|
||||
searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, matchMode, Searchers.Kugou);
|
||||
searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
|
||||
break;
|
||||
case LyricsSearchProvider.Netease:
|
||||
searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, matchMode, Searchers.Netease);
|
||||
searchedLyrics = await SearchUsingLyricifyAsync(title, artist, album, (int)durationMs, Searchers.Netease);
|
||||
break;
|
||||
case LyricsSearchProvider.AmllTtmlDb:
|
||||
searchedLyrics = await SearchAmllTtmlDbAsync(title, artist);
|
||||
@@ -145,25 +217,40 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
}
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchedLyrics))
|
||||
{
|
||||
if (provider.Provider.IsRemote())
|
||||
{
|
||||
WriteCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
}
|
||||
|
||||
return (searchedLyrics, lyricsFormat == LyricsFormat.NotSpecified ? searchedLyrics.DetectFormat() : lyricsFormat);
|
||||
return searchedLyrics;
|
||||
}
|
||||
}
|
||||
|
||||
return (null, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool MusicMatch(string fileName, string title, string artist)
|
||||
{
|
||||
return fileName.Contains(title) && fileName.Contains(artist);
|
||||
var normFileName = Normalize(fileName);
|
||||
var normTitle = Normalize(title);
|
||||
var normArtist = Normalize(artist);
|
||||
|
||||
// 常见两种顺序
|
||||
return normFileName == normTitle + normArtist
|
||||
|| normFileName == normArtist + normTitle;
|
||||
}
|
||||
|
||||
// 预处理:去除空格、括号、下划线、横杠、点、大小写等
|
||||
static string Normalize(string s) =>
|
||||
new string(s
|
||||
.Where(c => char.IsLetterOrDigit(c))
|
||||
.ToArray())
|
||||
.ToLowerInvariant();
|
||||
|
||||
private static string SanitizeFileName(string fileName, char replacement = '_')
|
||||
{
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
@@ -224,7 +311,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ReadCache(string title, string artist, LyricsFormat format, string cacheFolderPath)
|
||||
private string? ReadLyricsCache(string title, string artist, LyricsFormat format, string cacheFolderPath)
|
||||
{
|
||||
var safeArtist = SanitizeFileName(artist);
|
||||
var safeTitle = SanitizeFileName(title);
|
||||
@@ -236,6 +323,18 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[]? ReadAlbumArtCache(string album, string artist, string format, string cacheFolderPath)
|
||||
{
|
||||
var safeArtist = SanitizeFileName(artist);
|
||||
var safeAlbum = SanitizeFileName(album);
|
||||
var cacheFilePath = Path.Combine(cacheFolderPath, $"{safeArtist} - {safeAlbum}{format}");
|
||||
if (File.Exists(cacheFilePath))
|
||||
{
|
||||
return File.ReadAllBytes(cacheFilePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<string?> SearchAmllTtmlDbAsync(string title, string artist)
|
||||
{
|
||||
// 检索本地 JSONL 索引文件,查找 rawLyricFile
|
||||
@@ -289,7 +388,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return null;
|
||||
|
||||
// 下载歌词内容
|
||||
var url = $"https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/refs/heads/main/raw-lyrics/{rawLyricFile}";
|
||||
var url = $"https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/refs/heads/main/raw-img/{rawLyricFile}";
|
||||
try
|
||||
{
|
||||
var response = await _amllTtmlDbHttpClient.GetAsync(url);
|
||||
@@ -303,20 +402,15 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string?> SearchLrcLibAsync(string title, string artist, string album, int duration, MusicSearchMatchMode matchMode)
|
||||
private async Task<string?> SearchLrcLibAsync(string title, string artist, string album, int duration)
|
||||
{
|
||||
// Build API query URL
|
||||
var url =
|
||||
$"https://lrclib.net/api/search?"
|
||||
+ $"track_name={Uri.EscapeDataString(title)}&"
|
||||
+ $"artist_name={Uri.EscapeDataString(artist)}";
|
||||
|
||||
if (matchMode == MusicSearchMatchMode.TitleArtistAlbumAndDuration)
|
||||
{
|
||||
url +=
|
||||
$"&album_name={Uri.EscapeDataString(album)}"
|
||||
+ $"&durationMs={Uri.EscapeDataString(duration.ToString())}";
|
||||
}
|
||||
$"https://lrclib.net/api/search?" +
|
||||
$"track_name={Uri.EscapeDataString(title)}&" +
|
||||
$"artist_name={Uri.EscapeDataString(artist)}&" +
|
||||
$"&album_name={Uri.EscapeDataString(album)}" +
|
||||
$"&durationMs={Uri.EscapeDataString(duration.ToString())}";
|
||||
|
||||
var response = await _lrcLibHttpClient.GetAsync(url);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
@@ -347,16 +441,14 @@ namespace BetterLyrics.WinUI3.Services
|
||||
string artist,
|
||||
string album,
|
||||
int durationMs,
|
||||
MusicSearchMatchMode matchMode,
|
||||
Searchers searchers
|
||||
)
|
||||
{
|
||||
var result = await SearchersHelper.GetSearcher(searchers).SearchForResult(
|
||||
new Lyricify.Lyrics.Models.TrackMultiArtistMetadata()
|
||||
{
|
||||
DurationMs = matchMode == MusicSearchMatchMode.TitleArtistAlbumAndDuration ? durationMs : null,
|
||||
Album = matchMode == MusicSearchMatchMode.TitleArtistAlbumAndDuration ? album : null,
|
||||
AlbumArtists = [artist],
|
||||
DurationMs = durationMs,
|
||||
Album = album,
|
||||
Artists = [artist],
|
||||
Title = title,
|
||||
}
|
||||
@@ -364,7 +456,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
if (result is QQMusicSearchResult qqResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Decrypter.Qrc.Helper.GetLyricsAsync(qqResult.Id);
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id);
|
||||
var original = response?.Lyrics;
|
||||
return original;
|
||||
}
|
||||
@@ -375,7 +467,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
else if (result is KugouSearchResult kugouResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(kugouResult.Hash);
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(hash: kugouResult.Hash);
|
||||
if (response?.Candidates.FirstOrDefault() is SearchLyricsResponse.Candidate candidate)
|
||||
{
|
||||
return Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyrics(
|
||||
@@ -388,7 +480,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
private void WriteCache(
|
||||
private void WriteLyricsCache(
|
||||
string title,
|
||||
string artist,
|
||||
string lyrics,
|
||||
@@ -404,5 +496,22 @@ namespace BetterLyrics.WinUI3.Services
|
||||
);
|
||||
File.WriteAllText(cacheFilePath, lyrics);
|
||||
}
|
||||
|
||||
private void WriteAlbumArtCache(
|
||||
string album,
|
||||
string artist,
|
||||
byte[] img,
|
||||
string format,
|
||||
string cacheFolderPath
|
||||
)
|
||||
{
|
||||
var safeArtist = SanitizeFileName(artist);
|
||||
var safeAlbum = SanitizeFileName(album);
|
||||
var cacheFilePath = Path.Combine(
|
||||
cacheFolderPath,
|
||||
$"{safeArtist} - {safeAlbum}{format}"
|
||||
);
|
||||
File.WriteAllBytes(cacheFilePath, img);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,208 +1,257 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using CommunityToolkit.WinUI;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Windows.ApplicationModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Media.Control;
|
||||
using Windows.Storage.Streams;
|
||||
using WindowsMediaController;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public partial class PlaybackService : IPlaybackService
|
||||
public partial class PlaybackService : BaseViewModel, IPlaybackService, IRecipient<PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>>>
|
||||
{
|
||||
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
private readonly IMusicSearchService _musicSearchService;
|
||||
private readonly ILogger<PlaybackService> _logger;
|
||||
|
||||
private GlobalSystemMediaTransportControlsSession? _currentSession = null;
|
||||
private readonly MediaManager _mediaManager = new();
|
||||
|
||||
private GlobalSystemMediaTransportControlsSessionManager? _sessionManager = null;
|
||||
private CancellationTokenSource? _mediaPropsCts;
|
||||
|
||||
public PlaybackService(ISettingsService settingsService, IMusicSearchService musicSearchService)
|
||||
{
|
||||
_musicSearchService = musicSearchService;
|
||||
InitMediaManager().ConfigureAwait(true);
|
||||
}
|
||||
private List<MediaSourceProviderInfo> _mediaSourceProvidersInfo;
|
||||
|
||||
public event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
|
||||
|
||||
public event EventHandler<PositionChangedEventArgs>? PositionChanged;
|
||||
|
||||
public event EventHandler<SongInfoChangedEventArgs>? SongInfoChanged;
|
||||
public event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
|
||||
|
||||
public bool IsPlaying { get; private set; }
|
||||
|
||||
public TimeSpan Position { get; private set; }
|
||||
|
||||
public SongInfo? SongInfo { get; private set; }
|
||||
|
||||
private void CurrentSession_MediaPropertiesChanged(GlobalSystemMediaTransportControlsSession? sender, MediaPropertiesChangedEventArgs? args)
|
||||
public PlaybackService(ISettingsService settingsService, IMusicSearchService musicSearchService) : base(settingsService)
|
||||
{
|
||||
App.DispatcherQueueTimer!.Debounce(
|
||||
async () =>
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProps = null;
|
||||
if (sender == null)
|
||||
{
|
||||
SongInfo = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
mediaProps = await sender.TryGetMediaPropertiesAsync();
|
||||
}
|
||||
catch (Exception) { }
|
||||
_musicSearchService = musicSearchService;
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<PlaybackService>>();
|
||||
|
||||
if (mediaProps == null)
|
||||
{
|
||||
SongInfo = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SongInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProps.Title,
|
||||
Artist = mediaProps.Artist,
|
||||
Album = mediaProps?.AlbumTitle ?? string.Empty,
|
||||
DurationMs = _currentSession
|
||||
?.GetTimelineProperties()
|
||||
.EndTime.TotalMilliseconds,
|
||||
SourceAppUserModelId = _currentSession?.SourceAppUserModelId,
|
||||
};
|
||||
|
||||
if (mediaProps?.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
{
|
||||
SongInfo.AlbumArt = await ImageHelper.ToByteArrayAsync(
|
||||
streamReference
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
SongInfo.AlbumArt = _musicSearchService.SearchAlbumArtAsync(
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist
|
||||
);
|
||||
|
||||
if (SongInfo.AlbumArt == null)
|
||||
{
|
||||
SongInfo.AlbumArt =
|
||||
await ImageHelper.CreateTextPlaceholderBytesAsync(
|
||||
$"{SongInfo.Artist} - {SongInfo.Title}",
|
||||
400,
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(SongInfo));
|
||||
}
|
||||
);
|
||||
},
|
||||
TimeSpan.FromMilliseconds(1000)
|
||||
);
|
||||
_mediaSourceProvidersInfo = _settingsService.MediaSourceProvidersInfo;
|
||||
InitMediaManager();
|
||||
}
|
||||
|
||||
private void CurrentSession_PlaybackInfoChanged(GlobalSystemMediaTransportControlsSession? sender, PlaybackInfoChangedEventArgs? args)
|
||||
private bool IsMediaSourceEnabled(string id)
|
||||
{
|
||||
if (sender == null)
|
||||
return _mediaSourceProvidersInfo.FirstOrDefault(s => s.Provider == id)?.IsEnabled ?? true;
|
||||
}
|
||||
|
||||
private void InitMediaManager()
|
||||
{
|
||||
_mediaManager.Start();
|
||||
|
||||
_mediaManager.OnAnySessionOpened += MediaManager_OnAnySessionOpened;
|
||||
_mediaManager.OnAnySessionClosed += MediaManager_OnAnySessionClosed;
|
||||
_mediaManager.OnFocusedSessionChanged += MediaManager_OnFocusedSessionChanged;
|
||||
_mediaManager.OnAnyMediaPropertyChanged += MediaManager_OnAnyMediaPropertyChanged;
|
||||
_mediaManager.OnAnyPlaybackStateChanged += MediaManager_OnAnyPlaybackStateChanged;
|
||||
_mediaManager.OnAnyTimelinePropertyChanged += MediaManager_OnAnyTimelinePropertyChanged;
|
||||
|
||||
MediaManager_OnFocusedSessionChanged(_mediaManager.GetFocusedSession());
|
||||
}
|
||||
|
||||
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession mediaSession)
|
||||
{
|
||||
if (mediaSession == null || !IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId))
|
||||
{
|
||||
IsPlaying = false;
|
||||
SendNullMessages();
|
||||
}
|
||||
else
|
||||
{
|
||||
var playbackState = sender.GetPlaybackInfo().PlaybackStatus;
|
||||
// _logger.LogDebug(playbackState.ToString());
|
||||
|
||||
switch (playbackState)
|
||||
_dispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Closed:
|
||||
case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Opened:
|
||||
case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Changing:
|
||||
case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Stopped:
|
||||
case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Paused:
|
||||
IsPlaying = false;
|
||||
break;
|
||||
case GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing:
|
||||
IsPlaying = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
var props = await mediaSession.ControlSession.TryGetMediaPropertiesAsync();
|
||||
MediaManager_OnAnyMediaPropertyChanged(mediaSession, props);
|
||||
MediaManager_OnAnyPlaybackStateChanged(mediaSession, mediaSession.ControlSession.GetPlaybackInfo());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "TryGetMediaPropertiesAsync failed");
|
||||
SendNullMessages();
|
||||
}
|
||||
});
|
||||
}
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(IsPlaying));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void CurrentSession_TimelinePropertiesChanged(GlobalSystemMediaTransportControlsSession? sender, TimelinePropertiesChangedEventArgs? args)
|
||||
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties)
|
||||
{
|
||||
if (sender == null)
|
||||
{
|
||||
Position = TimeSpan.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
Position = sender.GetTimelineProperties().Position;
|
||||
}
|
||||
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
|
||||
|
||||
_dispatcherQueue.TryEnqueue(
|
||||
DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(Position));
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(timelineProperties.Position));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async Task InitMediaManager()
|
||||
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo)
|
||||
{
|
||||
_sessionManager = await GlobalSystemMediaTransportControlsSessionManager.RequestAsync();
|
||||
_sessionManager.CurrentSessionChanged += SessionManager_CurrentSessionChanged;
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
|
||||
|
||||
SessionManager_CurrentSessionChanged(_sessionManager, null);
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(playbackInfo.PlaybackStatus switch
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
|
||||
_ => false,
|
||||
}));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void SessionManager_CurrentSessionChanged(
|
||||
GlobalSystemMediaTransportControlsSessionManager sender,
|
||||
CurrentSessionChangedEventArgs? args
|
||||
)
|
||||
private async void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
|
||||
{
|
||||
// _logger.LogDebug("SessionManager_CurrentSessionChanged");
|
||||
// Unregister events associated with the previous session
|
||||
if (_currentSession != null)
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
|
||||
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
string id = mediaSession.ControlSession.SourceAppUserModelId;
|
||||
if (!IsMediaSourceEnabled(id) || mediaSession != _mediaManager.GetFocusedSession()) return;
|
||||
|
||||
_mediaPropsCts?.Cancel();
|
||||
var cts = new CancellationTokenSource();
|
||||
_mediaPropsCts = cts;
|
||||
var token = cts.Token;
|
||||
|
||||
try
|
||||
{
|
||||
_currentSession.MediaPropertiesChanged -= CurrentSession_MediaPropertiesChanged;
|
||||
_currentSession.PlaybackInfoChanged -= CurrentSession_PlaybackInfoChanged;
|
||||
_currentSession.TimelinePropertiesChanged -=
|
||||
CurrentSession_TimelinePropertiesChanged;
|
||||
SongInfo? songInfo;
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
songInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProperties.Title,
|
||||
Artist = mediaProperties.Artist,
|
||||
Album = mediaProperties.AlbumTitle,
|
||||
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
SourceAppUserModelId = id,
|
||||
};
|
||||
|
||||
byte[]? bytes;
|
||||
|
||||
bytes = await _musicSearchService.SearchAlbumArtAsync(
|
||||
songInfo.Title,
|
||||
songInfo.Artist,
|
||||
songInfo.Album
|
||||
);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (bytes == null)
|
||||
{
|
||||
if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
{
|
||||
bytes = await ImageHelper.ToByteArrayAsync(streamReference);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes = await ImageHelper.CreateTextPlaceholderBytesAsync($"{songInfo.Artist} - {songInfo.Title}", 400, 400);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
await stream.WriteAsync(bytes.AsBuffer());
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var decoder = await BitmapDecoder.CreateAsync(stream);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
songInfo.AlbumArtSwBitmap?.Dispose();
|
||||
songInfo.AlbumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
songInfo.AlbumArtAccentColor = ImageHelper.GetAccentColorsFromByte(bytes).FirstOrDefault();
|
||||
|
||||
if (!token.IsCancellationRequested)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(songInfo));
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
// Record and register events for current session
|
||||
_currentSession = sender.GetCurrentSession();
|
||||
|
||||
if (_currentSession != null)
|
||||
private void MediaManager_OnAnySessionClosed(MediaManager.MediaSession mediaSession)
|
||||
{
|
||||
if (_mediaManager.CurrentMediaSessions.Count == 0)
|
||||
{
|
||||
_currentSession.MediaPropertiesChanged += CurrentSession_MediaPropertiesChanged;
|
||||
_currentSession.PlaybackInfoChanged += CurrentSession_PlaybackInfoChanged;
|
||||
_currentSession.TimelinePropertiesChanged +=
|
||||
CurrentSession_TimelinePropertiesChanged;
|
||||
SendNullMessages();
|
||||
}
|
||||
}
|
||||
|
||||
CurrentSession_MediaPropertiesChanged(_currentSession, null);
|
||||
CurrentSession_PlaybackInfoChanged(_currentSession, null);
|
||||
CurrentSession_TimelinePropertiesChanged(_currentSession, null);
|
||||
private void MediaManager_OnAnySessionOpened(MediaManager.MediaSession mediaSession)
|
||||
{
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
}
|
||||
|
||||
private void RecordMediaSourceProviderInfo(MediaManager.MediaSession mediaSession)
|
||||
{
|
||||
var id = mediaSession?.ControlSession?.SourceAppUserModelId;
|
||||
if (string.IsNullOrEmpty(id)) return;
|
||||
|
||||
var found = _mediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == id);
|
||||
if (found == null)
|
||||
{
|
||||
_mediaSourceProvidersInfo.Add(new MediaSourceProviderInfo(id, true));
|
||||
_settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo;
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
MediaSourceProvidersInfoChanged?.Invoke(this, new MediaSourceProvidersInfoEventArgs(_mediaSourceProvidersInfo));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void SendNullMessages()
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(null));
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(false));
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(TimeSpan.Zero));
|
||||
});
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.MediaSourceProvidersInfo))
|
||||
{
|
||||
_mediaSourceProvidersInfo = [.. message.NewValue];
|
||||
_settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo;
|
||||
MediaManager_OnFocusedSessionChanged(_mediaManager.GetFocusedSession());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
@@ -10,6 +7,9 @@ using BetterLyrics.WinUI3.Serialization;
|
||||
using CommunityToolkit.WinUI.Helpers;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Windows.Storage;
|
||||
using Windows.UI;
|
||||
|
||||
@@ -17,13 +17,15 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public class SettingsService : ISettingsService
|
||||
{
|
||||
public const string LyricsCustomFontColorKey = "LyricsCustomFontColor";
|
||||
public const string LyricsCustomBgFontColorKey = "LyricsCustomBgFontColor";
|
||||
public const string LyricsCustomFgFontColorKey = "LyricsCustomFgFontColor";
|
||||
public const string LyricsCustomStrokeFontColorKey = "LyricsCustomStrokeFontColor";
|
||||
|
||||
// App behavior
|
||||
|
||||
private const string AutoStartWindowTypeKey = "AutoStartWindowType";
|
||||
|
||||
private const string CoverImageRadiusKey = "CoverImageRadius";
|
||||
private const string CoverImageRadiusKey = "AlbumArtCornerRadius";
|
||||
private const string CoverOverlayBlurAmountKey = "CoverOverlayBlurAmount";
|
||||
private const string CoverOverlayOpacityKey = "CoverOverlayOpacity";
|
||||
private const string IsCoverOverlayEnabledKey = "IsCoverOverlayEnabled";
|
||||
@@ -32,6 +34,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private const string DesktopWindowTopKey = "DesktopWindowTop";
|
||||
private const string DesktopWindowWidthKey = "DesktopWindowWidth";
|
||||
private const string DesktopWindowHeightKey = "DesktopWindowHeight";
|
||||
|
||||
private const string StandardWindowLeftKey = "StandardWindowLeft";
|
||||
private const string StandardWindowTopKey = "StandardWindowTop";
|
||||
private const string StandardWindowWidthKey = "StandardWindowWidth";
|
||||
private const string StandardWindowHeightKey = "StandardWindowHeight";
|
||||
|
||||
private const string AutoLockOnDesktopModeKey = "AutoLockOnDesktopMode";
|
||||
|
||||
private const string IsDynamicCoverOverlayEnabledKey = "IsDynamicCoverOverlayEnabled";
|
||||
@@ -41,9 +49,16 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private const string LanguageKey = "Language";
|
||||
|
||||
private const string LocalLyricsFoldersKey = "LocalLyricsFolders";
|
||||
private const string LyricsAlignmentTypeKey = "LyricsAlignmentType";
|
||||
private const string LyricsAlignmentTypeKey = "TextAlignmentType";
|
||||
private const string SongInfoAlignmentTypeKey = "SongInfoAlignmentType";
|
||||
private const string LyricsBlurAmountKey = "LyricsBlurAmount";
|
||||
private const string LyricsFontColorTypeKey = "LyricsFontColorType";
|
||||
|
||||
private const string LyricsBgFontColorTypeKey = "_lyricsBgFontColorType";
|
||||
private const string LyricsFgFontColorTypeKey = "LyricsFgFontColorType";
|
||||
private const string LyricsStrokeFontColorTypeKey = "LyricsStrokeFontColorType";
|
||||
|
||||
private const string LyricsFontStrokeWidthKey = "LyricsFontStrokeWidth";
|
||||
|
||||
private const string LyricsFontSizeKey = "LyricsFontSize";
|
||||
private const string LyricsFontWeightKey = "LyricsFontWeightKey";
|
||||
private const string LyricsGlowEffectScopeKey = "LyricsGlowEffectScope";
|
||||
@@ -51,6 +66,18 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private const string LyricsSearchProvidersInfoKey = "LyricsSearchProvidersInfo";
|
||||
private const string LyricsVerticalEdgeOpacityKey = "LyricsVerticalEdgeOpacity";
|
||||
|
||||
private const string MediaSourceProvidersInfoKey = "MediaSourceProvidersInfo";
|
||||
|
||||
private const string IsTranslationEnabledKey = "IsTranslationEnabled";
|
||||
private const string LibreTranslateServerKey = "LibreTranslateServer";
|
||||
private const string SelectedTargetLanguageIndexKey = "SelectedTargetLanguageIndex";
|
||||
|
||||
private const string LyricsBackgroundThemeKey = "LyricsBackgroundTheme";
|
||||
|
||||
private const string IgnoreFullscreenWindowKey = "IgnoreFullscreenWindow";
|
||||
|
||||
private const string PreferredDisplayTypeKey = "PreferredDisplayTypeKey";
|
||||
|
||||
private readonly ApplicationDataContainer _localSettings;
|
||||
|
||||
public SettingsService()
|
||||
@@ -69,6 +96,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SourceGenerationContext.Default.ListLyricsSearchProviderInfo
|
||||
)
|
||||
);
|
||||
SetDefault(MediaSourceProvidersInfoKey, "[]");
|
||||
if (LyricsSearchProvidersInfo.Count != Enum.GetValues<LyricsSearchProvider>().Length)
|
||||
{
|
||||
LyricsSearchProvidersInfo = Enum.GetValues<LyricsSearchProvider>()
|
||||
@@ -83,31 +111,70 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
// App appearance
|
||||
SetDefault(LanguageKey, (int)Language.FollowSystem);
|
||||
SetDefault(DesktopWindowHeightKey, 400);
|
||||
SetDefault(DesktopWindowLeftKey, 0);
|
||||
SetDefault(DesktopWindowTopKey, 0);
|
||||
SetDefault(DesktopWindowWidthKey, 600);
|
||||
|
||||
SetDefault(DesktopWindowHeightKey, 600);
|
||||
SetDefault(DesktopWindowLeftKey, 200);
|
||||
SetDefault(DesktopWindowTopKey, 200);
|
||||
SetDefault(DesktopWindowWidthKey, 1200);
|
||||
|
||||
SetDefault(StandardWindowHeightKey, 800);
|
||||
SetDefault(StandardWindowLeftKey, 200);
|
||||
SetDefault(StandardWindowTopKey, 200);
|
||||
SetDefault(StandardWindowWidthKey, 1600);
|
||||
|
||||
SetDefault(AutoLockOnDesktopModeKey, false);
|
||||
// App behavior
|
||||
SetDefault(AutoStartWindowTypeKey, (int)AutoStartWindowType.StandardMode);
|
||||
// Album art
|
||||
SetDefault(IsCoverOverlayEnabledKey, true);
|
||||
SetDefault(IsDynamicCoverOverlayEnabledKey, true);
|
||||
SetDefault(CoverOverlayOpacityKey, 75); // 100 % = 1.0
|
||||
SetDefault(CoverOverlayOpacityKey, 100); // 100 % = 1.0
|
||||
SetDefault(CoverOverlayBlurAmountKey, 200);
|
||||
SetDefault(CoverImageRadiusKey, 24); // 24 %
|
||||
SetDefault(CoverImageRadiusKey, 12); // 12 %
|
||||
// Lyrics
|
||||
SetDefault(LyricsAlignmentTypeKey, (int)LyricsAlignmentType.Center);
|
||||
SetDefault(LyricsAlignmentTypeKey, (int)TextAlignmentType.Center);
|
||||
SetDefault(SongInfoAlignmentTypeKey, (int)TextAlignmentType.Left);
|
||||
SetDefault(LyricsFontWeightKey, (int)LyricsFontWeight.Bold);
|
||||
SetDefault(LyricsBlurAmountKey, 5);
|
||||
SetDefault(LyricsFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed);
|
||||
SetDefault(LyricsCustomFontColorKey, Colors.White.ToInt());
|
||||
|
||||
SetDefault(LyricsBackgroundThemeKey, (int)ElementTheme.Default);
|
||||
|
||||
SetDefault(LyricsBgFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed);
|
||||
SetDefault(LyricsFgFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed);
|
||||
SetDefault(LyricsStrokeFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed);
|
||||
|
||||
SetDefault(LyricsCustomBgFontColorKey, Colors.White.ToInt());
|
||||
SetDefault(LyricsCustomFgFontColorKey, Colors.White.ToInt());
|
||||
SetDefault(LyricsCustomStrokeFontColorKey, Colors.White.ToInt());
|
||||
|
||||
SetDefault(LyricsFontSizeKey, 28);
|
||||
SetDefault(LyricsLineSpacingFactorKey, 0.5f);
|
||||
SetDefault(LyricsVerticalEdgeOpacityKey, 0);
|
||||
SetDefault(IsLyricsGlowEffectEnabledKey, true);
|
||||
SetDefault(LyricsGlowEffectScopeKey, (int)LineRenderingType.CurrentCharOnly);
|
||||
SetDefault(IsFanLyricsEnabledKey, false);
|
||||
|
||||
SetDefault(LibreTranslateServerKey, "");
|
||||
SetDefault(IsTranslationEnabledKey, false);
|
||||
SetDefault(SelectedTargetLanguageIndexKey, 6);
|
||||
|
||||
SetDefault(LyricsFontStrokeWidthKey, 3);
|
||||
|
||||
SetDefault(IgnoreFullscreenWindowKey, false);
|
||||
|
||||
SetDefault(PreferredDisplayTypeKey, (int)LyricsDisplayType.SplitView);
|
||||
}
|
||||
|
||||
public LyricsDisplayType PreferredDisplayType
|
||||
{
|
||||
get => (LyricsDisplayType)GetValue<int>(PreferredDisplayTypeKey);
|
||||
set => SetValue(PreferredDisplayTypeKey, (int)value);
|
||||
}
|
||||
|
||||
public ElementTheme LyricsBackgroundTheme
|
||||
{
|
||||
get => (ElementTheme)GetValue<int>(LyricsBackgroundThemeKey);
|
||||
set => SetValue(LyricsBackgroundThemeKey, (int)value);
|
||||
}
|
||||
|
||||
public AutoStartWindowType AutoStartWindowType
|
||||
@@ -139,6 +206,31 @@ namespace BetterLyrics.WinUI3.Services
|
||||
get => GetValue<int>(DesktopWindowHeightKey);
|
||||
set => SetValue(DesktopWindowHeightKey, value);
|
||||
}
|
||||
|
||||
public int StandardWindowLeft
|
||||
{
|
||||
get => GetValue<int>(StandardWindowLeftKey);
|
||||
set => SetValue(StandardWindowLeftKey, value);
|
||||
}
|
||||
|
||||
public int StandardWindowTop
|
||||
{
|
||||
get => GetValue<int>(StandardWindowTopKey);
|
||||
set => SetValue(StandardWindowTopKey, value);
|
||||
}
|
||||
|
||||
public int StandardWindowWidth
|
||||
{
|
||||
get => GetValue<int>(StandardWindowWidthKey);
|
||||
set => SetValue(StandardWindowWidthKey, value);
|
||||
}
|
||||
|
||||
public int StandardWindowHeight
|
||||
{
|
||||
get => GetValue<int>(StandardWindowHeightKey);
|
||||
set => SetValue(StandardWindowHeightKey, value);
|
||||
}
|
||||
|
||||
public bool AutoLockOnDesktopMode
|
||||
{
|
||||
get => GetValue<bool>(AutoLockOnDesktopModeKey);
|
||||
@@ -210,28 +302,64 @@ namespace BetterLyrics.WinUI3.Services
|
||||
);
|
||||
}
|
||||
|
||||
public LyricsAlignmentType LyricsAlignmentType
|
||||
public TextAlignmentType LyricsAlignmentType
|
||||
{
|
||||
get => (LyricsAlignmentType)GetValue<int>(LyricsAlignmentTypeKey);
|
||||
get => (TextAlignmentType)GetValue<int>(LyricsAlignmentTypeKey);
|
||||
set => SetValue(LyricsAlignmentTypeKey, (int)value);
|
||||
}
|
||||
|
||||
public TextAlignmentType SongInfoAlignmentType
|
||||
{
|
||||
get => (TextAlignmentType)GetValue<int>(SongInfoAlignmentTypeKey);
|
||||
set => SetValue(SongInfoAlignmentTypeKey, (int)value);
|
||||
}
|
||||
|
||||
public int LyricsBlurAmount
|
||||
{
|
||||
get => GetValue<int>(LyricsBlurAmountKey);
|
||||
set => SetValue(LyricsBlurAmountKey, value);
|
||||
}
|
||||
|
||||
public Color LyricsCustomFontColor
|
||||
public Color LyricsCustomBgFontColor
|
||||
{
|
||||
get => GetValue<int>(LyricsCustomFontColorKey)!.ToColor();
|
||||
set => SetValue(LyricsCustomFontColorKey, value.ToInt());
|
||||
get => GetValue<int>(LyricsCustomBgFontColorKey)!.ToColor();
|
||||
set => SetValue(LyricsCustomBgFontColorKey, value.ToInt());
|
||||
}
|
||||
|
||||
public LyricsFontColorType LyricsFontColorType
|
||||
public Color LyricsCustomFgFontColor
|
||||
{
|
||||
get => (LyricsFontColorType)GetValue<int>(LyricsFontColorTypeKey);
|
||||
set => SetValue(LyricsFontColorTypeKey, (int)value);
|
||||
get => GetValue<int>(LyricsCustomFgFontColorKey)!.ToColor();
|
||||
set => SetValue(LyricsCustomFgFontColorKey, value.ToInt());
|
||||
}
|
||||
|
||||
public Color LyricsCustomStrokeFontColor
|
||||
{
|
||||
get => GetValue<int>(LyricsCustomStrokeFontColorKey)!.ToColor();
|
||||
set => SetValue(LyricsCustomStrokeFontColorKey, value.ToInt());
|
||||
}
|
||||
|
||||
public LyricsFontColorType LyricsBgFontColorType
|
||||
{
|
||||
get => (LyricsFontColorType)GetValue<int>(LyricsBgFontColorTypeKey);
|
||||
set => SetValue(LyricsBgFontColorTypeKey, (int)value);
|
||||
}
|
||||
|
||||
public LyricsFontColorType LyricsFgFontColorType
|
||||
{
|
||||
get => (LyricsFontColorType)GetValue<int>(LyricsFgFontColorTypeKey);
|
||||
set => SetValue(LyricsFgFontColorTypeKey, (int)value);
|
||||
}
|
||||
|
||||
public LyricsFontColorType LyricsStrokeFontColorType
|
||||
{
|
||||
get => (LyricsFontColorType)GetValue<int>(LyricsStrokeFontColorTypeKey);
|
||||
set => SetValue(LyricsStrokeFontColorTypeKey, (int)value);
|
||||
}
|
||||
|
||||
public int LyricsFontStrokeWidth
|
||||
{
|
||||
get => GetValue<int>(LyricsFontStrokeWidthKey);
|
||||
set => SetValue(LyricsFontStrokeWidthKey, value);
|
||||
}
|
||||
|
||||
public int LyricsFontSize
|
||||
@@ -275,12 +403,53 @@ namespace BetterLyrics.WinUI3.Services
|
||||
);
|
||||
}
|
||||
|
||||
public List<MediaSourceProviderInfo> MediaSourceProvidersInfo
|
||||
{
|
||||
get =>
|
||||
System.Text.Json.JsonSerializer.Deserialize(
|
||||
GetValue<string>(MediaSourceProvidersInfoKey) ?? "[]",
|
||||
SourceGenerationContext.Default.ListMediaSourceProviderInfo
|
||||
)!;
|
||||
set =>
|
||||
SetValue(
|
||||
MediaSourceProvidersInfoKey,
|
||||
System.Text.Json.JsonSerializer.Serialize(
|
||||
value,
|
||||
SourceGenerationContext.Default.ListMediaSourceProviderInfo
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public int LyricsVerticalEdgeOpacity
|
||||
{
|
||||
get => GetValue<int>(LyricsVerticalEdgeOpacityKey);
|
||||
set => SetValue(LyricsVerticalEdgeOpacityKey, value);
|
||||
}
|
||||
|
||||
public string LibreTranslateServer
|
||||
{
|
||||
get => GetValue<string>(LibreTranslateServerKey)!;
|
||||
set => SetValue(LibreTranslateServerKey, value);
|
||||
}
|
||||
|
||||
public bool IsTranslationEnabled
|
||||
{
|
||||
get => GetValue<bool>(IsTranslationEnabledKey);
|
||||
set => SetValue(IsTranslationEnabledKey, value);
|
||||
}
|
||||
|
||||
public int SelectedTargetLanguageIndex
|
||||
{
|
||||
get => GetValue<int>(SelectedTargetLanguageIndexKey);
|
||||
set => SetValue(SelectedTargetLanguageIndexKey, value);
|
||||
}
|
||||
|
||||
public bool IgnoreFullscreenWindow
|
||||
{
|
||||
get => GetValue<bool>(IgnoreFullscreenWindowKey);
|
||||
set => SetValue(IgnoreFullscreenWindowKey, value);
|
||||
}
|
||||
|
||||
private T? GetValue<T>(string key)
|
||||
{
|
||||
if (_localSettings.Values.TryGetValue(key, out object? value))
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
<value>Add a folder</value>
|
||||
</data>
|
||||
<data name="SettingsPageTheme.Header" xml:space="preserve">
|
||||
<value>Lyrics window theme</value>
|
||||
<value>Lyrics background theme</value>
|
||||
</data>
|
||||
<data name="SettingsPageLanguage.Header" xml:space="preserve">
|
||||
<value>Language</value>
|
||||
@@ -147,6 +147,9 @@
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>Adaptive to lyrics background (Colored)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>Adaptive to lyrics background (Colored)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>Light</value>
|
||||
</data>
|
||||
@@ -301,7 +304,7 @@
|
||||
<value>Welcome to BetterLyrics</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>Let's setup lyrics database now</value>
|
||||
<value>Hover the mouse over the top or bottom area of the app to display more function options</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>No music playing now</value>
|
||||
@@ -321,14 +324,20 @@
|
||||
<data name="SettingsPageCache.Description" xml:space="preserve">
|
||||
<value>Including log files, network lyrics cache</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColor.Header" xml:space="preserve">
|
||||
<value>Font color</value>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>Font color (Non-current playback area)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>Font color (Current playback area)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>Adaptive to lyrics background (Grayed)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>Adaptive to lyrics background (Grayed)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>Album art style</value>
|
||||
<value>Album art area style</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>Corner radius</value>
|
||||
@@ -531,6 +540,9 @@
|
||||
<data name="SettingsPageLyricsFontColorCustom.Content" xml:space="preserve">
|
||||
<value>Custom</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorCustom.Content" xml:space="preserve">
|
||||
<value>Custom</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyrics.Content" xml:space="preserve">
|
||||
<value>Lyrics style and effect</value>
|
||||
</data>
|
||||
@@ -540,4 +552,106 @@
|
||||
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
|
||||
<value>Auto-lock when activating desktop mode</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>Alignment</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>Center</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>Left</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>Right</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>Album art</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
|
||||
<value>Song title & artist</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
|
||||
<value>Easing animation type</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>Media sources</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>Media source</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>Enable or disable lyrics display for a specified media source</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>Log record</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslation.Content" xml:space="preserve">
|
||||
<value>Lyrics translation</value>
|
||||
</data>
|
||||
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
|
||||
<value>Lyrics timeline offset (ms)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>Configure translation services</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>Server address</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>Test server</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
|
||||
<value>Target language</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfo.Header" xml:space="preserve">
|
||||
<value>Translation service powered by LibreTranslate</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>Visit https://github.com/LibreTranslate/LibreTranslate for installation instructions and more information (this software is not affiliated with this translation service in any way)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<value>Server test successful</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<value>Server test failed</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
<value>Lyrics stroke width (Desktop mode only)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFollowSystem.Content" xml:space="preserve">
|
||||
<value>Follow system</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
|
||||
<value>Automatic startup</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsStrokeFontColor.Header" xml:space="preserve">
|
||||
<value>Lyrics stroke color (Desktop mode only)</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Header" xml:space="preserve">
|
||||
<value>Always stay on top of fullscreen applications</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
|
||||
<value>Force this app to appear on top of full-screen apps when docked or desktop mode is enabled</value>
|
||||
</data>
|
||||
<data name="HostWindowMoreButtonToolTip.Content" xml:space="preserve">
|
||||
<value>More</value>
|
||||
</data>
|
||||
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Lyrics timeline offset</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Translate</value>
|
||||
</data>
|
||||
<data name="LyricsPageDisplayTypeButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Display type</value>
|
||||
</data>
|
||||
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>Translate server is not set, please configure it in settings first</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>Will automatically reset to 0 when switching songs</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -139,7 +139,7 @@
|
||||
<value>フォルダーを追加します</value>
|
||||
</data>
|
||||
<data name="SettingsPageTheme.Header" xml:space="preserve">
|
||||
<value>歌詞ウィンドウのテーマ</value>
|
||||
<value>歌詞の背景テーマ</value>
|
||||
</data>
|
||||
<data name="SettingsPageLanguage.Header" xml:space="preserve">
|
||||
<value>言語</value>
|
||||
@@ -147,6 +147,9 @@
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>歌詞の背景に適応する(色付き)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>歌詞の背景に適応する(色付き)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>ライト</value>
|
||||
</data>
|
||||
@@ -301,7 +304,7 @@
|
||||
<value>BetterLyrics へようこそ</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>今すぐ歌詞データベースをセットアップしましょう</value>
|
||||
<value>マウスをアプリの上または下部の領域にホバリングして、より多くの機能オプションを表示します</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>今は音楽が再生されていません</value>
|
||||
@@ -321,14 +324,20 @@
|
||||
<data name="SettingsPageCache.Description" xml:space="preserve">
|
||||
<value>ログファイル、ネットワーク歌詞キャッシュを含む</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColor.Header" xml:space="preserve">
|
||||
<value>フォントカラー</value>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>フォントカラー(非電流再生エリア)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>フォントカラー(現在の再生エリア)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>歌詞の背景に適応する(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>歌詞の背景に適応する(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>アルバムアートスタイル</value>
|
||||
<value>アルバムエリアスタイル</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>コーナー半径</value>
|
||||
@@ -531,6 +540,9 @@
|
||||
<data name="SettingsPageLyricsFontColorCustom.Content" xml:space="preserve">
|
||||
<value>カスタマイズ</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorCustom.Content" xml:space="preserve">
|
||||
<value>カスタマイズ</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyrics.Content" xml:space="preserve">
|
||||
<value>歌詞のスタイルと効果</value>
|
||||
</data>
|
||||
@@ -540,4 +552,106 @@
|
||||
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
|
||||
<value>デスクトップモードをアクティブにするときの自動ロック</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>アライメント</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>中心</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>左</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>右</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>アルバムアート</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
|
||||
<value>曲のタイトル&アーティスト</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
|
||||
<value>アニメーションタイプを緩和します</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>メディアソース</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>メディアソース</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>指定されたメディアソースの歌詞ディスプレイを有効または無効にする</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>ログレコード</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslation.Content" xml:space="preserve">
|
||||
<value>歌詞翻訳</value>
|
||||
</data>
|
||||
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
|
||||
<value>歌詞タイムラインオフセット(ミリ秒)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>翻訳サービスを構成します</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>サーバーアドレス</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>テストサーバー</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
|
||||
<value>ターゲット言語</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfo.Header" xml:space="preserve">
|
||||
<value>LibreTranslate を搭載した翻訳サービス</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>https://github.com/LibreTranslate/LibreTranslate にアクセスしてください。</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<value>サーバーテストが成功しました</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<value>サーバーテストに失敗しました</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
<value>歌詞ストローク幅(デスクトップモードのみ)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFollowSystem.Content" xml:space="preserve">
|
||||
<value>システムをフォローします</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
|
||||
<value>自動起動</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsStrokeFontColor.Header" xml:space="preserve">
|
||||
<value>歌詞ストロークカラー(デスクトップモードのみ)</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Header" xml:space="preserve">
|
||||
<value>常にフルスクリーンアプリケーションを常に把握してください</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
|
||||
<value>このアプリは、ドッキングまたはデスクトップモードが有効になっているときにフルスクリーンアプリの上に表示されます</value>
|
||||
</data>
|
||||
<data name="HostWindowMoreButtonToolTip.Content" xml:space="preserve">
|
||||
<value>もっと</value>
|
||||
</data>
|
||||
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
|
||||
<value>歌詞タイムラインオフセット</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationButtonToolTip.Content" xml:space="preserve">
|
||||
<value>翻訳する</value>
|
||||
</data>
|
||||
<data name="LyricsPageDisplayTypeButtonToolTip.Content" xml:space="preserve">
|
||||
<value>表示タイプ</value>
|
||||
</data>
|
||||
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>翻訳サーバーは設定されていません。最初に設定で構成してください</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>曲を切り替えると、0 に自動的にリセットされます</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -139,7 +139,7 @@
|
||||
<value>폴더를 추가하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageTheme.Header" xml:space="preserve">
|
||||
<value>가사 창 테마</value>
|
||||
<value>가사 배경 테마</value>
|
||||
</data>
|
||||
<data name="SettingsPageLanguage.Header" xml:space="preserve">
|
||||
<value>언어</value>
|
||||
@@ -147,6 +147,9 @@
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>가사 배경 (색상)에 적응</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>가사 배경 (색상)에 적응</value>
|
||||
</data>
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>빛</value>
|
||||
</data>
|
||||
@@ -301,7 +304,7 @@
|
||||
<value>Betterlyrics에 오신 것을 환영합니다</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>지금 가사 데이터베이스를 설정합시다</value>
|
||||
<value>더 많은 기능 옵션을 표시하려면 앱의 상단 또는 하단 영역 위로 마우스를 가져옵니다.</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>지금 음악이 재생되지 않습니다</value>
|
||||
@@ -321,14 +324,20 @@
|
||||
<data name="SettingsPageCache.Description" xml:space="preserve">
|
||||
<value>로그 파일, 네트워크 가사 캐시 포함</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColor.Header" xml:space="preserve">
|
||||
<value>글꼴 색상</value>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>글꼴 색상 (비 전류 재생 영역)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>글꼴 색상 (현재 재생 영역)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>가사 배경 (회색)에 적응</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>가사 배경 (회색)에 적응</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>앨범 아트 스타일</value>
|
||||
<value>앨범 영역 스타일</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>코너 반경</value>
|
||||
@@ -531,6 +540,9 @@
|
||||
<data name="SettingsPageLyricsFontColorCustom.Content" xml:space="preserve">
|
||||
<value>사용자 정의하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorCustom.Content" xml:space="preserve">
|
||||
<value>사용자 정의하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyrics.Content" xml:space="preserve">
|
||||
<value>가사 스타일과 효과</value>
|
||||
</data>
|
||||
@@ -540,4 +552,106 @@
|
||||
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
|
||||
<value>데스크탑 모드를 활성화 할 때 자동 잠금</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>조정</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>센터</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>왼쪽</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>오른쪽</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>앨범 아트</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
|
||||
<value>노래 제목 및 아티스트</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
|
||||
<value>애니메이션 유형 완화</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>미디어 소스</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>미디어 소스</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>지정된 미디어 소스의 가사 디스플레이 활성화 또는 비활성화</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>로그 레코드</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslation.Content" xml:space="preserve">
|
||||
<value>가사 번역</value>
|
||||
</data>
|
||||
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
|
||||
<value>가사 타임 라인 오프셋 (밀리초)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>번역 서비스를 구성하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>서버 주소</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>테스트 서버</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
|
||||
<value>대상 언어</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfo.Header" xml:space="preserve">
|
||||
<value>LibreTranslate 가 구동하는 번역 서비스</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>설치 지침 및 자세한 정보는 https://github.com/LibreTranslate/LibreTranslate 를 방문하십시오 (이 소프트웨어는이 번역 서비스와 제휴하지 않습니다).</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<value>서버 테스트 성공</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<value>서버 테스트가 실패했습니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
<value>가사 뇌졸중 너비 (데스크탑 모드 만)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFollowSystem.Content" xml:space="preserve">
|
||||
<value>시스템을 따르십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
|
||||
<value>자동 시작</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsStrokeFontColor.Header" xml:space="preserve">
|
||||
<value>가사 스트로크 컬러 (데스크탑 모드 만)</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Header" xml:space="preserve">
|
||||
<value>항상 전체 화면 응용 프로그램 위에 머물러 있습니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
|
||||
<value>도킹 또는 데스크탑 모드가 활성화 될 때이 앱이 전체 화면 앱 위에 나타나도록 강요</value>
|
||||
</data>
|
||||
<data name="HostWindowMoreButtonToolTip.Content" xml:space="preserve">
|
||||
<value>더</value>
|
||||
</data>
|
||||
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
|
||||
<value>가사 타임 라인 오프셋</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationButtonToolTip.Content" xml:space="preserve">
|
||||
<value>번역하다</value>
|
||||
</data>
|
||||
<data name="LyricsPageDisplayTypeButtonToolTip.Content" xml:space="preserve">
|
||||
<value>디스플레이 유형</value>
|
||||
</data>
|
||||
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>설정</value>
|
||||
</data>
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>번역 서버가 설정되지 않았습니다. 먼저 설정으로 구성하십시오.</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>노래를 전환 할 때 자동으로 0 으로 재설정됩니다</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -139,7 +139,7 @@
|
||||
<value>添加文件夹</value>
|
||||
</data>
|
||||
<data name="SettingsPageTheme.Header" xml:space="preserve">
|
||||
<value>歌词窗口主题</value>
|
||||
<value>歌词背景主题</value>
|
||||
</data>
|
||||
<data name="SettingsPageLanguage.Header" xml:space="preserve">
|
||||
<value>语言</value>
|
||||
@@ -147,6 +147,9 @@
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>适应歌词背景(彩色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>适应歌词背景(彩色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>浅色</value>
|
||||
</data>
|
||||
@@ -301,7 +304,7 @@
|
||||
<value>欢迎使用 BetterLyrics</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>现在就来初始化歌词数据库吧</value>
|
||||
<value>将鼠标悬停在应用程序的顶部或底部区域以显示更多功能选项</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>当前没有正在播放的音乐</value>
|
||||
@@ -321,14 +324,20 @@
|
||||
<data name="SettingsPageCache.Description" xml:space="preserve">
|
||||
<value>包括日志文件,网络歌词缓存</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColor.Header" xml:space="preserve">
|
||||
<value>字体颜色</value>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>字体颜色(非当前播放区域)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>字体颜色(当前播放区域)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>适应歌词背景(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>适应歌词背景(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>专辑封面样式</value>
|
||||
<value>专辑区域样式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>圆角半径</value>
|
||||
@@ -531,6 +540,9 @@
|
||||
<data name="SettingsPageLyricsFontColorCustom.Content" xml:space="preserve">
|
||||
<value>自定义</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorCustom.Content" xml:space="preserve">
|
||||
<value>自定义</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyrics.Content" xml:space="preserve">
|
||||
<value>歌词样式与动效</value>
|
||||
</data>
|
||||
@@ -540,4 +552,106 @@
|
||||
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
|
||||
<value>启动桌面模式时随即锁定窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>对齐方式</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>居中</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>靠左</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>专辑</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
|
||||
<value>歌曲标题和艺术家</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
|
||||
<value>缓动动画类型</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>媒体来源</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>媒体来源</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>为指定媒体源启用或禁用歌词显示</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>日志记录</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslation.Content" xml:space="preserve">
|
||||
<value>歌词翻译</value>
|
||||
</data>
|
||||
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
|
||||
<value>歌词时间轴偏移(毫秒)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>配置翻译服务</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>服务器地址</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>测试服务器</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
|
||||
<value>目标语言</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfo.Header" xml:space="preserve">
|
||||
<value>翻译服务由 LibreTranslate 驱动</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>访问 https://github.com/LibreTranslate/LibreTranslate 获取安装教程及更多信息(本软件与该翻译服务无任何联系)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<value>服务器测试成功</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<value>服务器测试失败</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
<value>歌词描边宽度(仅桌面模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFollowSystem.Content" xml:space="preserve">
|
||||
<value>跟随系统</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
|
||||
<value>自动启动</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsStrokeFontColor.Header" xml:space="preserve">
|
||||
<value>歌词描边颜色(仅桌面模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Header" xml:space="preserve">
|
||||
<value>始终显示在全屏应用上方</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
|
||||
<value>当启用停靠模式或桌面模式时强制将本应用显示在全屏应用的上方</value>
|
||||
</data>
|
||||
<data name="HostWindowMoreButtonToolTip.Content" xml:space="preserve">
|
||||
<value>更多</value>
|
||||
</data>
|
||||
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
|
||||
<value>歌词时间偏移</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationButtonToolTip.Content" xml:space="preserve">
|
||||
<value>翻译</value>
|
||||
</data>
|
||||
<data name="LyricsPageDisplayTypeButtonToolTip.Content" xml:space="preserve">
|
||||
<value>显示类型</value>
|
||||
</data>
|
||||
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
</data>
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>未设置翻译服务器,请先在设置中进行配置</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>切换歌曲时将自动重置为 0</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -139,7 +139,7 @@
|
||||
<value>新增資料夾</value>
|
||||
</data>
|
||||
<data name="SettingsPageTheme.Header" xml:space="preserve">
|
||||
<value>歌詞窗口主題</value>
|
||||
<value>歌詞背景主題</value>
|
||||
</data>
|
||||
<data name="SettingsPageLanguage.Header" xml:space="preserve">
|
||||
<value>語言</value>
|
||||
@@ -147,6 +147,9 @@
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>適應歌詞背景(彩色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>適應歌詞背景(彩色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLight.Content" xml:space="preserve">
|
||||
<value>淺色</value>
|
||||
</data>
|
||||
@@ -301,7 +304,7 @@
|
||||
<value>歡迎使用 BetterLyrics</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>現在就來初始化歌詞資料庫吧</value>
|
||||
<value>將鼠標懸停在應用程序的頂部或底部區域以顯示更多功能選項</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>目前沒有正在播放的音樂</value>
|
||||
@@ -321,14 +324,20 @@
|
||||
<data name="SettingsPageCache.Description" xml:space="preserve">
|
||||
<value>包括日誌文件,網絡歌詞緩存</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColor.Header" xml:space="preserve">
|
||||
<value>字體顏色</value>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>字體顏色(非當前播放區域)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>字體顏色(當前播放區域)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>適應歌詞背景(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveGrayed.Content" xml:space="preserve">
|
||||
<value>適應歌詞背景(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>專輯封面樣式</value>
|
||||
<value>专辑区域样式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>圓角半徑</value>
|
||||
@@ -531,6 +540,9 @@
|
||||
<data name="SettingsPageLyricsFontColorCustom.Content" xml:space="preserve">
|
||||
<value>自定義</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorCustom.Content" xml:space="preserve">
|
||||
<value>自定義</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyrics.Content" xml:space="preserve">
|
||||
<value>歌詞樣式與動效</value>
|
||||
</data>
|
||||
@@ -540,4 +552,106 @@
|
||||
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
|
||||
<value>啟動桌面模式時隨即鎖定窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>對齊方式</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>居中</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>靠左</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>專輯</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
|
||||
<value>歌曲標題和藝術家</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
|
||||
<value>缓动动画类型</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>媒體來源</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
|
||||
<value>媒體來源</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
|
||||
<value>為指定媒體源啟用或禁用歌詞顯示</value>
|
||||
</data>
|
||||
<data name="SettingsPageLog.Header" xml:space="preserve">
|
||||
<value>日誌記錄</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslation.Content" xml:space="preserve">
|
||||
<value>歌詞翻譯</value>
|
||||
</data>
|
||||
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
|
||||
<value>歌詞時間軸偏移(毫秒)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>配置翻譯服務</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>服務器地址</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>測試服務器</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
|
||||
<value>目標語言</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfo.Header" xml:space="preserve">
|
||||
<value>翻译服务由 LibreTranslate 驱动</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>造訪 https://github.com/LibreTranslate/LibreTranslate 以取得安裝教學及更多資訊(本軟體與此翻譯服務無任何關聯)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<value>服務器測試成功</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<value>服務器測試失敗</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
<value>歌詞描邊寬度(僅桌面模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFollowSystem.Content" xml:space="preserve">
|
||||
<value>跟随系统</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
|
||||
<value>自動啟動</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsStrokeFontColor.Header" xml:space="preserve">
|
||||
<value>歌词描边颜色(仅桌面模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Header" xml:space="preserve">
|
||||
<value>始終顯示在全螢幕應用程式上方</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
|
||||
<value>啟用停靠模式或桌面模式時強制將本應用程式顯示在全螢幕應用程式的上方</value>
|
||||
</data>
|
||||
<data name="HostWindowMoreButtonToolTip.Content" xml:space="preserve">
|
||||
<value>更多</value>
|
||||
</data>
|
||||
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
|
||||
<value>歌詞時間偏移</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationButtonToolTip.Content" xml:space="preserve">
|
||||
<value>翻譯</value>
|
||||
</data>
|
||||
<data name="LyricsPageDisplayTypeButtonToolTip.Content" xml:space="preserve">
|
||||
<value>顯示類型</value>
|
||||
</data>
|
||||
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>未設定翻譯伺服器,請先在設定中進行配置</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>將在切換歌曲時自動重設為 0</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,9 +1,5 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using BetterInAppLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
@@ -14,8 +10,8 @@ using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using WinUIEx.Messaging;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
@@ -28,68 +24,61 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
public LyricsPageViewModel(ISettingsService settingsService, IPlaybackService playbackService) : base(settingsService)
|
||||
{
|
||||
LyricsFontSize = _settingsService.LyricsFontSize;
|
||||
CoverImageRadius = _settingsService.CoverImageRadius;
|
||||
IsTranslationEnabled = _settingsService.IsTranslationEnabled;
|
||||
PreferredDisplayType = _settingsService.PreferredDisplayType;
|
||||
|
||||
_playbackService = playbackService;
|
||||
_playbackService.SongInfoChanged += async (_, args) =>
|
||||
await UpdateSongInfoUI(args.SongInfo).ConfigureAwait(true);
|
||||
_playbackService.SongInfoChanged += PlaybackService_SongInfoChanged;
|
||||
|
||||
|
||||
IsFirstRun = _settingsService.IsFirstRun;
|
||||
}
|
||||
|
||||
UpdateSongInfoUI(_playbackService.SongInfo).ConfigureAwait(true);
|
||||
private void PlaybackService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
|
||||
{
|
||||
SongInfo = e.SongInfo;
|
||||
PositionOffset = 0; // Reset position offset when song changes
|
||||
TrySwitchToPreferredDisplayType(e.SongInfo);
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool AboutToUpdateUI { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial BitmapImage? CoverImage { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial double CoverImageGridActualHeight { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial CornerRadius CoverImageGridCornerRadius { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int CoverImageRadius { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsDisplayType DisplayType { get; set; }
|
||||
public partial LyricsDisplayType DisplayType { get; set; } = LyricsDisplayType.PlaceholderOnly;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsFirstRun { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsNotDockMode { get; set; } = true;
|
||||
public partial bool IsWelcomeTeachingTipOpen { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsWelcomeTeachingTipOpen { get; set; }
|
||||
public partial Visibility BottomCommandGridVisibility { get; set; } = Visibility.Visible;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int LyricsFontSize { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial double MaxLyricsWidth { get; set; } = 0.0;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LyricsDisplayType? PreferredDisplayType { get; set; } = LyricsDisplayType.SplitView;
|
||||
public partial LyricsDisplayType PreferredDisplayType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial SongInfo? SongInfo { get; set; } = null;
|
||||
|
||||
public void OpenMatchedFileFolderInFileExplorer(string path)
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int PositionOffset { get; set; } = 0;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsTranslationEnabled { get; set; } = false;
|
||||
|
||||
partial void OnIsTranslationEnabledChanged(bool value)
|
||||
{
|
||||
Process.Start(
|
||||
new ProcessStartInfo
|
||||
{
|
||||
FileName = "explorer.exe",
|
||||
Arguments = $"/select,\"{path}\"",
|
||||
UseShellExecute = true,
|
||||
}
|
||||
);
|
||||
_settingsService.IsTranslationEnabled = value;
|
||||
}
|
||||
|
||||
partial void OnPreferredDisplayTypeChanged(LyricsDisplayType value)
|
||||
{
|
||||
_settingsService.PreferredDisplayType = value;
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
@@ -98,7 +87,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsWindowViewModel.IsDockMode))
|
||||
{
|
||||
IsNotDockMode = !message.NewValue;
|
||||
SetNonStandardModePreferredDisplayType(message.NewValue);
|
||||
TrySwitchToPreferredDisplayType(SongInfo);
|
||||
}
|
||||
@@ -114,37 +102,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.CoverImageRadius))
|
||||
{
|
||||
CoverImageRadius = message.NewValue;
|
||||
}
|
||||
}
|
||||
if (message.Sender is LyricsSettingsControlViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontSize))
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize))
|
||||
{
|
||||
LyricsFontSize = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateSongInfoUI(SongInfo? songInfo)
|
||||
{
|
||||
AboutToUpdateUI = true;
|
||||
await Task.Delay(AnimationHelper.StoryboardDefaultDuration);
|
||||
|
||||
SongInfo = songInfo;
|
||||
|
||||
CoverImage =
|
||||
(songInfo?.AlbumArt == null)
|
||||
? null
|
||||
: await ImageHelper.GetBitmapImageFromBytesAsync(songInfo.AlbumArt);
|
||||
|
||||
TrySwitchToPreferredDisplayType(songInfo);
|
||||
|
||||
AboutToUpdateUI = false;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenSettingsWindow()
|
||||
{
|
||||
@@ -160,7 +124,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
PreferredDisplayType = _preferredDisplayTypeBeforeSwitchToNonStandardMode;
|
||||
PreferredDisplayType = _preferredDisplayTypeBeforeSwitchToNonStandardMode ?? LyricsDisplayType.SplitView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,24 +149,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
}
|
||||
|
||||
partial void OnCoverImageGridActualHeightChanged(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
return;
|
||||
|
||||
CoverImageGridCornerRadius = new CornerRadius(CoverImageRadius / 100f * value / 2);
|
||||
}
|
||||
|
||||
partial void OnCoverImageRadiusChanged(int value)
|
||||
{
|
||||
if (double.IsNaN(CoverImageGridActualHeight))
|
||||
return;
|
||||
|
||||
CoverImageGridCornerRadius = new CornerRadius(
|
||||
value / 100f * CoverImageGridActualHeight / 2
|
||||
);
|
||||
}
|
||||
|
||||
partial void OnIsFirstRunChanged(bool value)
|
||||
{
|
||||
IsWelcomeTeachingTipOpen = value;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Graphics.Canvas.Effects;
|
||||
@@ -11,9 +8,17 @@ using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
@@ -25,30 +30,25 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
using var blurredLyrics = new CanvasCommandList(control);
|
||||
using (var blurredLyricsDs = blurredLyrics.CreateDrawingSession())
|
||||
{
|
||||
switch (DisplayType)
|
||||
{
|
||||
case LyricsDisplayType.AlbumArtOnly:
|
||||
case LyricsDisplayType.PlaceholderOnly:
|
||||
break;
|
||||
case LyricsDisplayType.LyricsOnly:
|
||||
case LyricsDisplayType.SplitView:
|
||||
DrawBlurredLyrics(control, blurredLyricsDs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
DrawBlurredLyrics(control, blurredLyricsDs);
|
||||
}
|
||||
|
||||
if (_lastAlbumArtSwBitmap != null && _lastAlbumArtCanvasBitmap == null)
|
||||
{
|
||||
_lastAlbumArtCanvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, _lastAlbumArtSwBitmap);
|
||||
}
|
||||
if (_albumArtSwBitmap != null && _albumArtCanvasBitmap == null)
|
||||
{
|
||||
_albumArtCanvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, _albumArtSwBitmap);
|
||||
}
|
||||
|
||||
using var combined = new CanvasCommandList(control);
|
||||
using var combinedDs = combined.CreateDrawingSession();
|
||||
|
||||
DrawAlbumArtBackground(control, combinedDs);
|
||||
|
||||
if (_isDockMode)
|
||||
{
|
||||
DrawImmersiveBackground(control, combinedDs);
|
||||
}
|
||||
|
||||
combinedDs.DrawImage(blurredLyrics);
|
||||
|
||||
if (_isDesktopMode)
|
||||
@@ -60,12 +60,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
ds.DrawImage(combined);
|
||||
}
|
||||
|
||||
DrawAlbumArt(control, ds);
|
||||
DrawTitleAndArtist(control, ds);
|
||||
|
||||
if (_isDebugOverlayEnabled)
|
||||
{
|
||||
var currentPlayingLineIndex = GetCurrentPlayingLineIndex();
|
||||
var currentPlayingLine = _multiLangLyrics
|
||||
.SafeGet(_langIndex)
|
||||
?.SafeGet(currentPlayingLineIndex);
|
||||
?.SafeGet(_playingLineIndex);
|
||||
|
||||
if (currentPlayingLine != null)
|
||||
{
|
||||
@@ -76,49 +78,100 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
out float charProgress
|
||||
);
|
||||
|
||||
ds.DrawText(
|
||||
$"DEBUG: "
|
||||
+ $"Cur playing {currentPlayingLineIndex}, char start idx {charStartIndex}, length {charLength}, prog {charProgress}\n"
|
||||
+ $"Visible lines [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n"
|
||||
+ $"Cur time {TotalTime}\n" +
|
||||
$"Lang size: {_multiLangLyrics.Count}\n" +
|
||||
$"{_lyricsOpacityTransition.Value}",
|
||||
new Vector2(10, 10),
|
||||
ThemeTypeSent == Microsoft.UI.Xaml.ElementTheme.Light ? Colors.Black : Colors.White
|
||||
);
|
||||
//ds.DrawText(
|
||||
// $"[DEBUG]\n" +
|
||||
// $"Cur playing {_playingLineIndex}, char start idx {charStartIndex}, length {charLength}, prog {charProgress}\n" +
|
||||
// $"Visible lines [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" +
|
||||
// $"Cur time {_totalTime + _positionOffset}\n" +
|
||||
// $"Lang size {_multiLangLyrics.Count}\n" +
|
||||
// $"Song duration {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}",
|
||||
// new Vector2(10, 10),
|
||||
// ThemeTypeSent == Microsoft.UI.Xaml.ElementTheme.Light ? Colors.Black : Colors.White
|
||||
//);
|
||||
|
||||
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++)
|
||||
{
|
||||
LyricsLine? line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i);
|
||||
if (line != null)
|
||||
{
|
||||
ds.DrawText(
|
||||
$"[{i}] {line.OriginalText} {line.HighlightOpacityTransition.Value}",
|
||||
new Vector2(10, 30 + (i - _startVisibleLineIndex) * 20),
|
||||
ThemeTypeSent == ElementTheme.Light ? Colors.Black : Colors.White
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawImgae(
|
||||
ICanvasAnimatedControl control,
|
||||
CanvasDrawingSession ds,
|
||||
SoftwareBitmap softwareBitmap,
|
||||
float opacity
|
||||
)
|
||||
private void DrawBackgroundImgae(ICanvasAnimatedControl control, CanvasDrawingSession ds, CanvasBitmap canvasBitmap, float opacity)
|
||||
{
|
||||
using var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, softwareBitmap);
|
||||
float imageWidth = (float)canvasBitmap.Size.Width;
|
||||
float imageHeight = (float)canvasBitmap.Size.Height;
|
||||
|
||||
var scaleFactor =
|
||||
(float)Math.Sqrt(Math.Pow(control.Size.Width, 2) + Math.Pow(control.Size.Height, 2))
|
||||
/ Math.Min(imageWidth, imageHeight);
|
||||
float scaleFactor = MathF.Sqrt(MathF.Pow(_canvasWidth, 2) + MathF.Pow(_canvasHeight, 2)) / MathF.Min(imageWidth, imageHeight);
|
||||
|
||||
ds.DrawImage(
|
||||
new OpacityEffect
|
||||
float x = _canvasWidth / 2 - imageWidth * scaleFactor / 2;
|
||||
float y = _canvasHeight / 2 - imageHeight * scaleFactor / 2;
|
||||
|
||||
// Source: https://zhuanlan.zhihu.com/p/37178216
|
||||
float bright = _lyricsBgBrightnessTransition.Value / 1f * 2f; // 明度参数,范围在0.0f到2.0f之间
|
||||
|
||||
float whiteX = Math.Min(2 - bright, 1);
|
||||
float whiteY = 1f;
|
||||
float blackX = Math.Max(1 - bright, 0);
|
||||
float blackY = 0f;
|
||||
|
||||
ds.DrawImage(new OpacityEffect
|
||||
{
|
||||
Source = new BrightnessEffect
|
||||
{
|
||||
Source = new ScaleEffect
|
||||
{
|
||||
InterpolationMode = CanvasImageInterpolation.HighQualityCubic,
|
||||
BorderMode = EffectBorderMode.Hard,
|
||||
Scale = new Vector2(scaleFactor),
|
||||
Source = canvasBitmap,
|
||||
},
|
||||
Opacity = opacity,
|
||||
WhitePoint = new Vector2(whiteX, whiteY),
|
||||
BlackPoint = new Vector2(blackX, blackY),
|
||||
},
|
||||
(float)control.Size.Width / 2 - imageWidth * scaleFactor / 2,
|
||||
(float)control.Size.Height / 2 - imageHeight * scaleFactor / 2
|
||||
Opacity = opacity,
|
||||
}, new Vector2(x, y)
|
||||
);
|
||||
}
|
||||
|
||||
private void DrawForegroundImgae(ICanvasAnimatedControl control, CanvasDrawingSession ds, CanvasBitmap canvasBitmap, float opacity)
|
||||
{
|
||||
if (opacity == 0) return;
|
||||
|
||||
float imageWidth = (float)canvasBitmap.Size.Width;
|
||||
float imageHeight = (float)canvasBitmap.Size.Height;
|
||||
|
||||
float scaleFactor = _albumArtSize / Math.Min(imageWidth, imageHeight);
|
||||
if (scaleFactor < 0.01f) return;
|
||||
|
||||
float cornerRadius = _albumArtCornerRadius / 100f * _albumArtSize / 2;
|
||||
|
||||
using var cornerRadiusMask = new CanvasCommandList(control.Device);
|
||||
using var cornerRadiusMaskDs = cornerRadiusMask.CreateDrawingSession();
|
||||
cornerRadiusMaskDs.FillRoundedRectangle(
|
||||
new Rect(0, 0, imageWidth * scaleFactor, imageHeight * scaleFactor),
|
||||
cornerRadius, cornerRadius, Colors.White
|
||||
);
|
||||
|
||||
ds.DrawImage(new OpacityEffect
|
||||
{
|
||||
Source = new AlphaMaskEffect
|
||||
{
|
||||
Source = new ScaleEffect
|
||||
{
|
||||
Scale = new Vector2(scaleFactor),
|
||||
Source = canvasBitmap,
|
||||
},
|
||||
AlphaMask = cornerRadiusMask,
|
||||
},
|
||||
Opacity = opacity,
|
||||
}, new Vector2(_albumArtXTransition.Value, _albumArtY)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,26 +179,16 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
ds.Transform = Matrix3x2.CreateRotation(_rotateAngle, control.Size.ToVector2() * 0.5f);
|
||||
|
||||
var overlappedCovers = new CanvasCommandList(control.Device);
|
||||
using var overlappedCovers = new CanvasCommandList(control.Device);
|
||||
using var overlappedCoversDs = overlappedCovers.CreateDrawingSession();
|
||||
|
||||
if (_lastAlbumArtBitmap != null)
|
||||
if (_lastAlbumArtCanvasBitmap != null)
|
||||
{
|
||||
DrawImgae(
|
||||
control,
|
||||
overlappedCoversDs,
|
||||
_lastAlbumArtBitmap,
|
||||
1 - _albumArtBgTransition.Value
|
||||
);
|
||||
DrawBackgroundImgae(control, overlappedCoversDs, _lastAlbumArtCanvasBitmap, 1 - _albumArtBgTransition.Value);
|
||||
}
|
||||
if (_albumArtBitmap != null)
|
||||
if (_albumArtCanvasBitmap != null)
|
||||
{
|
||||
DrawImgae(
|
||||
control,
|
||||
overlappedCoversDs,
|
||||
_albumArtBitmap,
|
||||
_albumArtBgTransition.Value
|
||||
);
|
||||
DrawBackgroundImgae(control, overlappedCoversDs, _albumArtCanvasBitmap, _albumArtBgTransition.Value);
|
||||
}
|
||||
|
||||
using var coverOverlayEffect = new OpacityEffect
|
||||
@@ -162,13 +205,73 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
ds.Transform = Matrix3x2.Identity;
|
||||
}
|
||||
|
||||
private void DrawAlbumArt(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
using var albumArt = new CanvasCommandList(control.Device);
|
||||
using var albumArtDs = albumArt.CreateDrawingSession();
|
||||
if (_albumArtCanvasBitmap != null)
|
||||
{
|
||||
DrawForegroundImgae(control, albumArtDs, _albumArtCanvasBitmap, _albumArtBgTransition.Value);
|
||||
}
|
||||
if (_lastAlbumArtCanvasBitmap != null)
|
||||
{
|
||||
DrawForegroundImgae(control, albumArtDs, _lastAlbumArtCanvasBitmap, 1 - _albumArtBgTransition.Value);
|
||||
}
|
||||
|
||||
using var opacity = new CanvasCommandList(control.Device);
|
||||
using var opacityDs = opacity.CreateDrawingSession();
|
||||
opacityDs.DrawImage(new GaussianBlurEffect
|
||||
{
|
||||
Source = albumArt,
|
||||
BlurAmount = 12f,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
});
|
||||
opacityDs.DrawImage(albumArt);
|
||||
|
||||
ds.DrawImage(new OpacityEffect
|
||||
{
|
||||
Source = opacity,
|
||||
Opacity = _albumArtOpacityTransition.Value
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawTitleAndArtist(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
if (_lastSongTitle != null || _lastSongArtist != null)
|
||||
{
|
||||
DrawSingleTitleAndArtist(control, ds, _lastSongTitle, _lastSongArtist, 1 - _songInfoOpacityTransition.Value);
|
||||
}
|
||||
if (_songTitle != null || _songArtist != null)
|
||||
{
|
||||
DrawSingleTitleAndArtist(control, ds, _songTitle, _songArtist, _songInfoOpacityTransition.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSingleTitleAndArtist(ICanvasAnimatedControl control, CanvasDrawingSession ds, string? title, string? artist, float opacity)
|
||||
{
|
||||
CanvasTextLayout titleLayout = new(
|
||||
control, title ?? string.Empty,
|
||||
_titleTextFormat, _albumArtSize, _canvasHeight
|
||||
);
|
||||
CanvasTextLayout artistLayout = new(
|
||||
control, artist ?? string.Empty,
|
||||
_artistTextFormat, _albumArtSize, _canvasHeight
|
||||
);
|
||||
ds.DrawTextLayout(
|
||||
titleLayout,
|
||||
new Vector2(_albumArtXTransition.Value, _titleY),
|
||||
_bgFontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 255 * opacity)));
|
||||
ds.DrawTextLayout(
|
||||
artistLayout,
|
||||
new Vector2(_albumArtXTransition.Value, _titleY + (float)titleLayout.LayoutBounds.Height),
|
||||
_bgFontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 128 * opacity)));
|
||||
}
|
||||
|
||||
private void DrawBlurredLyrics(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
var currentPlayingLineIndex = GetCurrentPlayingLineIndex();
|
||||
|
||||
var currentPlayingLine = _multiLangLyrics
|
||||
.SafeGet(_langIndex)
|
||||
?.SafeGet(currentPlayingLineIndex);
|
||||
?.SafeGet(_playingLineIndex);
|
||||
|
||||
if (currentPlayingLine == null)
|
||||
{
|
||||
@@ -206,67 +309,71 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
switch (LyricsAlignmentType)
|
||||
{
|
||||
case LyricsAlignmentType.Left:
|
||||
case TextAlignmentType.Left:
|
||||
textLayout.HorizontalAlignment = CanvasHorizontalAlignment.Left;
|
||||
break;
|
||||
case LyricsAlignmentType.Center:
|
||||
case TextAlignmentType.Center:
|
||||
textLayout.HorizontalAlignment = CanvasHorizontalAlignment.Center;
|
||||
centerX += (float)_maxLyricsWidthTransition.Value / 2;
|
||||
centerX += _maxLyricsWidth / 2;
|
||||
break;
|
||||
case LyricsAlignmentType.Right:
|
||||
case TextAlignmentType.Right:
|
||||
textLayout.HorizontalAlignment = CanvasHorizontalAlignment.Right;
|
||||
centerX += (float)_maxLyricsWidthTransition.Value;
|
||||
centerX += _maxLyricsWidth;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
float offsetToLeft =
|
||||
(float)control.Size.Width - _rightMargin - _maxLyricsWidthTransition.Value;
|
||||
|
||||
// 组合变换:缩放 -> 旋转 -> 平移
|
||||
ds.Transform =
|
||||
Matrix3x2.CreateScale(line.ScaleTransition.Value, new Vector2(centerX, centerY))
|
||||
* Matrix3x2.CreateRotation(
|
||||
line.AngleTransition.Value,
|
||||
currentPlayingLine.Position
|
||||
)
|
||||
* Matrix3x2.CreateTranslation(
|
||||
offsetToLeft,
|
||||
_canvasYScrollTransition.Value + (float)(control.Size.Height / 2)
|
||||
);
|
||||
* Matrix3x2.CreateRotation(line.AngleTransition.Value, currentPlayingLine.Position)
|
||||
* Matrix3x2.CreateTranslation(_lyricsXTransition.Value, _canvasYScrollTransition.Value + _canvasHeight / 2);
|
||||
|
||||
// Create the original lyrics line
|
||||
using var lyrics = new CanvasCommandList(control.Device);
|
||||
using var lyricsDs = lyrics.CreateDrawingSession();
|
||||
lyricsDs.DrawTextLayout(textLayout, position, _fontColor);
|
||||
// Create the background lyrics line with stroke and fill
|
||||
using var bgLyrics = new CanvasCommandList(control.Device);
|
||||
using var bgLyricsDs = bgLyrics.CreateDrawingSession();
|
||||
|
||||
// Create the foreground lyrics line with stroke and fill
|
||||
using var fgLyrics = new CanvasCommandList(control.Device);
|
||||
using var fgLyricsDs = fgLyrics.CreateDrawingSession();
|
||||
|
||||
// 创建文字几何体
|
||||
using (var textGeometry = CanvasGeometry.CreateText(textLayout))
|
||||
{
|
||||
if (_isDesktopMode)
|
||||
{
|
||||
bgLyricsDs.DrawGeometry(textGeometry, position, _strokeFontColor, _lyricsFontStrokeWidth); // 背景描边
|
||||
fgLyricsDs.DrawGeometry(textGeometry, position, _strokeFontColor, _lyricsFontStrokeWidth); // 前景描边
|
||||
}
|
||||
|
||||
bgLyricsDs.FillGeometry(textGeometry, position, _bgFontColor); // 背景填充
|
||||
fgLyricsDs.FillGeometry(textGeometry, position, _fgFontColor); // 前景填充
|
||||
}
|
||||
|
||||
// Mock gradient blurred lyrics layer
|
||||
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层
|
||||
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次)
|
||||
// Current line will not be blurred
|
||||
ds.DrawImage(
|
||||
new GaussianBlurEffect
|
||||
{
|
||||
Source = new OpacityEffect { Source = lyrics, Opacity = line.OpacityTransition.Value * _lyricsOpacityTransition.Value },
|
||||
Source = new OpacityEffect { Source = bgLyrics, Opacity = line.OpacityTransition.Value * _lyricsOpacityTransition.Value },
|
||||
BlurAmount = line.BlurAmountTransition.Value,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
BorderMode = EffectBorderMode.Soft,
|
||||
}
|
||||
);
|
||||
|
||||
// 再叠加当前行歌词层
|
||||
// Only draw the current line and the two lines around it
|
||||
// This layer is to highlight the current line
|
||||
// and for fade-in and fade-out effects, two lines around it is also drawn
|
||||
if (Math.Abs(i - currentPlayingLineIndex) <= 1)
|
||||
if (line.HighlightOpacityTransition.Value != 0)
|
||||
{
|
||||
// 再叠加高亮行歌词层(前景歌词层)
|
||||
using var mask = new CanvasCommandList(control.Device);
|
||||
using var maskDs = mask.CreateDrawingSession();
|
||||
|
||||
using var highlightMask = new CanvasCommandList(control.Device);
|
||||
using var highlightMaskDs = highlightMask.CreateDrawingSession();
|
||||
|
||||
if (i == currentPlayingLineIndex)
|
||||
if (i == _playingLineIndex)
|
||||
{
|
||||
GetLinePlayingProgress(
|
||||
line,
|
||||
@@ -343,12 +450,19 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = 0f;
|
||||
var regions = textLayout.GetCharacterRegions(0, string.Join("", line.CharTimings.Select(x => x.Text)).Length);
|
||||
if (regions.Length > 0)
|
||||
{
|
||||
height = (float)regions[^1].LayoutBounds.Bottom - (float)regions[0].LayoutBounds.Top;
|
||||
}
|
||||
|
||||
maskDs.FillRectangle(
|
||||
new Rect(
|
||||
textLayout.LayoutBounds.X,
|
||||
position.Y,
|
||||
textLayout.LayoutBounds.Width,
|
||||
textLayout.LayoutBounds.Height
|
||||
height
|
||||
),
|
||||
Colors.White
|
||||
);
|
||||
@@ -364,7 +478,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
Source = new AlphaMaskEffect
|
||||
{
|
||||
Source = lyrics,
|
||||
Source = fgLyrics,
|
||||
AlphaMask = LyricsGlowEffectScope switch
|
||||
{
|
||||
LineRenderingType.UntilCurrentChar => mask,
|
||||
@@ -378,7 +492,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
: new CanvasCommandList(control.Device),
|
||||
Foreground = new AlphaMaskEffect
|
||||
{
|
||||
Source = lyrics,
|
||||
Source = fgLyrics,
|
||||
AlphaMask = mask,
|
||||
},
|
||||
},
|
||||
@@ -399,7 +513,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
)
|
||||
{
|
||||
ds.FillRectangle(
|
||||
new Rect(0, 0, control.Size.Width, control.Size.Height),
|
||||
new Rect(0, 0, _canvasWidth, _canvasHeight),
|
||||
new CanvasLinearGradientBrush(
|
||||
control,
|
||||
[
|
||||
@@ -424,7 +538,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
)
|
||||
{
|
||||
StartPoint = new Vector2(0, 0),
|
||||
EndPoint = new Vector2(0, (float)control.Size.Height),
|
||||
EndPoint = new Vector2(0, _canvasHeight),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -451,87 +565,5 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
EndPoint = new Vector2(startX + width, 0),
|
||||
};
|
||||
}
|
||||
|
||||
void DrawShenGuang(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
float w = (float)control.Size.Width;
|
||||
float h = (float)control.Size.Height;
|
||||
|
||||
float beamLength = h; // 光束长度等于画布高度
|
||||
float beamAngle = (float)(Math.PI / 6); // 30°
|
||||
float centerX = w / 2;
|
||||
float centerY = h;
|
||||
float angle = _shenGuangAngleTransition.Value;
|
||||
|
||||
var p0 = new Vector2(centerX, centerY);
|
||||
var p1 = new Vector2(
|
||||
centerX + beamLength * (float)Math.Cos(angle - beamAngle / 2),
|
||||
centerY + beamLength * (float)Math.Sin(angle - beamAngle / 2)
|
||||
);
|
||||
var p2 = new Vector2(
|
||||
centerX + beamLength * (float)Math.Cos(angle + beamAngle / 2),
|
||||
centerY + beamLength * (float)Math.Sin(angle + beamAngle / 2)
|
||||
);
|
||||
|
||||
using var path = new CanvasPathBuilder(control);
|
||||
path.BeginFigure(p0);
|
||||
path.AddLine(p1);
|
||||
path.AddArc(
|
||||
p2,
|
||||
beamLength,
|
||||
beamLength,
|
||||
0,
|
||||
CanvasSweepDirection.Clockwise,
|
||||
CanvasArcSize.Small
|
||||
);
|
||||
path.EndFigure(CanvasFigureLoop.Closed);
|
||||
|
||||
using var geometry = CanvasGeometry.CreatePath(path);
|
||||
|
||||
// 渐变为白色,透明度递减
|
||||
using var brush = new CanvasRadialGradientBrush(
|
||||
control,
|
||||
new[]
|
||||
{
|
||||
new CanvasGradientStop
|
||||
{
|
||||
Position = 0f,
|
||||
Color = Color.FromArgb(180, 255, 255, 255),
|
||||
},
|
||||
new CanvasGradientStop
|
||||
{
|
||||
Position = 0.5f,
|
||||
Color = Color.FromArgb(60, 255, 255, 255),
|
||||
},
|
||||
new CanvasGradientStop
|
||||
{
|
||||
Position = 1f,
|
||||
Color = Color.FromArgb(0, 255, 255, 255),
|
||||
},
|
||||
}
|
||||
)
|
||||
{
|
||||
Center = p0,
|
||||
OriginOffset = new Vector2(0, 0),
|
||||
RadiusX = beamLength * 0.8f,
|
||||
RadiusY = beamLength * 0.8f,
|
||||
};
|
||||
|
||||
using var beamCmd = new CanvasCommandList(control);
|
||||
using (var beamDs = beamCmd.CreateDrawingSession())
|
||||
{
|
||||
beamDs.FillGeometry(geometry, brush);
|
||||
}
|
||||
|
||||
var blur = new GaussianBlurEffect
|
||||
{
|
||||
Source = beamCmd,
|
||||
BlurAmount = 36f,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
BorderMode = EffectBorderMode.Soft,
|
||||
};
|
||||
|
||||
ds.DrawImage(blur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using BetterInAppLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Graphics.Imaging;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
@@ -17,41 +13,39 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
public partial class LyricsRendererViewModel
|
||||
: IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<float>>,
|
||||
IRecipient<PropertyChangedMessage<double>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<Color>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsDisplayType>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsFontColorType>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsAlignmentType>>,
|
||||
IRecipient<PropertyChangedMessage<TextAlignmentType>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsFontWeight>>,
|
||||
IRecipient<PropertyChangedMessage<LineRenderingType>>,
|
||||
IRecipient<PropertyChangedMessage<ElementTheme>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>>>
|
||||
{
|
||||
public async void Receive(
|
||||
PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>> message
|
||||
)
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LocalLyricsFolders))
|
||||
{
|
||||
// Music lib changed, re-fetch lyrics
|
||||
await RefreshLyricsAsync();
|
||||
_logger.LogInformation("Local lyrics folders changed, refreshing lyrics.");
|
||||
RefreshLyricsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void Receive(
|
||||
PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>> message
|
||||
)
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsSearchProvidersInfo))
|
||||
{
|
||||
// Lyrics search providers info changed, re-fetch lyrics
|
||||
await RefreshLyricsAsync();
|
||||
_logger.LogInformation("Lyrics search providers info changed, refreshing lyrics.");
|
||||
RefreshLyricsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,35 +56,22 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (
|
||||
message.PropertyName
|
||||
== nameof(SettingsPageViewModel.IsDynamicCoverOverlayEnabled)
|
||||
)
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.IsDynamicCoverOverlayEnabled))
|
||||
{
|
||||
IsDynamicCoverOverlayEnabled = message.NewValue;
|
||||
}
|
||||
else if (
|
||||
message.PropertyName == nameof(SettingsPageViewModel.IsDebugOverlayEnabled)
|
||||
)
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.IsDebugOverlayEnabled))
|
||||
{
|
||||
_isDebugOverlayEnabled = message.NewValue;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsSettingsControlViewModel)
|
||||
{
|
||||
if (
|
||||
message.PropertyName
|
||||
== nameof(LyricsSettingsControlViewModel.IsLyricsGlowEffectEnabled)
|
||||
)
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.IsLyricsGlowEffectEnabled))
|
||||
{
|
||||
IsLyricsGlowEffectEnabled = message.NewValue;
|
||||
}
|
||||
else if (
|
||||
message.PropertyName
|
||||
== nameof(LyricsSettingsControlViewModel.IsFanLyricsEnabled)
|
||||
)
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.IsFanLyricsEnabled))
|
||||
{
|
||||
_isFanLyricsEnabled = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsWindowViewModel)
|
||||
@@ -104,6 +85,15 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_isDesktopMode = message.NewValue;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsPageViewModel.IsTranslationEnabled))
|
||||
{
|
||||
_isTranslationEnabled = message.NewValue;
|
||||
_logger.LogInformation("Translation enabled state changed: {IsEnabled}", _isTranslationEnabled);
|
||||
UpdateTranslationsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<Color> message)
|
||||
@@ -113,40 +103,35 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (message.PropertyName == nameof(LyricsWindowViewModel.ActivatedWindowAccentColor))
|
||||
{
|
||||
_immersiveBgTransition.StartTransition(message.NewValue);
|
||||
_lyricsWindowBgColor = message.NewValue;
|
||||
_adaptiveFontColor = Helper.ColorHelper.GetForegroundColor(_lyricsWindowBgColor);
|
||||
_environmentalColor = message.NewValue;
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsSettingsControlViewModel)
|
||||
else if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsCustomFontColor))
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomBgFontColor))
|
||||
{
|
||||
_customFontColor = message.NewValue;
|
||||
_customBgFontColor = message.NewValue;
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<double> message)
|
||||
{
|
||||
if (message.Sender is LyricsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsPageViewModel.MaxLyricsWidth))
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomFgFontColor))
|
||||
{
|
||||
_maxLyricsWidthTransition.StartTransition((float)message.NewValue);
|
||||
_customFgFontColor = message.NewValue;
|
||||
UpdateFontColor();
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomStrokeFontColor))
|
||||
{
|
||||
_customStrokeFontColor = message.NewValue;
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<float> message)
|
||||
{
|
||||
if (message.Sender is LyricsSettingsControlViewModel)
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (
|
||||
message.PropertyName
|
||||
== nameof(LyricsSettingsControlViewModel.LyricsLineSpacingFactor)
|
||||
)
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsLineSpacingFactor))
|
||||
{
|
||||
LyricsLineSpacingFactor = message.NewValue;
|
||||
}
|
||||
@@ -159,155 +144,141 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.CoverImageRadius))
|
||||
{
|
||||
CoverImageRadius = message.NewValue;
|
||||
_albumArtCornerRadius = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayOpacity))
|
||||
{
|
||||
CoverOverlayOpacity = message.NewValue;
|
||||
}
|
||||
else if (
|
||||
message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayBlurAmount)
|
||||
)
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.CoverOverlayBlurAmount))
|
||||
{
|
||||
CoverOverlayBlurAmount = message.NewValue;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsSettingsControlViewModel)
|
||||
{
|
||||
if (
|
||||
message.PropertyName
|
||||
== nameof(LyricsSettingsControlViewModel.LyricsVerticalEdgeOpacity)
|
||||
)
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsVerticalEdgeOpacity))
|
||||
{
|
||||
LyricsVerticalEdgeOpacity = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (
|
||||
message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsBlurAmount)
|
||||
)
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBlurAmount))
|
||||
{
|
||||
LyricsBlurAmount = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (
|
||||
message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontSize)
|
||||
)
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize))
|
||||
{
|
||||
LyricsFontSize = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.SelectedTargetLanguageIndex))
|
||||
{
|
||||
_targetLanguageIndex = message.NewValue;
|
||||
_logger.LogInformation("Target language index changed: {Index}", _targetLanguageIndex);
|
||||
UpdateTranslationsAsync();
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontStrokeWidth))
|
||||
{
|
||||
_lyricsFontStrokeWidth = message.NewValue;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsPageViewModel.PositionOffset))
|
||||
{
|
||||
_positionOffset = TimeSpan.FromMilliseconds(message.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LineRenderingType> message)
|
||||
{
|
||||
if (message.Sender is LyricsSettingsControlViewModel)
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (
|
||||
message.PropertyName
|
||||
== nameof(LyricsSettingsControlViewModel.LyricsGlowEffectScope)
|
||||
)
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsGlowEffectScope))
|
||||
{
|
||||
LyricsGlowEffectScope = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsAlignmentType> message)
|
||||
public void Receive(PropertyChangedMessage<TextAlignmentType> message)
|
||||
{
|
||||
if (message.Sender is LyricsSettingsControlViewModel)
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (
|
||||
message.PropertyName
|
||||
== nameof(LyricsSettingsControlViewModel.LyricsAlignmentType)
|
||||
)
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsAlignmentType))
|
||||
{
|
||||
LyricsAlignmentType = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.SongInfoAlignmentType))
|
||||
{
|
||||
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment =
|
||||
message.NewValue.ToCanvasHorizontalAlignment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsDisplayType> message)
|
||||
{
|
||||
DisplayType = message.NewValue;
|
||||
_displayTypeReceived = message.NewValue;
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsFontColorType> message)
|
||||
{
|
||||
if (message.Sender is LyricsSettingsControlViewModel)
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (
|
||||
message.PropertyName
|
||||
== nameof(LyricsSettingsControlViewModel.LyricsFontColorType)
|
||||
)
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBgFontColorType))
|
||||
{
|
||||
LyricsFontColorType = message.NewValue;
|
||||
_lyricsBgFontColorType = message.NewValue;
|
||||
UpdateFontColor();
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFgFontColorType))
|
||||
{
|
||||
_lyricsFgFontColorType = message.NewValue;
|
||||
UpdateFontColor();
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsStrokeFontColorType))
|
||||
{
|
||||
_lyricsStrokeFontColorType = message.NewValue;
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsFontWeight> message)
|
||||
{
|
||||
if (message.Sender is LyricsSettingsControlViewModel)
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontWeight))
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontWeight))
|
||||
{
|
||||
LyricsFontWeight = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnLyricsFontColorTypeChanged(LyricsFontColorType value)
|
||||
public void Receive(PropertyChangedMessage<ElementTheme> message)
|
||||
{
|
||||
UpdateFontColor();
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBackgroundTheme))
|
||||
{
|
||||
_lyricsBgTheme = message.NewValue;
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnLyricsFontSizeChanged(int value)
|
||||
{
|
||||
_isRelayoutNeeded = true;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
|
||||
partial void OnLyricsFontWeightChanged(LyricsFontWeight value)
|
||||
{
|
||||
_textFormat.FontWeight = value.ToFontWeight();
|
||||
_isRelayoutNeeded = true;
|
||||
_lyricsTextFormat.FontWeight = value.ToFontWeight();
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
|
||||
partial void OnLyricsLineSpacingFactorChanged(float value)
|
||||
{
|
||||
_isRelayoutNeeded = true;
|
||||
}
|
||||
|
||||
async partial void OnSongInfoChanged(SongInfo? oldValue, SongInfo? newValue)
|
||||
{
|
||||
TotalTime = TimeSpan.Zero;
|
||||
|
||||
SoftwareBitmap? newalbumArtBitmap;
|
||||
Color? newAlbumArtAccentColor;
|
||||
|
||||
if (newValue?.AlbumArt is byte[] bytes)
|
||||
{
|
||||
var decoder = await ImageHelper.GetDecoderFromByte(bytes);
|
||||
newalbumArtBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
|
||||
newAlbumArtAccentColor = (ImageHelper.GetAccentColorsFromByte(bytes)).SafeGet(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
newalbumArtBitmap = null;
|
||||
newAlbumArtAccentColor = null;
|
||||
}
|
||||
|
||||
_lastAlbumArtBitmap = _albumArtBitmap;
|
||||
_albumArtBitmap = newalbumArtBitmap;
|
||||
|
||||
_albumArtBgTransition.Reset(0f);
|
||||
_albumArtBgTransition.StartTransition(1f);
|
||||
|
||||
_albumArtAccentColor = newAlbumArtAccentColor;
|
||||
_lyricsWindowBgColor = _albumArtAccentColor ?? Colors.Gray;
|
||||
|
||||
if (!_isDesktopMode && !_isDockMode) _adaptiveFontColor = Helper.ColorHelper.GetForegroundColor(_lyricsWindowBgColor);
|
||||
|
||||
UpdateFontColor();
|
||||
|
||||
await RefreshLyricsAsync();
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using Microsoft.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class LyricsRendererViewModel
|
||||
{
|
||||
private readonly ValueTransition<float> _canvasYScrollTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<Color> _immersiveBgTransition = new(
|
||||
initialValue: Colors.Transparent,
|
||||
durationSeconds: 0.3f,
|
||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _lyricsXTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _lyricsOpacityTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _albumArtBgTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 1f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _albumArtOpacityTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 1f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _albumArtXTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _songInfoOpacityTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 1f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _lyricsBgBrightnessTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 1f
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class LyricsRendererViewModel
|
||||
{
|
||||
private bool _isCanvasWidthChanged = false;
|
||||
private bool _isCanvasHeightChanged = false;
|
||||
|
||||
private bool _isDisplayTypeChanged = false;
|
||||
|
||||
private bool _isPlayingLineChanged = false;
|
||||
private bool _isVisibleLinesBoundaryChanged = false;
|
||||
|
||||
public void Update(ICanvasAnimatedControl control, CanvasAnimatedUpdateEventArgs args)
|
||||
{
|
||||
_elapsedTime = args.Timing.ElapsedTime;
|
||||
|
||||
if (_isPlaying)
|
||||
{
|
||||
TotalTime += args.Timing.ElapsedTime;
|
||||
_totalTime += _elapsedTime;
|
||||
}
|
||||
|
||||
ElapsedTime = args.Timing.ElapsedTime;
|
||||
var playingLineIndex = GetCurrentPlayingLineIndex();
|
||||
|
||||
if (_immersiveBgTransition.IsTransitioning)
|
||||
{
|
||||
_immersiveBgTransition.Update(ElapsedTime);
|
||||
}
|
||||
_isCanvasWidthChanged = _canvasWidth != control.Size.Width;
|
||||
_isCanvasHeightChanged = _canvasHeight != control.Size.Height;
|
||||
_isDisplayTypeChanged = _displayType != _displayTypeReceived;
|
||||
_isPlayingLineChanged = _playingLineIndex != playingLineIndex;
|
||||
|
||||
if (_albumArtBgTransition.IsTransitioning)
|
||||
{
|
||||
_albumArtBgTransition.Update(ElapsedTime);
|
||||
}
|
||||
_canvasWidth = (float)control.Size.Width;
|
||||
_canvasHeight = (float)control.Size.Height;
|
||||
_displayType = _displayTypeReceived;
|
||||
_playingLineIndex = playingLineIndex;
|
||||
|
||||
_immersiveBgTransition.Update(_elapsedTime);
|
||||
_albumArtBgTransition.Update(_elapsedTime);
|
||||
_lyricsBgBrightnessTransition.Update(_elapsedTime);
|
||||
_songInfoOpacityTransition.Update(_elapsedTime);
|
||||
|
||||
if (IsDynamicCoverOverlayEnabled)
|
||||
{
|
||||
@@ -38,54 +54,74 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_rotateAngle %= MathF.PI * 2;
|
||||
}
|
||||
|
||||
if (_maxLyricsWidthTransition.IsTransitioning)
|
||||
if (_isCanvasHeightChanged)
|
||||
{
|
||||
_maxLyricsWidthTransition.Update(ElapsedTime);
|
||||
_isRelayoutNeeded = true;
|
||||
_albumArtY = 36 + (_canvasHeight - 36 * 2) * 3 / 16f;
|
||||
}
|
||||
|
||||
switch (DisplayType)
|
||||
if (_isCanvasWidthChanged || _isCanvasHeightChanged)
|
||||
{
|
||||
case Enums.LyricsDisplayType.AlbumArtOnly:
|
||||
_lyricsOpacityTransition.StartTransition(0f);
|
||||
break;
|
||||
case Enums.LyricsDisplayType.LyricsOnly:
|
||||
case Enums.LyricsDisplayType.SplitView:
|
||||
_lyricsOpacityTransition.StartTransition(1f);
|
||||
break;
|
||||
case Enums.LyricsDisplayType.PlaceholderOnly:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
_albumArtSize = MathF.Min(
|
||||
(_canvasHeight - _topMargin - _bottomMargin) * 8.5f / 16,
|
||||
(_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2);
|
||||
_albumArtSize = MathF.Max(0, _albumArtSize);
|
||||
|
||||
_titleY = _albumArtY + _albumArtSize * 1.05f;
|
||||
}
|
||||
|
||||
if (_lyricsOpacityTransition.IsTransitioning)
|
||||
if (_isDisplayTypeChanged || _isCanvasWidthChanged)
|
||||
{
|
||||
_lyricsOpacityTransition.Update(ElapsedTime);
|
||||
bool jumpTo = !_isDisplayTypeChanged && _isCanvasWidthChanged;
|
||||
switch (_displayType)
|
||||
{
|
||||
case LyricsDisplayType.AlbumArtOnly:
|
||||
_lyricsOpacityTransition.StartTransition(0f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtXTransition.StartTransition(_canvasWidth / 2 - _albumArtSize / 2, jumpTo);
|
||||
break;
|
||||
case LyricsDisplayType.LyricsOnly:
|
||||
_lyricsOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(0f, jumpTo);
|
||||
_lyricsXTransition.StartTransition(_leftMargin, jumpTo);
|
||||
break;
|
||||
case LyricsDisplayType.SplitView:
|
||||
_lyricsOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_lyricsXTransition.StartTransition((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 + _leftMargin + _middleMargin, jumpTo);
|
||||
_albumArtXTransition.StartTransition(_leftMargin + ((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 - _albumArtSize) / 2, jumpTo);
|
||||
break;
|
||||
case LyricsDisplayType.PlaceholderOnly:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 神光角度目标值(左右±15度摆动,周期约4秒)
|
||||
double t = DateTimeOffset.Now.ToUnixTimeMilliseconds() / 1000.0;
|
||||
float targetAngle = (float)(-Math.PI / 2 + Math.Sin(t * Math.PI / 2) * (Math.PI / 12)); // -90°为正上,±15°摆动
|
||||
_shenGuangAngleTransition.StartTransition(targetAngle);
|
||||
_lyricsXTransition.Update(_elapsedTime);
|
||||
_albumArtXTransition.Update(_elapsedTime);
|
||||
_lyricsOpacityTransition.Update(_elapsedTime);
|
||||
_albumArtOpacityTransition.Update(_elapsedTime);
|
||||
|
||||
if (_shenGuangAngleTransition.IsTransitioning)
|
||||
if (_isCanvasWidthChanged || _lyricsXTransition.IsTransitioning)
|
||||
{
|
||||
_shenGuangAngleTransition.Update(ElapsedTime);
|
||||
_maxLyricsWidth = _canvasWidth - _lyricsXTransition.Value - _rightMargin;
|
||||
_maxLyricsWidth = Math.Max(_maxLyricsWidth, 0);
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
|
||||
if (_isRelayoutNeeded)
|
||||
if (_isLayoutChanged)
|
||||
{
|
||||
ReLayout(control);
|
||||
_isRelayoutNeeded = false;
|
||||
UpdateCanvasYScrollOffset(control, false);
|
||||
UpdateCanvasYScrollOffset(control, true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCanvasYScrollOffset(control, true);
|
||||
UpdateCanvasYScrollOffset(control, false, true);
|
||||
}
|
||||
|
||||
UpdateLinesProps();
|
||||
|
||||
_isLayoutChanged = false;
|
||||
}
|
||||
|
||||
private void ReLayout(ICanvasAnimatedControl control)
|
||||
@@ -93,9 +129,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (control == null)
|
||||
return;
|
||||
|
||||
_textFormat.FontSize = LyricsFontSize;
|
||||
_lyricsTextFormat.FontSize = LyricsFontSize;
|
||||
|
||||
float y = _topMargin;
|
||||
float y = 0;
|
||||
|
||||
// Init Positions
|
||||
for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++)
|
||||
@@ -116,10 +152,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
// Calculate layout bounds
|
||||
line.CanvasTextLayout = new CanvasTextLayout(
|
||||
control,
|
||||
line.Text,
|
||||
_textFormat,
|
||||
(float)_maxLyricsWidthTransition.Value,
|
||||
(float)control.Size.Height
|
||||
line.DisplayedText,
|
||||
_lyricsTextFormat,
|
||||
_maxLyricsWidth,
|
||||
_canvasHeight
|
||||
);
|
||||
|
||||
line.Position = new Vector2(0, y);
|
||||
@@ -131,51 +167,35 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCanvasYScrollOffset(ICanvasAnimatedControl control, bool withAnimation)
|
||||
private void UpdateCanvasYScrollOffset(ICanvasAnimatedControl control, bool forceScroll, bool withAnimation)
|
||||
{
|
||||
var currentPlayingLineIndex = GetCurrentPlayingLineIndex();
|
||||
|
||||
var (startLineIndex, endLineIndex) = GetMaxLyricsLineIndexBoundaries();
|
||||
|
||||
if (startLineIndex < 0 || endLineIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (startLineIndex < 0 || endLineIndex < 0) return;
|
||||
|
||||
// Set _scrollOffsetY
|
||||
LyricsLine? currentPlayingLine = _multiLangLyrics
|
||||
.SafeGet(_langIndex)
|
||||
?.SafeGet(currentPlayingLineIndex);
|
||||
|
||||
var playingTextLayout = currentPlayingLine?.CanvasTextLayout;
|
||||
|
||||
if (currentPlayingLine == null || playingTextLayout == null)
|
||||
if ((!_isPlayingLineChanged && forceScroll) || _isPlayingLineChanged)
|
||||
{
|
||||
return;
|
||||
LyricsLine? currentPlayingLine = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(_playingLineIndex);
|
||||
|
||||
if (currentPlayingLine == null) return;
|
||||
|
||||
var playingTextLayout = currentPlayingLine?.CanvasTextLayout;
|
||||
|
||||
if (playingTextLayout == null) return;
|
||||
|
||||
float? targetYScrollOffset = (float?)(-currentPlayingLine!.Position.Y + _multiLangLyrics.SafeGet(_langIndex)?[0].Position.Y - playingTextLayout.LayoutBounds.Height / 2);
|
||||
|
||||
if (!targetYScrollOffset.HasValue) return;
|
||||
|
||||
_canvasYScrollTransition.StartTransition(targetYScrollOffset.Value, !withAnimation);
|
||||
}
|
||||
|
||||
float targetYScrollOffset =
|
||||
(float?)(
|
||||
-currentPlayingLine.Position.Y
|
||||
+ _multiLangLyrics.SafeGet(_langIndex)?[0].Position.Y
|
||||
- playingTextLayout.LayoutBounds.Height / 2
|
||||
) ?? 0f;
|
||||
_canvasYScrollTransition.Update(_elapsedTime);
|
||||
|
||||
if (withAnimation && !_canvasYScrollTransition.IsTransitioning)
|
||||
{
|
||||
_canvasYScrollTransition.StartTransition(targetYScrollOffset);
|
||||
}
|
||||
else if (!withAnimation)
|
||||
{
|
||||
_canvasYScrollTransition.JumpTo(targetYScrollOffset);
|
||||
}
|
||||
|
||||
if (_canvasYScrollTransition.IsTransitioning)
|
||||
{
|
||||
_canvasYScrollTransition.Update(ElapsedTime);
|
||||
}
|
||||
|
||||
_startVisibleLineIndex = _endVisibleLineIndex = -1;
|
||||
int startVisibleLineIndex = -1;
|
||||
int endVisibleLineIndex = -1;
|
||||
|
||||
// Update visible line indices
|
||||
for (int i = startLineIndex; i <= endLineIndex; i++)
|
||||
@@ -191,66 +211,146 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
if (
|
||||
_canvasYScrollTransition.Value
|
||||
+ (float)(control.Size.Height / 2)
|
||||
+ _canvasHeight / 2
|
||||
+ line.Position.Y
|
||||
+ textLayout.LayoutBounds.Height
|
||||
>= 0
|
||||
)
|
||||
{
|
||||
if (_startVisibleLineIndex == -1)
|
||||
if (startVisibleLineIndex == -1)
|
||||
{
|
||||
_startVisibleLineIndex = i;
|
||||
startVisibleLineIndex = i;
|
||||
}
|
||||
}
|
||||
if (
|
||||
_canvasYScrollTransition.Value
|
||||
+ (float)(control.Size.Height / 2)
|
||||
+ _canvasHeight / 2
|
||||
+ line.Position.Y
|
||||
+ textLayout.LayoutBounds.Height
|
||||
>= control.Size.Height
|
||||
)
|
||||
{
|
||||
if (_endVisibleLineIndex == -1)
|
||||
if (endVisibleLineIndex == -1)
|
||||
{
|
||||
_endVisibleLineIndex = i;
|
||||
endVisibleLineIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_startVisibleLineIndex != -1 && _endVisibleLineIndex == -1)
|
||||
if (startVisibleLineIndex != -1 && endVisibleLineIndex == -1)
|
||||
{
|
||||
_endVisibleLineIndex = endLineIndex;
|
||||
endVisibleLineIndex = endLineIndex;
|
||||
}
|
||||
|
||||
_isVisibleLinesBoundaryChanged = _startVisibleLineIndex != startVisibleLineIndex || _endVisibleLineIndex != endVisibleLineIndex;
|
||||
|
||||
_startVisibleLineIndex = startVisibleLineIndex;
|
||||
_endVisibleLineIndex = endVisibleLineIndex;
|
||||
}
|
||||
|
||||
private protected void UpdateFontColor()
|
||||
private void UpdateFontColor()
|
||||
{
|
||||
ThemeTypeSent =
|
||||
Helper.ColorHelper.GetElementThemeFromBackgroundColor(_lyricsWindowBgColor);
|
||||
if (_isDesktopMode || _isDockMode)
|
||||
{
|
||||
ThemeTypeSent = Helper.ColorHelper.GetElementThemeFromBackgroundColor(_environmentalColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThemeTypeSent = _lyricsBgTheme;
|
||||
}
|
||||
|
||||
float brightness = 0f;
|
||||
|
||||
Color grayedEnvironmentalColor = Colors.Transparent;
|
||||
|
||||
Color fallbackFg = Colors.Transparent;
|
||||
switch (ThemeTypeSent)
|
||||
{
|
||||
case ElementTheme.Default:
|
||||
switch (Application.Current.RequestedTheme)
|
||||
{
|
||||
case ApplicationTheme.Light:
|
||||
_adaptiveGrayedFontColor = _darkColor;
|
||||
brightness = 0.7f;
|
||||
break;
|
||||
case ApplicationTheme.Dark:
|
||||
_adaptiveGrayedFontColor = _lightColor;
|
||||
brightness = 0.3f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ElementTheme.Light:
|
||||
fallbackFg = _darkFontColor;
|
||||
_adaptiveGrayedFontColor = _darkColor;
|
||||
brightness = 0.7f;
|
||||
break;
|
||||
case ElementTheme.Dark:
|
||||
fallbackFg = _lightFontColor;
|
||||
_adaptiveGrayedFontColor = _lightColor;
|
||||
brightness = 0.3f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (LyricsFontColorType)
|
||||
if (_adaptiveGrayedFontColor == _lightColor)
|
||||
{
|
||||
case Enums.LyricsFontColorType.AdaptiveGrayed:
|
||||
_fontColor = fallbackFg;
|
||||
grayedEnvironmentalColor = _darkColor;
|
||||
} else if (_adaptiveGrayedFontColor == _darkColor)
|
||||
{
|
||||
grayedEnvironmentalColor = _lightColor;
|
||||
}
|
||||
|
||||
_lyricsBgBrightnessTransition.StartTransition(brightness);
|
||||
|
||||
if (_isDesktopMode || _isDockMode)
|
||||
{
|
||||
_adaptiveColoredFontColor = Helper.ColorHelper.GetForegroundColor(_environmentalColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
_adaptiveColoredFontColor = Helper.ColorHelper.GetForegroundColor(_albumArtAccentColor?.WithBrightness(brightness) ?? Colors.Transparent);
|
||||
}
|
||||
|
||||
switch (_lyricsBgFontColorType)
|
||||
{
|
||||
case LyricsFontColorType.AdaptiveGrayed:
|
||||
_bgFontColor = _adaptiveGrayedFontColor;
|
||||
break;
|
||||
case Enums.LyricsFontColorType.AdaptiveColored:
|
||||
_fontColor = _adaptiveFontColor ?? fallbackFg;
|
||||
case LyricsFontColorType.AdaptiveColored:
|
||||
_bgFontColor = _adaptiveColoredFontColor ?? _adaptiveGrayedFontColor;
|
||||
break;
|
||||
case Enums.LyricsFontColorType.Custom:
|
||||
_fontColor = _customFontColor ?? fallbackFg;
|
||||
case LyricsFontColorType.Custom:
|
||||
_bgFontColor = _customBgFontColor ?? _adaptiveGrayedFontColor;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (_lyricsFgFontColorType)
|
||||
{
|
||||
case LyricsFontColorType.AdaptiveGrayed:
|
||||
_fgFontColor = _adaptiveGrayedFontColor;
|
||||
break;
|
||||
case LyricsFontColorType.AdaptiveColored:
|
||||
_fgFontColor = _adaptiveColoredFontColor ?? _adaptiveGrayedFontColor;
|
||||
break;
|
||||
case LyricsFontColorType.Custom:
|
||||
_fgFontColor = _customFgFontColor ?? _adaptiveGrayedFontColor;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (_lyricsStrokeFontColorType)
|
||||
{
|
||||
case LyricsFontColorType.AdaptiveGrayed:
|
||||
_strokeFontColor = grayedEnvironmentalColor.WithBrightness(0.7);
|
||||
break;
|
||||
case LyricsFontColorType.AdaptiveColored:
|
||||
_strokeFontColor = _environmentalColor.WithBrightness(0.7);
|
||||
break;
|
||||
case LyricsFontColorType.Custom:
|
||||
_strokeFontColor = _customStrokeFontColor ?? _environmentalColor;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -259,83 +359,42 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private void UpdateLinesProps()
|
||||
{
|
||||
var currentPlayingLineIndex = GetCurrentPlayingLineIndex();
|
||||
var currentPlayingLine = _multiLangLyrics
|
||||
.SafeGet(_langIndex)
|
||||
?.SafeGet(_playingLineIndex);
|
||||
|
||||
int halfVisibleLineCount =
|
||||
Math.Max(1, Math.Max(
|
||||
currentPlayingLineIndex - _startVisibleLineIndex,
|
||||
_endVisibleLineIndex - currentPlayingLineIndex
|
||||
));
|
||||
|
||||
if (halfVisibleLineCount < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (currentPlayingLine == null) return;
|
||||
|
||||
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++)
|
||||
{
|
||||
var line = _multiLangLyrics.SafeGet(_langIndex)?.SafeGet(i);
|
||||
|
||||
if (line == null)
|
||||
if (line == null) continue;
|
||||
|
||||
if (_isLayoutChanged || _isVisibleLinesBoundaryChanged || _isPlayingLineChanged)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
float distanceFromPlayingLine = Math.Abs(line.Position.Y - currentPlayingLine.Position.Y);
|
||||
float distanceFactor = Math.Clamp(distanceFromPlayingLine / (_canvasHeight / 2), 0, 1);
|
||||
|
||||
int distanceFromPlayingLine = Math.Abs(i - currentPlayingLineIndex);
|
||||
if (distanceFromPlayingLine > halfVisibleLineCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float distanceFactor = distanceFromPlayingLine / (float)halfVisibleLineCount;
|
||||
|
||||
line.AngleTransition.StartTransition(
|
||||
_isFanLyricsEnabled
|
||||
? (float)Math.PI
|
||||
* (30f / 180f)
|
||||
* distanceFactor
|
||||
* (i - currentPlayingLineIndex > 0 ? 1 : -1)
|
||||
: 0
|
||||
);
|
||||
line.BlurAmountTransition.StartTransition(LyricsBlurAmount * distanceFactor);
|
||||
line.ScaleTransition.StartTransition(
|
||||
_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale)
|
||||
);
|
||||
line.OpacityTransition.StartTransition(_defaultOpacity - distanceFactor * _defaultOpacity * (1 - LyricsVerticalEdgeOpacity / 100f));
|
||||
|
||||
// Only calculate highlight opacity for the current line and the two lines around it
|
||||
// to avoid unnecessary calculations
|
||||
if (distanceFromPlayingLine <= 1)
|
||||
{
|
||||
line.HighlightOpacityTransition.StartTransition(
|
||||
distanceFromPlayingLine == 0 ? 1 : 0
|
||||
line.AngleTransition.StartTransition(_isFanLyricsEnabled
|
||||
? (float)Math.PI
|
||||
* (30f / 180f)
|
||||
* distanceFactor
|
||||
* (i > _playingLineIndex ? 1 : -1)
|
||||
: 0
|
||||
);
|
||||
|
||||
line.BlurAmountTransition.StartTransition(LyricsBlurAmount * distanceFactor);
|
||||
line.ScaleTransition.StartTransition(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale));
|
||||
line.OpacityTransition.StartTransition(_defaultOpacity - distanceFactor * _defaultOpacity * (1 - LyricsVerticalEdgeOpacity / 100f));
|
||||
line.HighlightOpacityTransition.StartTransition(i == _playingLineIndex ? 1f : 0f);
|
||||
}
|
||||
|
||||
if (line.AngleTransition.IsTransitioning)
|
||||
{
|
||||
line.AngleTransition.Update(ElapsedTime);
|
||||
}
|
||||
if (line.ScaleTransition.IsTransitioning)
|
||||
{
|
||||
line.ScaleTransition.Update(ElapsedTime);
|
||||
}
|
||||
if (line.BlurAmountTransition.IsTransitioning)
|
||||
{
|
||||
line.BlurAmountTransition.Update(ElapsedTime);
|
||||
}
|
||||
if (line.OpacityTransition.IsTransitioning)
|
||||
{
|
||||
line.OpacityTransition.Update(ElapsedTime);
|
||||
}
|
||||
// Only update highlight opacity for the current line and the two lines around it
|
||||
if (distanceFromPlayingLine <= 1)
|
||||
{
|
||||
if (line.HighlightOpacityTransition.IsTransitioning)
|
||||
{
|
||||
line.HighlightOpacityTransition.Update(ElapsedTime);
|
||||
}
|
||||
}
|
||||
line.AngleTransition.Update(_elapsedTime);
|
||||
line.ScaleTransition.Update(_elapsedTime);
|
||||
line.BlurAmountTransition.Update(_elapsedTime);
|
||||
line.OpacityTransition.Update(_elapsedTime);
|
||||
line.HighlightOpacityTransition.Update(_elapsedTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using ABI.Microsoft.UI.Xaml;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
|
||||
@@ -19,118 +28,150 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class LyricsRendererViewModel : BaseViewModel
|
||||
{
|
||||
private readonly ValueTransition<float> _albumArtBgTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 1.0f
|
||||
);
|
||||
private TimeSpan _elapsedTime = TimeSpan.Zero;
|
||||
private TimeSpan _totalTime = TimeSpan.Zero;
|
||||
private TimeSpan _positionOffset = TimeSpan.Zero;
|
||||
|
||||
private readonly ValueTransition<float> _canvasYScrollTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.8f,
|
||||
easingType: EasingType.SmootherStep
|
||||
);
|
||||
private SoftwareBitmap? _lastAlbumArtSwBitmap = null;
|
||||
private SoftwareBitmap? _albumArtSwBitmap = null;
|
||||
|
||||
private readonly float _coverRotateSpeed = 0.003f;
|
||||
private CanvasBitmap? _lastAlbumArtCanvasBitmap = null;
|
||||
private CanvasBitmap? _albumArtCanvasBitmap = null;
|
||||
|
||||
private float _albumArtSize = 0f;
|
||||
private int _albumArtCornerRadius = 0;
|
||||
|
||||
private float _albumArtY = 0f;
|
||||
|
||||
private string? _lastSongTitle;
|
||||
private string? _songTitle;
|
||||
|
||||
private float _titleY = 0f;
|
||||
|
||||
private string? _lastSongArtist;
|
||||
private string? _songArtist;
|
||||
|
||||
private float _canvasWidth = 0f;
|
||||
private float _canvasHeight = 0f;
|
||||
|
||||
private readonly float _defaultOpacity = 0.3f;
|
||||
|
||||
private readonly float _defaultScale = 0.75f;
|
||||
|
||||
private readonly float _highlightedOpacity = 1.0f;
|
||||
|
||||
private readonly float _defaultScale = 0.75f;
|
||||
private readonly float _highlightedScale = 1.0f;
|
||||
|
||||
private readonly ValueTransition<Color> _immersiveBgTransition = new(
|
||||
initialValue: Colors.Transparent,
|
||||
durationSeconds: 0.3f,
|
||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||
);
|
||||
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
private readonly float _coverRotateSpeed = 0.003f;
|
||||
private float _rotateAngle = 0f;
|
||||
|
||||
private readonly float _lyricsGlowEffectAmount = 8f;
|
||||
|
||||
private readonly ValueTransition<float> _maxLyricsWidthTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f,
|
||||
easingType: EasingType.SmoothStep
|
||||
);
|
||||
private float _maxLyricsWidth = 0f;
|
||||
|
||||
private readonly ValueTransition<float> _lyricsOpacityTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private protected readonly IMusicSearchService _musicSearchService;
|
||||
|
||||
private protected readonly IPlaybackService _playbackService;
|
||||
private readonly IMusicSearchService _musicSearchService;
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
private readonly IPlaybackService _playbackService;
|
||||
private readonly ILibreTranslateService _libreTranslateService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly float _leftMargin = 36f;
|
||||
private readonly float _middleMargin = 36f;
|
||||
private readonly float _rightMargin = 36f;
|
||||
private readonly float _topMargin = 36f;
|
||||
private readonly float _bottomMargin = 36f;
|
||||
|
||||
private readonly ValueTransition<float> _shenGuangAngleTransition = new(0f, 0.2f);
|
||||
|
||||
private readonly float _topMargin = 0f;
|
||||
|
||||
private Color? _adaptiveFontColor = null;
|
||||
private Color _adaptiveGrayedFontColor = Colors.Transparent;
|
||||
private Color? _adaptiveColoredFontColor = null;
|
||||
|
||||
private Color? _albumArtAccentColor = null;
|
||||
private Color _environmentalColor = Colors.Transparent;
|
||||
|
||||
private SoftwareBitmap? _albumArtBitmap = null;
|
||||
private Color _lightColor = Colors.White;
|
||||
private Color _darkColor = Colors.Black;
|
||||
|
||||
private Color? _customFontColor;
|
||||
private Color _bgFontColor;
|
||||
private Color _fgFontColor;
|
||||
private Color _strokeFontColor;
|
||||
|
||||
private Color _darkFontColor = Colors.Black;
|
||||
private Color? _customBgFontColor;
|
||||
private Color? _customFgFontColor;
|
||||
private Color? _customStrokeFontColor;
|
||||
|
||||
private LyricsFontColorType _lyricsBgFontColorType;
|
||||
private LyricsFontColorType _lyricsFgFontColorType;
|
||||
private LyricsFontColorType _lyricsStrokeFontColorType;
|
||||
|
||||
private ElementTheme _lyricsBgTheme;
|
||||
|
||||
private int _lyricsFontStrokeWidth;
|
||||
|
||||
private int _playingLineIndex = -1;
|
||||
|
||||
private int _startVisibleLineIndex = -1;
|
||||
private int _endVisibleLineIndex = -1;
|
||||
|
||||
private protected Color _fontColor;
|
||||
|
||||
private bool _isDebugOverlayEnabled = false;
|
||||
|
||||
private bool _isDesktopMode = false;
|
||||
|
||||
private bool _isDockMode = false;
|
||||
|
||||
private bool _isFanLyricsEnabled = false;
|
||||
|
||||
private bool _isPlaying = true;
|
||||
|
||||
private protected bool _isRelayoutNeeded = true;
|
||||
private bool _isLayoutChanged = true;
|
||||
|
||||
private int _langIndex = 0;
|
||||
|
||||
private SoftwareBitmap? _lastAlbumArtBitmap = null;
|
||||
|
||||
private Color _lightFontColor = Colors.White;
|
||||
|
||||
private Color _lyricsWindowBgColor = Colors.Transparent;
|
||||
|
||||
private List<List<LyricsLine>> _multiLangLyrics = [];
|
||||
private List<string> _translations = [];
|
||||
private bool _isTranslationEnabled = false;
|
||||
private int _targetLanguageIndex = 6;
|
||||
|
||||
private float _rotateAngle = 0f;
|
||||
|
||||
private int _startVisibleLineIndex = -1;
|
||||
|
||||
private protected CanvasTextFormat _textFormat = new()
|
||||
private CanvasTextFormat _lyricsTextFormat = new()
|
||||
{
|
||||
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
||||
VerticalAlignment = CanvasVerticalAlignment.Top,
|
||||
};
|
||||
private CanvasTextFormat _titleTextFormat = new()
|
||||
{
|
||||
FontSize = 18,
|
||||
FontWeight = FontWeights.Bold,
|
||||
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
||||
WordWrapping = CanvasWordWrapping.NoWrap,
|
||||
TrimmingSign = CanvasTrimmingSign.Ellipsis,
|
||||
TrimmingGranularity = CanvasTextTrimmingGranularity.Character,
|
||||
};
|
||||
private CanvasTextFormat _artistTextFormat = new()
|
||||
{
|
||||
FontSize = 16,
|
||||
FontWeight = FontWeights.Bold,
|
||||
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
||||
WordWrapping = CanvasWordWrapping.NoWrap,
|
||||
TrimmingSign = CanvasTrimmingSign.Ellipsis,
|
||||
TrimmingGranularity = CanvasTextTrimmingGranularity.Character,
|
||||
};
|
||||
|
||||
public LyricsRendererViewModel(
|
||||
ISettingsService settingsService, IPlaybackService playbackService,
|
||||
IMusicSearchService musicSearchService, ILibWatcherService libWatcherService) : base(settingsService)
|
||||
private Task? _refreshLyricsTask;
|
||||
private CancellationTokenSource? _refreshLyricsCts;
|
||||
|
||||
private Task? _showTranslationsTask;
|
||||
private CancellationTokenSource? _showTranslationsCts;
|
||||
|
||||
public LyricsRendererViewModel(ISettingsService settingsService, IPlaybackService playbackService, IMusicSearchService musicSearchService, ILibWatcherService libWatcherService, ILibreTranslateService libreTranslateService) : base(settingsService)
|
||||
{
|
||||
_musicSearchService = musicSearchService;
|
||||
_playbackService = playbackService;
|
||||
_libWatcherService = libWatcherService;
|
||||
_libreTranslateService = libreTranslateService;
|
||||
|
||||
CoverImageRadius = _settingsService.CoverImageRadius;
|
||||
_logger = Ioc.Default.GetRequiredService<ILogger<LyricsRendererViewModel>>();
|
||||
|
||||
_albumArtCornerRadius = _settingsService.CoverImageRadius;
|
||||
IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
|
||||
CoverOverlayOpacity = _settingsService.CoverOverlayOpacity;
|
||||
CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount;
|
||||
|
||||
LyricsFontColorType = _settingsService.LyricsFontColorType;
|
||||
_lyricsBgFontColorType = _settingsService.LyricsBgFontColorType;
|
||||
_lyricsFgFontColorType = _settingsService.LyricsFgFontColorType;
|
||||
|
||||
LyricsFontWeight = _settingsService.LyricsFontWeight;
|
||||
LyricsAlignmentType = _settingsService.LyricsAlignmentType;
|
||||
LyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
|
||||
@@ -139,7 +180,20 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
LyricsBlurAmount = _settingsService.LyricsBlurAmount;
|
||||
IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
|
||||
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
|
||||
_customFontColor = _settingsService.LyricsCustomFontColor;
|
||||
|
||||
_customBgFontColor = _settingsService.LyricsCustomBgFontColor;
|
||||
_customFgFontColor = _settingsService.LyricsCustomFgFontColor;
|
||||
|
||||
_lyricsBgTheme = _settingsService.LyricsBackgroundTheme;
|
||||
|
||||
_isFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
|
||||
|
||||
_lyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
|
||||
|
||||
_isTranslationEnabled = _settingsService.IsTranslationEnabled;
|
||||
_targetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
|
||||
|
||||
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
|
||||
|
||||
_libWatcherService.MusicLibraryFilesChanged +=
|
||||
LibWatcherService_MusicLibraryFilesChanged;
|
||||
@@ -148,34 +202,27 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_playbackService.SongInfoChanged += PlaybackService_SongInfoChanged;
|
||||
_playbackService.PositionChanged += PlaybackService_PositionChanged;
|
||||
|
||||
_isPlaying = _playbackService.IsPlaying;
|
||||
SongInfo = _playbackService.SongInfo;
|
||||
TotalTime = _playbackService.Position;
|
||||
|
||||
UpdateFontColor();
|
||||
}
|
||||
|
||||
public int CoverImageRadius { get; set; }
|
||||
[ObservableProperty]
|
||||
public partial bool IsTranslating { get; set; } = false;
|
||||
|
||||
public int CoverOverlayBlurAmount { get; set; }
|
||||
|
||||
public int CoverOverlayOpacity { get; set; }
|
||||
|
||||
public LyricsDisplayType DisplayType { get; set; }
|
||||
|
||||
public TimeSpan ElapsedTime { get; set; } = TimeSpan.Zero;
|
||||
private LyricsDisplayType _displayTypeReceived = LyricsDisplayType.PlaceholderOnly;
|
||||
private LyricsDisplayType _displayType = LyricsDisplayType.PlaceholderOnly;
|
||||
|
||||
public bool IsDynamicCoverOverlayEnabled { get; set; }
|
||||
|
||||
public bool IsLyricsGlowEffectEnabled { get; set; }
|
||||
|
||||
public LyricsAlignmentType LyricsAlignmentType { get; set; }
|
||||
public TextAlignmentType LyricsAlignmentType { get; set; }
|
||||
|
||||
public int LyricsBlurAmount { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LyricsFontColorType LyricsFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int LyricsFontSize { get; set; }
|
||||
|
||||
@@ -196,8 +243,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ElementTheme ThemeTypeSent { get; set; }
|
||||
|
||||
public TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
|
||||
|
||||
private int GetCurrentPlayingLineIndex()
|
||||
{
|
||||
for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++)
|
||||
@@ -208,8 +253,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
line.StartMs <= TotalTime.TotalMilliseconds
|
||||
&& TotalTime.TotalMilliseconds <= line.EndMs
|
||||
line.StartMs <= _totalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds
|
||||
&& _totalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds <= line.EndMs
|
||||
)
|
||||
{
|
||||
return i;
|
||||
@@ -225,7 +270,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
charLength = 0;
|
||||
charProgress = 0f;
|
||||
|
||||
float now = (float)TotalTime.TotalMilliseconds;
|
||||
float now = (float)_totalTime.TotalMilliseconds + (float)_positionOffset.TotalMilliseconds;
|
||||
|
||||
// 1. 还没到本句
|
||||
if (now < line.StartMs)
|
||||
@@ -270,7 +315,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
charProgress = (now - line.StartMs) / line.DurationMs;
|
||||
charProgress = Math.Clamp(charProgress, 0f, 1f);
|
||||
charStartIndex = 0;
|
||||
charLength = line.Text.Length;
|
||||
charLength = line.OriginalText.Length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,9 +333,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
return new Tuple<int, int>(0, _multiLangLyrics[_langIndex].Count - 1);
|
||||
}
|
||||
|
||||
private async void LibWatcherService_MusicLibraryFilesChanged(object? sender, LibChangedEventArgs e)
|
||||
private void LibWatcherService_MusicLibraryFilesChanged(object? sender, LibChangedEventArgs e)
|
||||
{
|
||||
await RefreshLyricsAsync();
|
||||
_logger.LogInformation("Music library files changed: {ChangeType} {FilePath}, refreshing lyrics...", e.ChangeType, e.FilePath);
|
||||
RefreshLyricsAsync();
|
||||
}
|
||||
|
||||
private void PlaybackService_IsPlayingChanged(object? sender, IsPlayingChangedEventArgs e)
|
||||
@@ -300,39 +346,202 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private void PlaybackService_PositionChanged(object? sender, PositionChangedEventArgs e)
|
||||
{
|
||||
TotalTime = e.Position;
|
||||
if (Math.Abs(_totalTime.TotalMilliseconds - e.Position.TotalMilliseconds) > 300)
|
||||
{
|
||||
_totalTime = e.Position;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaybackService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e)
|
||||
private async void PlaybackService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e)
|
||||
{
|
||||
SongInfo = e.SongInfo;
|
||||
|
||||
if (SongInfo?.AlbumArtSwBitmap != _albumArtSwBitmap)
|
||||
{
|
||||
_lastAlbumArtSwBitmap = _albumArtSwBitmap;
|
||||
_lastAlbumArtCanvasBitmap = null;
|
||||
|
||||
_albumArtSwBitmap = SongInfo?.AlbumArtSwBitmap;
|
||||
_albumArtCanvasBitmap = null;
|
||||
|
||||
_albumArtAccentColor = SongInfo?.AlbumArtAccentColor;
|
||||
|
||||
_albumArtBgTransition.Reset(0f);
|
||||
_albumArtBgTransition.StartTransition(1f);
|
||||
|
||||
UpdateFontColor();
|
||||
}
|
||||
|
||||
if (SongInfo?.Title != _songTitle || SongInfo?.Artist != _songArtist)
|
||||
{
|
||||
_lastSongTitle = _songTitle;
|
||||
_songTitle = SongInfo?.Title;
|
||||
|
||||
_lastSongArtist = _songArtist;
|
||||
_songArtist = SongInfo?.Artist;
|
||||
|
||||
_songInfoOpacityTransition.Reset(0f);
|
||||
_songInfoOpacityTransition.StartTransition(1f);
|
||||
|
||||
_logger.LogInformation("Song info changed: Title={Title}, Artist={Artist}, refreshing lyrics...", _songTitle, _songArtist);
|
||||
await RefreshLyricsAsync();
|
||||
|
||||
_totalTime = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshLyricsAsync()
|
||||
{
|
||||
SetLyricsLoadingPlaceholder();
|
||||
|
||||
string? lyricsRaw = null;
|
||||
LyricsFormat? lyricsFormat = null;
|
||||
|
||||
if (SongInfo != null)
|
||||
// 取消上一次
|
||||
_refreshLyricsCts?.Cancel();
|
||||
if (_refreshLyricsTask != null)
|
||||
{
|
||||
(lyricsRaw, lyricsFormat) = await _musicSearchService.SearchLyricsAsync(
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist,
|
||||
SongInfo.Album ?? "",
|
||||
SongInfo.DurationMs ?? 0
|
||||
);
|
||||
await _refreshLyricsTask;
|
||||
}
|
||||
|
||||
_multiLangLyrics = new LyricsParser().Parse(
|
||||
lyricsRaw,
|
||||
lyricsFormat,
|
||||
SongInfo?.Title,
|
||||
SongInfo?.Artist,
|
||||
(int)(SongInfo?.DurationMs ?? 0)
|
||||
);
|
||||
_isRelayoutNeeded = true;
|
||||
var cts = new CancellationTokenSource();
|
||||
_refreshLyricsCts = cts;
|
||||
var token = cts.Token;
|
||||
|
||||
_refreshLyricsTask = RefreshLyricsCoreAsync(token);
|
||||
await _refreshLyricsTask;
|
||||
}
|
||||
|
||||
private async Task UpdateTranslationsAsync()
|
||||
{
|
||||
IsTranslating = true;
|
||||
if (_isTranslationEnabled)
|
||||
{
|
||||
await ShowWithTranslationsAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowOriginalsOnly();
|
||||
}
|
||||
IsTranslating = false;
|
||||
}
|
||||
|
||||
private async Task ShowWithTranslationsAsync()
|
||||
{
|
||||
_showTranslationsCts?.Cancel();
|
||||
if (_showTranslationsTask != null)
|
||||
{
|
||||
await _showTranslationsTask;
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
_showTranslationsCts = cts;
|
||||
var token = cts.Token;
|
||||
|
||||
_showTranslationsTask = ShowTranslationsCoreAsync(token);
|
||||
await _showTranslationsTask;
|
||||
}
|
||||
|
||||
private async Task ShowTranslationsCoreAsync(CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Showing translations for lyrics...");
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(_settingsService.LibreTranslateServer))
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
App.Current.LyricsWindowNotificationPanel?.Notify(
|
||||
App.ResourceLoader!.GetString("TranslateServerNotSet"),
|
||||
Microsoft.UI.Xaml.Controls.InfoBarSeverity.Warning
|
||||
);
|
||||
});
|
||||
ShowOriginalsOnly();
|
||||
return;
|
||||
}
|
||||
var text = string.Join("\n", _multiLangLyrics.FirstOrDefault()?.Select(x => x.OriginalText) ?? []);
|
||||
var translated = await _libreTranslateService.TranslateAsync(text, token);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
_translations = translated.Split('\n').ToList();
|
||||
bool totallySame = true;
|
||||
foreach (var langLyrics in _multiLangLyrics)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var line in langLyrics)
|
||||
{
|
||||
if (line.OriginalText != _translations[i])
|
||||
{
|
||||
totallySame = false;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var langLyrics in _multiLangLyrics)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var line in langLyrics)
|
||||
{
|
||||
line.DisplayedText = totallySame ? line.OriginalText : $"{line.OriginalText}\n{_translations[i]}";
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
IsTranslating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowOriginalsOnly()
|
||||
{
|
||||
_logger.LogInformation("Showing original lyrics only, translations disabled.");
|
||||
foreach (var langLyrics in _multiLangLyrics)
|
||||
{
|
||||
foreach (var line in langLyrics)
|
||||
{
|
||||
line.DisplayedText = line.OriginalText;
|
||||
}
|
||||
}
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
|
||||
private async Task RefreshLyricsCoreAsync(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Refreshing lyrics...");
|
||||
|
||||
SetLyricsLoadingPlaceholder();
|
||||
|
||||
string? lyricsRaw = null;
|
||||
|
||||
if (SongInfo != null)
|
||||
{
|
||||
lyricsRaw = await _musicSearchService.SearchLyricsAsync(
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist,
|
||||
SongInfo.Album ?? "",
|
||||
SongInfo.DurationMs ?? 0,
|
||||
token
|
||||
);
|
||||
_logger.LogInformation("Lyrics search result: {LyricsRaw}", lyricsRaw ?? "null");
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("SongInfo is null, cannot search lyrics.");
|
||||
}
|
||||
|
||||
_multiLangLyrics = new LyricsParser().Parse(
|
||||
lyricsRaw,
|
||||
(int?)SongInfo?.DurationMs ?? (int)TimeSpan.FromMinutes(99).TotalMilliseconds
|
||||
);
|
||||
_logger.LogInformation("Parsed lyrics: {MultiLangLyricsCount} languages", _multiLangLyrics.Count);
|
||||
await UpdateTranslationsAsync();
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
private void SetLyricsLoadingPlaceholder()
|
||||
@@ -344,12 +553,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||
Text = App.ResourceLoader!.GetString("LyricsLoading"),
|
||||
OriginalText = "● ● ●",
|
||||
DisplayedText = "● ● ●",
|
||||
CharTimings = [],
|
||||
},
|
||||
]
|
||||
);
|
||||
_isRelayoutNeeded = true;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterInAppLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class LyricsSettingsControlViewModel : BaseViewModel
|
||||
{
|
||||
public LyricsSettingsControlViewModel(ISettingsService settingsService)
|
||||
: base(settingsService)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
LyricsAlignmentType = _settingsService.LyricsAlignmentType;
|
||||
LyricsFontWeight = _settingsService.LyricsFontWeight;
|
||||
LyricsBlurAmount = _settingsService.LyricsBlurAmount;
|
||||
LyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
|
||||
LyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor;
|
||||
LyricsFontSize = _settingsService.LyricsFontSize;
|
||||
IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
|
||||
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
|
||||
IsFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
|
||||
LyricsFontColorType = _settingsService.LyricsFontColorType;
|
||||
LyricsCustomFontColor = _settingsService.LyricsCustomFontColor;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsFanLyricsEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLyricsGlowEffectEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsAlignmentType LyricsAlignmentType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsBlurAmount { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsFontSize { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontWeight LyricsFontWeight { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LineRenderingType LyricsGlowEffectScope { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial float LyricsLineSpacingFactor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsVerticalEdgeOpacity { get; set; }
|
||||
|
||||
partial void OnIsFanLyricsEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsFanLyricsEnabled = value;
|
||||
}
|
||||
|
||||
partial void OnIsLyricsGlowEffectEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsLyricsGlowEffectEnabled = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsAlignmentTypeChanged(LyricsAlignmentType value)
|
||||
{
|
||||
_settingsService.LyricsAlignmentType = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsBlurAmountChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsBlurAmount = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsCustomFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomFontColor = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsFontColorType = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsFontSizeChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsFontSize = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsFontWeightChanged(LyricsFontWeight value)
|
||||
{
|
||||
_settingsService.LyricsFontWeight = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsGlowEffectScopeChanged(LineRenderingType value)
|
||||
{
|
||||
_settingsService.LyricsGlowEffectScope = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsLineSpacingFactorChanged(float value)
|
||||
{
|
||||
_settingsService.LyricsLineSpacingFactor = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsVerticalEdgeOpacityChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsVerticalEdgeOpacity = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using BetterInAppLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Messages;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
@@ -13,6 +11,8 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.UI;
|
||||
using WinRT.Interop;
|
||||
@@ -27,23 +27,9 @@ namespace BetterLyrics.WinUI3
|
||||
{
|
||||
private ForegroundWindowWatcherHelper? _watcherHelper = null;
|
||||
|
||||
public LyricsWindowViewModel(ISettingsService settingsService)
|
||||
: base(settingsService)
|
||||
public LyricsWindowViewModel(ISettingsService settingsService) : base(settingsService)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Register<ShowNotificatonMessage>(
|
||||
this,
|
||||
async (r, m) =>
|
||||
{
|
||||
Notification = m.Value;
|
||||
if (!Notification.IsForeverDismissable)
|
||||
{
|
||||
Notification.Visibility = Notification.IsForeverDismissable ? Visibility.Visible : Visibility.Collapsed;
|
||||
ShowInfoBar = true;
|
||||
await Task.Delay(AnimationHelper.StackedNotificationsShowingDuration);
|
||||
ShowInfoBar = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
_ignoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -77,6 +63,8 @@ namespace BetterLyrics.WinUI3
|
||||
[ObservableProperty]
|
||||
public partial double TitleBarHeight { get; set; } = 36;
|
||||
|
||||
private bool _ignoreFullscreenWindow = false;
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
{
|
||||
if (message.Sender is SystemTrayViewModel)
|
||||
@@ -89,6 +77,13 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.IgnoreFullscreenWindow))
|
||||
{
|
||||
_ignoreFullscreenWindow = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<ElementTheme> message)
|
||||
@@ -104,17 +99,16 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
public void Receive(PropertyChangedMessage<int> message)
|
||||
{
|
||||
if (message.Sender is LyricsSettingsControlViewModel)
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontSize))
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize))
|
||||
{
|
||||
if (IsDockMode)
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
DockModeHelper.UpdateAppBarHeight(
|
||||
WindowNative.GetWindowHandle(window),
|
||||
message.NewValue * 3
|
||||
);
|
||||
if (window == null) return;
|
||||
|
||||
DockModeHelper.UpdateAppBarHeight(WindowNative.GetWindowHandle(window), message.NewValue * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,13 +116,18 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
public void StartWatchWindowColorChange(WindowColorSampleMode mode)
|
||||
{
|
||||
var hwnd = WindowNative.GetWindowHandle(
|
||||
WindowHelper.GetWindowByWindowType<LyricsWindow>()
|
||||
);
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(window);
|
||||
_watcherHelper = new ForegroundWindowWatcherHelper(
|
||||
hwnd,
|
||||
onWindowChanged =>
|
||||
{
|
||||
if (_ignoreFullscreenWindow && window.AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.IsAlwaysOnTop = true;
|
||||
}
|
||||
UpdateAccentColor(hwnd, mode);
|
||||
}
|
||||
);
|
||||
@@ -145,6 +144,8 @@ namespace BetterLyrics.WinUI3
|
||||
private void LockWindow()
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
DesktopModeHelper.Lock(window);
|
||||
IsLyricsWindowLocked = true;
|
||||
}
|
||||
@@ -159,6 +160,8 @@ namespace BetterLyrics.WinUI3
|
||||
private void ToggleDesktopMode()
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
StopWatchWindowColorChange();
|
||||
|
||||
IsDesktopMode = !IsDesktopMode;
|
||||
@@ -170,7 +173,6 @@ namespace BetterLyrics.WinUI3
|
||||
else
|
||||
{
|
||||
DesktopModeHelper.Disable(window);
|
||||
StopWatchWindowColorChange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,17 +180,18 @@ namespace BetterLyrics.WinUI3
|
||||
private void ToggleDockMode()
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
StopWatchWindowColorChange();
|
||||
|
||||
IsDockMode = !IsDockMode;
|
||||
if (IsDockMode)
|
||||
{
|
||||
StartWatchWindowColorChange(WindowColorSampleMode.BelowWindow);
|
||||
DockModeHelper.Enable(window, _settingsService.LyricsFontSize * 3);
|
||||
DockModeHelper.Enable(window, _settingsService.LyricsFontSize * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartWatchWindowColorChange(WindowColorSampleMode.WindowEdge);
|
||||
DockModeHelper.Disable(window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Messages;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
@@ -16,22 +9,42 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml;
|
||||
using ShadowViewer.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Globalization;
|
||||
using Windows.Media.Playback;
|
||||
using Windows.System;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Popups;
|
||||
using WinRT.Interop;
|
||||
using AppInfo = BetterLyrics.WinUI3.Helper.AppInfo;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class SettingsPageViewModel : ObservableRecipient
|
||||
public partial class SettingsPageViewModel : BaseViewModel
|
||||
{
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IPlaybackService _playbackService;
|
||||
private readonly ILibreTranslateService _libreTranslateService;
|
||||
|
||||
public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService)
|
||||
private readonly string _autoStartupTaskId = "AutoStartup";
|
||||
|
||||
public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IPlaybackService playbackService, ILibreTranslateService libreTranslateService) : base(settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_libWatcherService = libWatcherService;
|
||||
_playbackService = playbackService;
|
||||
_libreTranslateService = libreTranslateService;
|
||||
|
||||
LibreTranslateServer = _settingsService.LibreTranslateServer;
|
||||
SelectedTargetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
|
||||
|
||||
LocalLyricsFolders = [.. _settingsService.LocalLyricsFolders];
|
||||
LyricsSearchProvidersInfo = [.. _settingsService.LyricsSearchProvidersInfo];
|
||||
@@ -46,12 +59,50 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
CoverOverlayOpacity = _settingsService.CoverOverlayOpacity;
|
||||
CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount;
|
||||
|
||||
LyricsAlignmentType = _settingsService.LyricsAlignmentType;
|
||||
SongInfoAlignmentType = _settingsService.SongInfoAlignmentType;
|
||||
LyricsFontWeight = _settingsService.LyricsFontWeight;
|
||||
LyricsBlurAmount = _settingsService.LyricsBlurAmount;
|
||||
LyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
|
||||
LyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor;
|
||||
LyricsFontSize = _settingsService.LyricsFontSize;
|
||||
IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
|
||||
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
|
||||
IsFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
|
||||
|
||||
LyricsBgFontColorType = _settingsService.LyricsBgFontColorType;
|
||||
LyricsFgFontColorType = _settingsService.LyricsFgFontColorType;
|
||||
LyricsStrokeFontColorType = _settingsService.LyricsStrokeFontColorType;
|
||||
|
||||
LyricsCustomBgFontColor = _settingsService.LyricsCustomBgFontColor;
|
||||
LyricsCustomFgFontColor = _settingsService.LyricsCustomFgFontColor;
|
||||
LyricsCustomStrokeFontColor = _settingsService.LyricsCustomStrokeFontColor;
|
||||
|
||||
LyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
|
||||
|
||||
LyricsBackgroundTheme = _settingsService.LyricsBackgroundTheme;
|
||||
|
||||
MediaSourceProvidersInfo = [.. _settingsService.MediaSourceProvidersInfo];
|
||||
|
||||
IgnoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow;
|
||||
|
||||
_playbackService.MediaSourceProvidersInfoChanged += PlaybackService_SessionIdsChanged;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
BuildDate = (await AppInfo.GetBuildDate()).ToString("(yyyy/MM/dd HH:mm:ss)");
|
||||
BuildDate = (await Helper.AppInfo.GetBuildDate()).ToString("(yyyy/MM/dd HH:mm:ss)");
|
||||
});
|
||||
}
|
||||
|
||||
private void PlaybackService_SessionIdsChanged(object? sender, Events.MediaSourceProvidersInfoEventArgs e)
|
||||
{
|
||||
MediaSourceProvidersInfo = [.. e.MediaSourceProviersInfo];
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ElementTheme LyricsBackgroundTheme { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial AutoStartWindowType AutoStartWindowType { get; set; }
|
||||
|
||||
@@ -74,6 +125,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsDebugOverlayEnabled { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLogEnabled { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsDynamicCoverOverlayEnabled { get; set; }
|
||||
@@ -88,12 +143,107 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ObservableCollection<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsFanLyricsEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLyricsGlowEffectEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial TextAlignmentType LyricsAlignmentType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial TextAlignmentType SongInfoAlignmentType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsBlurAmount { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomBgFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomFgFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomStrokeFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsBgFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsFgFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsStrokeFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsFontSize { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontWeight LyricsFontWeight { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LineRenderingType LyricsGlowEffectScope { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial float LyricsLineSpacingFactor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsVerticalEdgeOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial object NavViewSelectedItemTag { get; set; }
|
||||
|
||||
public string Version { get; set; } = Helper.AppInfo.AppVersion;
|
||||
|
||||
public string BuildDate { get; set; }
|
||||
public string BuildDate { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string LibreTranslateServer { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int SelectedTargetLanguageIndex { get; set; } = 0;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsLibreTranslateServerTesting { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsFontStrokeWidth { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IgnoreFullscreenWindow { get; set; }
|
||||
|
||||
partial void OnLyricsBackgroundThemeChanged(ElementTheme value)
|
||||
{
|
||||
_settingsService.LyricsBackgroundTheme = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsFontStrokeWidthChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsFontStrokeWidth = value;
|
||||
}
|
||||
|
||||
public void OnLyricsSearchProvidersReordered()
|
||||
{
|
||||
@@ -134,66 +284,33 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
);
|
||||
}
|
||||
|
||||
public void ToggleMediaSourceProvider(MediaSourceProviderInfo providerInfo)
|
||||
{
|
||||
Broadcast(
|
||||
MediaSourceProvidersInfo,
|
||||
MediaSourceProvidersInfo,
|
||||
nameof(MediaSourceProvidersInfo)
|
||||
);
|
||||
}
|
||||
|
||||
private void AddFolderAsync(string path)
|
||||
{
|
||||
var normalizedPath =
|
||||
Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar)
|
||||
+ Path.DirectorySeparatorChar;
|
||||
var normalizedPath = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
||||
|
||||
if (
|
||||
LocalLyricsFolders.Any(x =>
|
||||
Path.GetFullPath(x.Path)
|
||||
.TrimEnd(Path.DirectorySeparatorChar)
|
||||
.Equals(
|
||||
normalizedPath.TrimEnd(Path.DirectorySeparatorChar),
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
)
|
||||
)
|
||||
)
|
||||
if (LocalLyricsFolders.Any(x => Path.GetFullPath(x.Path).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(
|
||||
new ShowNotificatonMessage(
|
||||
new Notification(
|
||||
App.ResourceLoader!.GetString("SettingsPagePathExistedInfo")
|
||||
)
|
||||
)
|
||||
);
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathExistedInfo"));
|
||||
}
|
||||
else if (
|
||||
LocalLyricsFolders.Any(item =>
|
||||
normalizedPath.StartsWith(
|
||||
Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar)
|
||||
+ Path.DirectorySeparatorChar,
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
)
|
||||
)
|
||||
)
|
||||
else if (LocalLyricsFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的子文件夹
|
||||
WeakReferenceMessenger.Default.Send(
|
||||
new ShowNotificatonMessage(
|
||||
new Notification(
|
||||
App.ResourceLoader!.GetString("SettingsPagePathBeIncludedInfo")
|
||||
)
|
||||
)
|
||||
);
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathBeIncludedInfo"));
|
||||
}
|
||||
else if (
|
||||
LocalLyricsFolders.Any(item =>
|
||||
Path.GetFullPath(item.Path)
|
||||
.TrimEnd(Path.DirectorySeparatorChar)
|
||||
.StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
else if (LocalLyricsFolders.Any(item => Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase))
|
||||
)
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的父文件夹
|
||||
WeakReferenceMessenger.Default.Send(
|
||||
new ShowNotificatonMessage(
|
||||
new Notification(
|
||||
App.ResourceLoader!.GetString("SettingsPagePathIncludingOthersInfo")
|
||||
)
|
||||
)
|
||||
);
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathIncludingOthersInfo"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -243,11 +360,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[RelayCommand]
|
||||
private async Task SelectAndAddFolderAsync(UIElement sender)
|
||||
{
|
||||
var picker = new Windows.Storage.Pickers.FolderPicker();
|
||||
var window = WindowHelper.GetWindowByWindowType<SettingsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
var picker = new Windows.Storage.Pickers.FolderPicker();
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(WindowHelper.GetWindowByWindowType<SettingsWindow>());
|
||||
var hwnd = WindowNative.GetWindowHandle(window);
|
||||
InitializeWithWindow.Initialize(picker, hwnd);
|
||||
|
||||
var folder = await picker.PickSingleFolderAsync();
|
||||
@@ -258,6 +377,79 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void LibreTranslateServerTest()
|
||||
{
|
||||
IsLibreTranslateServerTesting = true;
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string result = await _libreTranslateService.TranslateAsync("Hello, world!", null);
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageLibreTranslateTestSuccessInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Success);
|
||||
IsLibreTranslateServerTesting = false;
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageLibreTranslateTestFailedInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
|
||||
IsLibreTranslateServerTesting = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<bool> ToggleAutoStartupAsync(bool target)
|
||||
{
|
||||
StartupTask startupTask = await StartupTask.GetAsync(_autoStartupTaskId);
|
||||
if (target)
|
||||
{
|
||||
await startupTask.RequestEnableAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
startupTask.Disable();
|
||||
}
|
||||
return await DetectIsAutoStartupEnabledAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> DetectIsAutoStartupEnabledAsync()
|
||||
{
|
||||
bool result = false;
|
||||
var startupTask = await StartupTask.GetAsync(_autoStartupTaskId);
|
||||
switch (startupTask.State)
|
||||
{
|
||||
case StartupTaskState.Disabled:
|
||||
case StartupTaskState.DisabledByUser:
|
||||
case StartupTaskState.DisabledByPolicy:
|
||||
result = false;
|
||||
break;
|
||||
case StartupTaskState.Enabled:
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
partial void OnIgnoreFullscreenWindowChanged(bool value)
|
||||
{
|
||||
_settingsService.IgnoreFullscreenWindow = value;
|
||||
}
|
||||
|
||||
partial void OnSelectedTargetLanguageIndexChanged(int value)
|
||||
{
|
||||
_settingsService.SelectedTargetLanguageIndex = value;
|
||||
}
|
||||
|
||||
partial void OnLibreTranslateServerChanged(string value)
|
||||
{
|
||||
_settingsService.LibreTranslateServer = value;
|
||||
}
|
||||
|
||||
partial void OnAutoStartWindowTypeChanged(AutoStartWindowType value)
|
||||
{
|
||||
_settingsService.AutoStartWindowType = value;
|
||||
@@ -315,5 +507,85 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
_settingsService.Language = Language;
|
||||
}
|
||||
|
||||
partial void OnIsFanLyricsEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsFanLyricsEnabled = value;
|
||||
}
|
||||
|
||||
partial void OnIsLyricsGlowEffectEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsLyricsGlowEffectEnabled = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsAlignmentTypeChanged(TextAlignmentType value)
|
||||
{
|
||||
_settingsService.LyricsAlignmentType = value;
|
||||
}
|
||||
|
||||
partial void OnSongInfoAlignmentTypeChanged(TextAlignmentType value)
|
||||
{
|
||||
_settingsService.SongInfoAlignmentType = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsBlurAmountChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsBlurAmount = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsCustomBgFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomBgFontColor = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsCustomFgFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomFgFontColor = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsCustomStrokeFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomStrokeFontColor = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsBgFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsBgFontColorType = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsFgFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsFgFontColorType = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsStrokeFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsStrokeFontColorType = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsFontSizeChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsFontSize = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsFontWeightChanged(LyricsFontWeight value)
|
||||
{
|
||||
_settingsService.LyricsFontWeight = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsGlowEffectScopeChanged(LineRenderingType value)
|
||||
{
|
||||
_settingsService.LyricsGlowEffectScope = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsLineSpacingFactorChanged(float value)
|
||||
{
|
||||
_settingsService.LyricsLineSpacingFactor = value;
|
||||
}
|
||||
|
||||
partial void OnLyricsVerticalEdgeOpacityChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsVerticalEdgeOpacity = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private void UnlockWindow()
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
DesktopModeHelper.Unlock(window);
|
||||
IsLyricsWindowLocked = false;
|
||||
}
|
||||
|
||||
@@ -20,279 +20,127 @@
|
||||
|
||||
<Grid x:Name="RootGrid">
|
||||
<!-- Lyrics area -->
|
||||
<Grid x:Name="LyricsGrid">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<renderer:LyricsRenderer />
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="36,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<!-- Lyrics placeholder -->
|
||||
<Grid
|
||||
x:Name="LyricsPlaceholderGrid"
|
||||
Opacity=".5"
|
||||
SizeChanged="LyricsPlaceholderGrid_SizeChanged">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Song info area -->
|
||||
<Grid x:Name="SongInfoInnerGrid" Margin="0,36">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="2*" />
|
||||
<!-- Cover area -->
|
||||
<RowDefinition Height="9*" />
|
||||
<!-- Spacer -->
|
||||
<RowDefinition Height="*" />
|
||||
<!-- Title and artist area -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="2*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<!-- Cover area -->
|
||||
<Grid
|
||||
x:Name="CoverArea"
|
||||
Grid.Row="1"
|
||||
SizeChanged="CoverArea_SizeChanged">
|
||||
|
||||
<Grid x:Name="CoverImageGrid" SizeChanged="CoverImageGrid_SizeChanged">
|
||||
<Grid CornerRadius="{x:Bind ViewModel.CoverImageGridCornerRadius, Mode=OneWay}">
|
||||
<Image
|
||||
x:Name="CoverImage"
|
||||
Source="{x:Bind ViewModel.CoverImage, Mode=OneWay}"
|
||||
Stretch="Uniform">
|
||||
<Image.Resources>
|
||||
<Storyboard x:Key="CoverIamgeFadeInStoryboard">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CoverImage" Storyboard.TargetProperty="Opacity">
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="0" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<Storyboard x:Key="CoverIamgeFadeOutStoryboard">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CoverImage" Storyboard.TargetProperty="Opacity">
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="1" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</Image.Resources>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.AboutToUpdateUI, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource CoverIamgeFadeOutStoryboard}" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.AboutToUpdateUI, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource CoverIamgeFadeInStoryboard}" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Image>
|
||||
</Grid>
|
||||
<ui:Effects.Shadow>
|
||||
<media:AttachedCardShadow
|
||||
BlurRadius="32"
|
||||
CornerRadius="{x:Bind ViewModel.CoverImageGridCornerRadius, Mode=OneWay, Converter={StaticResource CornerRadiusToDoubleConverter}}"
|
||||
InnerContentClipMode="CompositionMaskBrush"
|
||||
Opacity="0.1" />
|
||||
</ui:Effects.Shadow>
|
||||
</Grid>
|
||||
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Title and artist -->
|
||||
<StackPanel
|
||||
Grid.Row="3"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Vertical">
|
||||
<!-- Song title -->
|
||||
<controls:OpacityMaskView x:Name="TitleOpacityMaskView" HorizontalAlignment="Center">
|
||||
<controls:OpacityMaskView.OpacityMask>
|
||||
<Rectangle Fill="{StaticResource BaseHighEdgeHorizontalFadeBrush}" />
|
||||
</controls:OpacityMaskView.OpacityMask>
|
||||
|
||||
<controls:OpacityMaskView.Resources>
|
||||
<Storyboard x:Key="TitleOpacityMaskViewFadeInStoryboard">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="TitleOpacityMaskView" Storyboard.TargetProperty="Opacity">
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="0" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<Storyboard x:Key="TitleOpacityMaskViewFadeOutStoryboard">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="TitleOpacityMaskView" Storyboard.TargetProperty="Opacity">
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="1" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</controls:OpacityMaskView.Resources>
|
||||
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.AboutToUpdateUI, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource TitleOpacityMaskViewFadeOutStoryboard}" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.AboutToUpdateUI, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource TitleOpacityMaskViewFadeInStoryboard}" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
|
||||
<labs:MarqueeText
|
||||
x:Name="TitleTextBlock"
|
||||
Behavior="Bouncing"
|
||||
FontSize="{StaticResource TitleTextBlockFontSize}"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
Text="{x:Bind ViewModel.SongInfo.Title, Mode=OneWay}" />
|
||||
</controls:OpacityMaskView>
|
||||
|
||||
<!-- Song artist -->
|
||||
<controls:OpacityMaskView x:Name="ArtistOpacityMaskView" HorizontalAlignment="Center">
|
||||
<controls:OpacityMaskView.OpacityMask>
|
||||
<Rectangle Fill="{StaticResource BaseHighEdgeHorizontalFadeBrush}" />
|
||||
</controls:OpacityMaskView.OpacityMask>
|
||||
<controls:OpacityMaskView.Resources>
|
||||
<Storyboard x:Key="ArtistOpacityMaskViewFadeInStoryboard">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ArtistOpacityMaskView" Storyboard.TargetProperty="Opacity">
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="0" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<Storyboard x:Key="ArtistOpacityMaskViewFadeOutStoryboard">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ArtistOpacityMaskView" Storyboard.TargetProperty="Opacity">
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="1" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</controls:OpacityMaskView.Resources>
|
||||
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.AboutToUpdateUI, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource ArtistOpacityMaskViewFadeOutStoryboard}" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.AboutToUpdateUI, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource ArtistOpacityMaskViewFadeInStoryboard}" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
|
||||
<labs:MarqueeText
|
||||
Behavior="Bouncing"
|
||||
FontSize="{StaticResource SubtitleTextBlockFontSize}"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
Opacity="0.5"
|
||||
Text="{x:Bind ViewModel.SongInfo.Artist, Mode=OneWay}" />
|
||||
</controls:OpacityMaskView>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
<renderer:LyricsRenderer />
|
||||
|
||||
<!-- No music playing placeholder -->
|
||||
<TextBlock
|
||||
x:Name="MainPageNoMusicPlayingTextBlock"
|
||||
x:Uid="MainPageNoMusicPlaying"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource TitleTextBlockStyle}">
|
||||
<TextBlock.OpacityTransition>
|
||||
<Grid x:Name="NoMusicPlayingGrid" Background="{ThemeResource SolidBackgroundFillColorBaseBrush}">
|
||||
<TextBlock
|
||||
x:Uid="MainPageNoMusicPlaying"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</TextBlock.OpacityTransition>
|
||||
</TextBlock>
|
||||
</Grid.OpacityTransition>
|
||||
</Grid>
|
||||
|
||||
<!-- Bottom-right command area -->
|
||||
<!-- Bottom command area -->
|
||||
<Grid
|
||||
x:Name="BottomCommandGrid"
|
||||
Margin="0,0,4,4"
|
||||
HorizontalAlignment="Right"
|
||||
Padding="12"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="Transparent"
|
||||
Opacity="0"
|
||||
PointerEntered="BottomCommandGrid_PointerEntered"
|
||||
PointerExited="BottomCommandGrid_PointerExited"
|
||||
Visibility="{x:Bind ViewModel.IsNotDockMode, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||
PointerExited="BottomCommandGrid_PointerExited">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<StackPanel
|
||||
x:Name="BottomLeftCommandStackPanel"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
</StackPanel>
|
||||
|
||||
<Button Style="{StaticResource GhostButtonStyle}" Visibility="Collapsed">
|
||||
<Grid>
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontWeight="Bold"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
FontWeight="Bold"
|
||||
Text="0.1" />
|
||||
</Grid>
|
||||
<StackPanel
|
||||
x:Name="BottomCenterCommandStackPanel"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
x:Name="BottomRightCommandStackPanel"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
|
||||
<!-- Position offset -->
|
||||
<Button Style="{StaticResource GhostButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph=""
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<FontIcon.RenderTransform>
|
||||
<RotateTransform Angle="90" CenterX="0.5" CenterY="0.5" />
|
||||
</FontIcon.RenderTransform>
|
||||
</FontIcon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TimelineOffsetToolTip" x:Uid="LyricsPageTimelineOffsetButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<StackPanel>
|
||||
<Slider
|
||||
x:Uid="MainPagePositionOffsetSlider"
|
||||
Maximum="2000"
|
||||
Minimum="-2000"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.PositionOffset, Mode=TwoWay}" />
|
||||
<RelativePanel>
|
||||
<TextBlock
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Text="{x:Bind ViewModel.PositionOffset, Mode=OneWay}" />
|
||||
<Button
|
||||
Click="PositionOffsetResetButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</RelativePanel>
|
||||
<TextBlock Opacity="0.5" x:Uid="LyricsPagePositionOffsetHint"/>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
|
||||
<Button Style="{StaticResource GhostButtonStyle}" Visibility="Collapsed">
|
||||
<Grid>
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontWeight="Bold"
|
||||
Glyph=""
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<FontIcon.RenderTransform>
|
||||
<ScaleTransform ScaleX="-1" />
|
||||
</FontIcon.RenderTransform>
|
||||
</FontIcon>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
FontWeight="Bold"
|
||||
Text="0.1" />
|
||||
</Grid>
|
||||
</Button>
|
||||
<!-- Translation -->
|
||||
<ToggleButton
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsChecked="{x:Bind ViewModel.IsTranslationEnabled, Mode=TwoWay}"
|
||||
Style="{StaticResource GhostToggleButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TranslationToolTip" x:Uid="LyricsPageTranslationButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</ToggleButton>
|
||||
|
||||
<!-- Display type -->
|
||||
<Button
|
||||
x:Name="DisplayTypeSwitchButton"
|
||||
x:Uid="MainPageDisplayTypeSwitcher"
|
||||
Content="{ui:FontIcon FontWeight=Bold,
|
||||
FontFamily={StaticResource IconFontFamily},
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<Button.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Button.OpacityTransition>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="PresentationTypeToolTip" x:Uid="LyricsPageDisplayTypeButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
@@ -310,39 +158,17 @@
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
x:Name="MusicInfoButton"
|
||||
Content="{ui:FontIcon FontWeight=Bold,
|
||||
FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<StackPanel Spacing="16">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||
<TextBlock Text="{x:Bind ViewModel.SongInfo.SourceAppUserModelId, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||
<TextBlock Text="{x:Bind ViewModel.SongInfo.Title, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||
<TextBlock Text="{x:Bind ViewModel.SongInfo.Artist, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
|
||||
<!-- Settings -->
|
||||
<Button
|
||||
x:Name="SettingsButton"
|
||||
Command="{x:Bind ViewModel.OpenSettingsWindowCommand}"
|
||||
Content="{ui:FontIcon FontWeight=Bold,
|
||||
FontFamily={StaticResource IconFontFamily},
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="SettingsToolTip" x:Uid="LyricsPageSettingsButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
@@ -353,7 +179,7 @@
|
||||
x:Uid="MainPageWelcomeTeachingTip"
|
||||
Closed="WelcomeTeachingTip_Closed"
|
||||
IsOpen="{x:Bind ViewModel.IsWelcomeTeachingTipOpen, Mode=OneWay}"
|
||||
Target="{x:Bind SettingsButton}" />
|
||||
Target="{x:Bind RootGrid}" />
|
||||
|
||||
<uc:SystemTray />
|
||||
|
||||
@@ -361,23 +187,23 @@
|
||||
<VisualStateGroup x:Name="MusicPlayingStates">
|
||||
<VisualState x:Name="MusicPlaying">
|
||||
<VisualState.StateTriggers>
|
||||
<ui:IsNotEqualStateTrigger Value="{x:Bind ViewModel.DisplayType, Mode=OneWay}" To="3" />
|
||||
<ui:IsNotEqualStateTrigger Value="{x:Bind ViewModel.DisplayType, Converter={StaticResource EnumToIntConverter}, Mode=OneWay}" To="3" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="DisplayTypeSwitchButton.Visibility" Value="Visible" />
|
||||
<Setter Target="MusicInfoButton.Visibility" Value="Visible" />
|
||||
<Setter Target="NoMusicPlayingGrid.Opacity" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="NoMusicPlaying">
|
||||
<VisualState.StateTriggers>
|
||||
<ui:CompareStateTrigger
|
||||
Comparison="Equal"
|
||||
Value="{x:Bind ViewModel.DisplayType, Mode=OneWay}"
|
||||
Value="{x:Bind ViewModel.DisplayType, Converter={StaticResource EnumToIntConverter}, Mode=OneWay}"
|
||||
To="3" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="DisplayTypeSwitchButton.Visibility" Value="Collapsed" />
|
||||
<Setter Target="MusicInfoButton.Visibility" Value="Collapsed" />
|
||||
<Setter Target="NoMusicPlayingGrid.Opacity" Value="1" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
@@ -3,14 +3,10 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using WinUIEx.Messaging;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
@@ -21,153 +17,43 @@ namespace BetterLyrics.WinUI3.Views
|
||||
this.InitializeComponent();
|
||||
|
||||
DataContext = Ioc.Default.GetService<LyricsPageViewModel>();
|
||||
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsDisplayType>>(
|
||||
this,
|
||||
async (r, m) =>
|
||||
{
|
||||
if (m.Sender is LyricsPageViewModel)
|
||||
{
|
||||
if (m.PropertyName == nameof(LyricsPageViewModel.DisplayType))
|
||||
{
|
||||
switch (m.NewValue)
|
||||
{
|
||||
case LyricsDisplayType.AlbumArtOnly:
|
||||
await SwitchToAlbumArtOnlyDisplayTypeAsync();
|
||||
break;
|
||||
case LyricsDisplayType.LyricsOnly:
|
||||
await SwitchToLyricsOnlyDisplayTypeAsync();
|
||||
break;
|
||||
case LyricsDisplayType.SplitView:
|
||||
await SwitchToSplitViewDisplayTypeAsync();
|
||||
break;
|
||||
case LyricsDisplayType.PlaceholderOnly:
|
||||
await SwitchToPlaceholderOnlyDisplayTypeAsync();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext;
|
||||
|
||||
private void BottomCommandGrid_PointerEntered(
|
||||
object sender,
|
||||
Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e
|
||||
)
|
||||
{
|
||||
if (BottomCommandGrid.Opacity == 0)
|
||||
BottomCommandGrid.Opacity = .5;
|
||||
}
|
||||
|
||||
private void BottomCommandGrid_PointerExited(
|
||||
object sender,
|
||||
Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e
|
||||
)
|
||||
{
|
||||
if (BottomCommandGrid.Opacity == .5)
|
||||
BottomCommandGrid.Opacity = 0;
|
||||
}
|
||||
|
||||
private void CoverArea_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
CoverImageGrid.Width = CoverImageGrid.Height = Math.Min(
|
||||
CoverArea.ActualWidth,
|
||||
CoverArea.ActualHeight
|
||||
);
|
||||
}
|
||||
|
||||
private void CoverImageGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ViewModel.CoverImageGridActualHeight = e.NewSize.Height;
|
||||
}
|
||||
|
||||
private void LyricsPlaceholderGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ViewModel.MaxLyricsWidth = e.NewSize.Width;
|
||||
}
|
||||
|
||||
private void WelcomeTeachingTip_Closed(TeachingTip sender, TeachingTipClosedEventArgs args)
|
||||
{
|
||||
ViewModel.IsFirstRun = false;
|
||||
}
|
||||
|
||||
private async void LyricsOnlyRadioButton_Click(object sender, RoutedEventArgs e)
|
||||
private void LyricsOnlyRadioButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.PreferredDisplayType = ViewModel.DisplayType = LyricsDisplayType.LyricsOnly;
|
||||
await SwitchToLyricsOnlyDisplayTypeAsync();
|
||||
}
|
||||
|
||||
private async void AlbumArtOnlyRadioButton_Click(object sender, RoutedEventArgs e)
|
||||
private void AlbumArtOnlyRadioButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.PreferredDisplayType = ViewModel.DisplayType = LyricsDisplayType.AlbumArtOnly;
|
||||
await SwitchToAlbumArtOnlyDisplayTypeAsync();
|
||||
}
|
||||
|
||||
private async void SplitViewRadioButton_Click(object sender, RoutedEventArgs e)
|
||||
private void SplitViewRadioButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.PreferredDisplayType = ViewModel.DisplayType = LyricsDisplayType.SplitView;
|
||||
await SwitchToSplitViewDisplayTypeAsync();
|
||||
}
|
||||
|
||||
private async Task SwitchToLyricsOnlyDisplayTypeAsync()
|
||||
private void PositionOffsetResetButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await BeforeSwitchDisplayTypeAsync();
|
||||
|
||||
Grid.SetColumn(LyricsPlaceholderGrid, 0);
|
||||
Grid.SetColumnSpan(LyricsPlaceholderGrid, 3);
|
||||
|
||||
LyricsPlaceholderGrid.Opacity = 1;
|
||||
LyricsGrid.Opacity = 1;
|
||||
ViewModel.PositionOffset = 0;
|
||||
}
|
||||
|
||||
|
||||
private async Task SwitchToAlbumArtOnlyDisplayTypeAsync()
|
||||
private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
await BeforeSwitchDisplayTypeAsync();
|
||||
|
||||
Grid.SetColumn(SongInfoInnerGrid, 0);
|
||||
Grid.SetColumnSpan(SongInfoInnerGrid, 3);
|
||||
|
||||
SongInfoInnerGrid.Opacity = 1;
|
||||
LyricsGrid.Opacity = 1;
|
||||
BottomCommandGrid.Opacity = 1;
|
||||
}
|
||||
|
||||
|
||||
private async Task BeforeSwitchDisplayTypeAsync()
|
||||
private void BottomCommandGrid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
SongInfoInnerGrid.Opacity = 0;
|
||||
LyricsPlaceholderGrid.Opacity = 0;
|
||||
//LyricsGrid.Opacity = 0;
|
||||
MainPageNoMusicPlayingTextBlock.Opacity = 0;
|
||||
|
||||
await Task.Delay(300);
|
||||
}
|
||||
|
||||
private async Task SwitchToSplitViewDisplayTypeAsync()
|
||||
{
|
||||
await BeforeSwitchDisplayTypeAsync();
|
||||
|
||||
Grid.SetColumn(SongInfoInnerGrid, 0);
|
||||
Grid.SetColumnSpan(SongInfoInnerGrid, 1);
|
||||
|
||||
Grid.SetColumn(LyricsPlaceholderGrid, 2);
|
||||
Grid.SetColumnSpan(LyricsPlaceholderGrid, 1);
|
||||
|
||||
SongInfoInnerGrid.Opacity = 1;
|
||||
LyricsPlaceholderGrid.Opacity = 1;
|
||||
LyricsGrid.Opacity = 1;
|
||||
}
|
||||
|
||||
private async Task SwitchToPlaceholderOnlyDisplayTypeAsync()
|
||||
{
|
||||
await BeforeSwitchDisplayTypeAsync();
|
||||
|
||||
MainPageNoMusicPlayingTextBlock.Opacity = 1;
|
||||
BottomCommandGrid.Opacity = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,81 +9,70 @@
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:media="using:CommunityToolkit.WinUI.Media"
|
||||
xmlns:scontrols="using:ShadowViewer.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
PointerMoved="RootGrid_PointerMoved"
|
||||
RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
|
||||
<Grid x:Name="RootGrid" RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
|
||||
|
||||
<local:LyricsPage />
|
||||
|
||||
<!-- Top command -->
|
||||
<Grid
|
||||
x:Name="TopCommandGrid"
|
||||
Height="{x:Bind ViewModel.TitleBarHeight, Mode=OneWay}"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
Opacity="0">
|
||||
Opacity="0"
|
||||
PointerEntered="TopCommandGrid_PointerEntered"
|
||||
PointerExited="TopCommandGrid_PointerExited">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<StackPanel
|
||||
x:Name="TopLeftCommandStackPanel"
|
||||
Margin="12"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
x:Name="TopRightCommandStackPanel"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
|
||||
<!-- Look -->
|
||||
<Button
|
||||
x:Name="ClickThroughButton"
|
||||
Command="{x:Bind ViewModel.LockWindowCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
FontWeight="Bold"
|
||||
Glyph="" />
|
||||
</Grid>
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="LockToolTip" x:Uid="HostWindowLockToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
||||
<interactivity:ChangePropertyAction
|
||||
PropertyName="IsOpen"
|
||||
TargetObject="{x:Bind LockToolTip}"
|
||||
Value="True" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
||||
<interactivity:ChangePropertyAction
|
||||
PropertyName="IsOpen"
|
||||
TargetObject="{x:Bind LockToolTip}"
|
||||
Value="False" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
|
||||
<!-- More -->
|
||||
<Button x:Name="MoreButton" Style="{StaticResource TitleBarButtonStyle}">
|
||||
<Grid>
|
||||
<FontIcon
|
||||
Margin="0,0,0,8"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
FontWeight="ExtraBold"
|
||||
Glyph="" />
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
FontWeight="ExtraBold"
|
||||
Glyph="" />
|
||||
<FontIcon
|
||||
Margin="0,8,0,0"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
FontWeight="ExtraBold"
|
||||
Glyph="" />
|
||||
</Grid>
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="MoreButtonToolTip" x:Uid="HostWindowMoreButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
<ToggleMenuFlyoutItem
|
||||
@@ -124,7 +113,7 @@
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Maximise -->
|
||||
<Button
|
||||
@@ -134,7 +123,7 @@
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Restore -->
|
||||
<Button
|
||||
@@ -145,7 +134,7 @@
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Close -->
|
||||
<Button
|
||||
@@ -155,54 +144,22 @@
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
<InfoBar
|
||||
x:Name="HostInfoBar"
|
||||
Margin="36"
|
||||
<scontrols:NotificationPanel
|
||||
x:Name="TipContainerCenter"
|
||||
Margin="0,0,0,52"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{ThemeResource SystemFillColorSolidAttentionBackgroundBrush}"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.ShowInfoBar, Mode=OneWay}"
|
||||
Message="{x:Bind ViewModel.Notification.Message, Mode=OneWay}"
|
||||
Opacity="0"
|
||||
Severity="{x:Bind ViewModel.Notification.Severity, Mode=OneWay}">
|
||||
<InfoBar.RenderTransform>
|
||||
<TranslateTransform x:Name="HostInfoBarTransform" Y="20" />
|
||||
</InfoBar.RenderTransform>
|
||||
<InfoBar.Resources>
|
||||
<Storyboard x:Key="InfoBarShowAndHideStoryboard">
|
||||
<!-- Opacity -->
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="HostInfoBar" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:3.6" Value="1" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:3.9" Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
|
||||
<!-- Y -->
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="HostInfoBarTransform" Storyboard.TargetProperty="Y">
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="20" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:3.6" Value="0" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:3.9" Value="20" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</InfoBar.Resources>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{Binding ElementName=HostInfoBar, Path=IsOpen, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ControlStoryboardAction Storyboard="{StaticResource InfoBarShowAndHideStoryboard}" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</InfoBar>
|
||||
Canvas.ZIndex="1"
|
||||
FlowDirection="RightToLeft"
|
||||
Loaded="TipContainerCenter_Loaded"
|
||||
Orientation="Vertical"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
@@ -34,6 +38,12 @@ namespace BetterLyrics.WinUI3.Views
|
||||
switch (type!)
|
||||
{
|
||||
case AutoStartWindowType.StandardMode:
|
||||
AppWindow.MoveAndResize(new Windows.Graphics.RectInt32(
|
||||
_settingsService.StandardWindowLeft,
|
||||
_settingsService.StandardWindowTop,
|
||||
_settingsService.StandardWindowWidth,
|
||||
_settingsService.StandardWindowHeight
|
||||
));
|
||||
break;
|
||||
case AutoStartWindowType.DockMode:
|
||||
DockFlyoutItem.IsChecked = true;
|
||||
@@ -62,24 +72,32 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
if (args.DidPresenterChange)
|
||||
UpdateTitleBarWindowButtonsVisibility();
|
||||
if (ViewModel.IsDesktopMode && (args.DidPositionChange || args.DidSizeChange))
|
||||
OnPosOrSizeChanged();
|
||||
}
|
||||
|
||||
private void OnPosOrSizeChanged()
|
||||
{
|
||||
var rect = AppWindow.Position;
|
||||
var size = AppWindow.Size;
|
||||
if (args.DidPositionChange || args.DidSizeChange)
|
||||
{
|
||||
var rect = AppWindow.Position;
|
||||
var size = AppWindow.Size;
|
||||
|
||||
_settingsService.DesktopWindowLeft = rect.X;
|
||||
_settingsService.DesktopWindowTop = rect.Y;
|
||||
_settingsService.DesktopWindowWidth = size.Width;
|
||||
_settingsService.DesktopWindowHeight = size.Height;
|
||||
}
|
||||
if (ViewModel.IsDesktopMode)
|
||||
{
|
||||
_settingsService.DesktopWindowLeft = rect.X;
|
||||
_settingsService.DesktopWindowTop = rect.Y;
|
||||
_settingsService.DesktopWindowWidth = size.Width;
|
||||
_settingsService.DesktopWindowHeight = size.Height;
|
||||
}
|
||||
else if (ViewModel.IsDockMode)
|
||||
{
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHelper.ExitAllWindows();
|
||||
}
|
||||
else
|
||||
{
|
||||
_settingsService.StandardWindowLeft = rect.X;
|
||||
_settingsService.StandardWindowTop = rect.Y;
|
||||
_settingsService.StandardWindowWidth = size.Width;
|
||||
_settingsService.StandardWindowHeight = size.Height;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void FullScreenFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
@@ -101,14 +119,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void MaximiseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.Maximize();
|
||||
}
|
||||
}
|
||||
|
||||
private void MiniFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (MiniFlyoutItem.IsChecked)
|
||||
@@ -121,6 +131,112 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHelper.OpenOrShowWindow<SettingsWindow>();
|
||||
}
|
||||
|
||||
private void UpdateTitleBarWindowButtonsVisibility()
|
||||
{
|
||||
switch (AppWindow.Presenter.Kind)
|
||||
{
|
||||
case AppWindowPresenterKind.Default:
|
||||
break;
|
||||
case AppWindowPresenterKind.CompactOverlay:
|
||||
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
|
||||
AOTFlyoutItem.Visibility = DesktopFlyoutItem.Visibility = FullScreenFlyoutItem.Visibility = DockFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility = Visibility.Collapsed;
|
||||
|
||||
break;
|
||||
case AppWindowPresenterKind.FullScreen:
|
||||
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
MiniFlyoutItem.Visibility =
|
||||
DockFlyoutItem.Visibility =
|
||||
Visibility.Collapsed;
|
||||
FullScreenFlyoutItem.IsChecked = true;
|
||||
|
||||
break;
|
||||
case AppWindowPresenterKind.Overlapped:
|
||||
DockFlyoutItem.Visibility = Visibility.Visible;
|
||||
var overlappedPresenter = (OverlappedPresenter)AppWindow.Presenter;
|
||||
if (DockFlyoutItem.IsChecked)
|
||||
{
|
||||
overlappedPresenter.IsMinimizable =
|
||||
overlappedPresenter.IsMaximizable = false;
|
||||
|
||||
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility =
|
||||
FullScreenFlyoutItem.Visibility =
|
||||
MiniFlyoutItem.Visibility =
|
||||
Visibility.Collapsed;
|
||||
|
||||
}
|
||||
else if (DesktopFlyoutItem.IsChecked)
|
||||
{
|
||||
overlappedPresenter.IsMinimizable =
|
||||
overlappedPresenter.IsMaximizable = false;
|
||||
|
||||
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
|
||||
DockFlyoutItem.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
FullScreenFlyoutItem.Visibility =
|
||||
MiniFlyoutItem.Visibility =
|
||||
Visibility.Collapsed;
|
||||
|
||||
ClickThroughButton.Visibility = Visibility.Visible;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
overlappedPresenter.IsMinimizable =
|
||||
overlappedPresenter.IsMaximizable = true;
|
||||
|
||||
MinimiseButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
DockFlyoutItem.Visibility =
|
||||
MiniFlyoutItem.Visibility =
|
||||
FullScreenFlyoutItem.Visibility =
|
||||
Visibility.Visible;
|
||||
FullScreenFlyoutItem.IsChecked = false;
|
||||
ClickThroughButton.Visibility = Visibility.Collapsed;
|
||||
AOTFlyoutItem.IsChecked = overlappedPresenter.IsAlwaysOnTop;
|
||||
|
||||
if (overlappedPresenter.State == OverlappedPresenterState.Maximized)
|
||||
{
|
||||
MaximiseButton.Visibility = Visibility.Collapsed;
|
||||
RestoreButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
else if (overlappedPresenter.State == OverlappedPresenterState.Restored)
|
||||
{
|
||||
MaximiseButton.Visibility = Visibility.Visible;
|
||||
RestoreButton.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHelper.ExitAllWindows();
|
||||
}
|
||||
|
||||
private void MaximiseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.Maximize();
|
||||
}
|
||||
}
|
||||
|
||||
private void MinimiseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
@@ -137,118 +253,19 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void RootGrid_PointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
private void TopCommandGrid_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var point = e.GetCurrentPoint(RootGrid);
|
||||
double y = point.Position.Y;
|
||||
|
||||
if (y >= 0 && y <= TopCommandGrid.ActualHeight + 5)
|
||||
{
|
||||
if (TopCommandGrid.Opacity == 0)
|
||||
{
|
||||
TopCommandGrid.Opacity = .5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TopCommandGrid.Opacity == .5)
|
||||
{
|
||||
TopCommandGrid.Opacity = 0;
|
||||
}
|
||||
}
|
||||
TopCommandGrid.Opacity = 1;
|
||||
}
|
||||
|
||||
private void SettingsMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
private void TopCommandGrid_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
WindowHelper.OpenOrShowWindow<SettingsWindow>();
|
||||
TopCommandGrid.Opacity = 0;
|
||||
}
|
||||
|
||||
private void UpdateTitleBarWindowButtonsVisibility()
|
||||
private void TipContainerCenter_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
switch (AppWindow.Presenter.Kind)
|
||||
{
|
||||
case AppWindowPresenterKind.Default:
|
||||
break;
|
||||
case AppWindowPresenterKind.CompactOverlay:
|
||||
MinimiseButton.Visibility =
|
||||
MaximiseButton.Visibility =
|
||||
RestoreButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility =
|
||||
FullScreenFlyoutItem.Visibility =
|
||||
DockFlyoutItem.Visibility =
|
||||
Visibility.Collapsed;
|
||||
break;
|
||||
case AppWindowPresenterKind.FullScreen:
|
||||
MinimiseButton.Visibility =
|
||||
MaximiseButton.Visibility =
|
||||
RestoreButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
MiniFlyoutItem.Visibility =
|
||||
DockFlyoutItem.Visibility =
|
||||
Visibility.Collapsed;
|
||||
FullScreenFlyoutItem.IsChecked = true;
|
||||
break;
|
||||
case AppWindowPresenterKind.Overlapped:
|
||||
DockFlyoutItem.Visibility = Visibility.Visible;
|
||||
var overlappedPresenter = (OverlappedPresenter)AppWindow.Presenter;
|
||||
if (DockFlyoutItem.IsChecked)
|
||||
{
|
||||
MinimiseButton.Visibility =
|
||||
MaximiseButton.Visibility =
|
||||
RestoreButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility =
|
||||
FullScreenFlyoutItem.Visibility =
|
||||
MiniFlyoutItem.Visibility =
|
||||
Visibility.Collapsed;
|
||||
}
|
||||
else if (DesktopFlyoutItem.IsChecked)
|
||||
{
|
||||
MinimiseButton.Visibility =
|
||||
MaximiseButton.Visibility =
|
||||
RestoreButton.Visibility =
|
||||
DockFlyoutItem.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
FullScreenFlyoutItem.Visibility =
|
||||
MiniFlyoutItem.Visibility =
|
||||
Visibility.Collapsed;
|
||||
|
||||
ClickThroughButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
MinimiseButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
DockFlyoutItem.Visibility =
|
||||
MiniFlyoutItem.Visibility =
|
||||
FullScreenFlyoutItem.Visibility =
|
||||
Visibility.Visible;
|
||||
FullScreenFlyoutItem.IsChecked = false;
|
||||
ClickThroughButton.Visibility = Visibility.Collapsed;
|
||||
AOTFlyoutItem.IsChecked = overlappedPresenter.IsAlwaysOnTop;
|
||||
|
||||
if (overlappedPresenter.State == OverlappedPresenterState.Maximized)
|
||||
{
|
||||
MaximiseButton.Visibility = Visibility.Collapsed;
|
||||
RestoreButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
else if (overlappedPresenter.State == OverlappedPresenterState.Restored)
|
||||
{
|
||||
MaximiseButton.Visibility = Visibility.Visible;
|
||||
RestoreButton.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
TopCommandGrid.Opacity = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
App.Current.LyricsWindowNotificationPanel = TipContainerCenter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||
xmlns:scontrols="using:ShadowViewer.Controls"
|
||||
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
xmlns:vm="using:BetterLyrics.WinUI3.ViewModels"
|
||||
@@ -33,6 +34,11 @@
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="AlbumArtStyle" />
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageMediaLib"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="MediaLib" />
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageLyricsLib"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
@@ -48,6 +54,11 @@
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="Lyrics" />
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageTranslation"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="Translation" />
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageAbout"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
@@ -69,8 +80,183 @@
|
||||
</TransitionCollection>
|
||||
</controls:SwitchPresenter.ContentTransitions>
|
||||
|
||||
<!-- Lyrics lib -->
|
||||
<!-- App appearance and behavior -->
|
||||
<controls:Case Value="App">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<!-- App appearance -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAppAppearance" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageLanguage"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.Language, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageSystemLanguage" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEN" />
|
||||
<ComboBoxItem x:Uid="SettingsPageSC" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTC" />
|
||||
<ComboBoxItem x:Uid="SettingsPageJA" />
|
||||
<ComboBoxItem x:Uid="SettingsPageKO" />
|
||||
</ComboBox>
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard>
|
||||
<Button x:Uid="SettingsPageRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<!-- App behavior -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAppBehavior" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageAutoStart" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch x:Name="AutoStartupToggleSwitch" Loaded="AutoStartupToggleSwitch_Loaded" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageAutoStartWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.AutoStartWindowType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartInAppLyrics" />
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartDockLyrics" />
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartDesktopLyrics" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageAutoLock" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AutoLockOnDesktopMode, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageIgnoreFullscreenWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IgnoreFullscreenWindow, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics background -->
|
||||
<controls:Case Value="Background">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageTheme" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox x:Name="ThemeComboBox" SelectedIndex="{x:Bind ViewModel.LyricsBackgroundTheme, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageFollowSystem" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLight" />
|
||||
<ComboBoxItem x:Uid="SettingsPageDark" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageDynamicLyricsBackground" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsDynamicCoverOverlayEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsBackgroundOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.CoverOverlayOpacity, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" %" />
|
||||
<Slider
|
||||
Maximum="100"
|
||||
Minimum="1"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.CoverOverlayOpacity, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsBackgroundBlurAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind ViewModel.CoverOverlayBlurAmount, Mode=OneWay}" />
|
||||
<Slider
|
||||
Maximum="200"
|
||||
Minimum="50"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="10"
|
||||
TickFrequency="10"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.CoverOverlayBlurAmount, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Album art area style -->
|
||||
<controls:Case Value="AlbumArtStyle">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAlbumArt" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.CoverImageRadius, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" %" />
|
||||
<Slider
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="2"
|
||||
TickFrequency="2"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.CoverImageRadius, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<TextBlock x:Uid="SettingsPageSongInfo" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageSongInfoAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.SongInfoAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageSongInfoLeft" />
|
||||
<ComboBoxItem x:Uid="SettingsPageSongInfoCenter" />
|
||||
<ComboBoxItem x:Uid="SettingsPageSongInfoRight" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Media source lib -->
|
||||
<controls:Case Value="MediaLib">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsCard x:Uid="SettingsPageMediaSourceProvidersConfig" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
<ListView
|
||||
x:Name="MediaSourceProvidersListView"
|
||||
Margin="0,-4,0,0"
|
||||
ItemsSource="{x:Bind ViewModel.MediaSourceProvidersInfo, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:MediaSourceProviderInfo">
|
||||
<controls:SettingsCard Padding="60,0,48,0" Header="{Binding Provider, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" Toggled="MediaSourceProviderToggleSwitch_Toggled" />
|
||||
</controls:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics lib -->
|
||||
<controls:Case Value="LyricsLib">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsExpander
|
||||
@@ -83,7 +269,10 @@
|
||||
<DataTemplate>
|
||||
<controls:SettingsCard>
|
||||
<controls:SettingsCard.Header>
|
||||
<HyperlinkButton Content="{Binding Path, Mode=OneWay}" NavigateUri="{Binding Path, Mode=OneWay}" />
|
||||
<HyperlinkButton
|
||||
Click="LocalFolderHyperlinkButton_Click"
|
||||
Content="{Binding Path, Mode=OneWay}"
|
||||
Tag="{Binding Path, Mode=OneWay}" />
|
||||
</controls:SettingsCard.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<HyperlinkButton
|
||||
@@ -170,137 +359,10 @@
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- App appearance and behavior -->
|
||||
|
||||
<controls:Case Value="App">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<!-- App appearance -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAppAppearance" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageLanguage"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.Language, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageSystemLanguage" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEN" />
|
||||
<ComboBoxItem x:Uid="SettingsPageSC" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTC" />
|
||||
<ComboBoxItem x:Uid="SettingsPageJA" />
|
||||
<ComboBoxItem x:Uid="SettingsPageKO" />
|
||||
</ComboBox>
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard>
|
||||
<Button x:Uid="SettingsPageRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<!-- App behavior -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAppBehavior" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsCard x:Uid="SettingsPageAutoStartWindow">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.AutoStartWindowType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartInAppLyrics" />
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartDockLyrics" />
|
||||
<ComboBoxItem x:Uid="SettingsPageAutoStartDesktopLyrics" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageAutoLock">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AutoLockOnDesktopMode, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics background -->
|
||||
|
||||
<controls:Case Value="Background">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageDynamicLyricsBackground">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsDynamicCoverOverlayEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsBackgroundOpacity">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.CoverOverlayOpacity, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" %" />
|
||||
<Slider
|
||||
Maximum="100"
|
||||
Minimum="1"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.CoverOverlayOpacity, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsBackgroundBlurAmount">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind ViewModel.CoverOverlayBlurAmount, Mode=OneWay}" />
|
||||
<Slider
|
||||
Maximum="200"
|
||||
Minimum="50"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="10"
|
||||
TickFrequency="10"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.CoverOverlayBlurAmount, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Album art style -->
|
||||
|
||||
<controls:Case Value="AlbumArtStyle">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.CoverImageRadius, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" %" />
|
||||
<Slider
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="2"
|
||||
TickFrequency="2"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.CoverImageRadius, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics style and effect -->
|
||||
|
||||
<controls:Case Value="Lyrics">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
@@ -309,7 +371,7 @@
|
||||
<TextBlock x:Uid="SettingsPageLyricsStyle" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsSettingsControlViewModel.LyricsAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsLeft" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsCenter" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsRight" />
|
||||
@@ -317,7 +379,7 @@
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsFontWeight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsSettingsControlViewModel.LyricsFontWeight, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsFontWeight, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsThin" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsExtraLight" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsLight" />
|
||||
@@ -332,8 +394,26 @@
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsFontColor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsSettingsControlViewModel.LyricsFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsFontStrokeWidth" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind ViewModel.LyricsFontStrokeWidth, Mode=OneWay}" />
|
||||
<Slider
|
||||
Maximum="5"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.LyricsFontStrokeWidth, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsStrokeFontColor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsStrokeFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
|
||||
@@ -349,16 +429,84 @@
|
||||
IsColorSliderVisible="True"
|
||||
IsHexInputVisible="True"
|
||||
IsMoreButtonVisible="True"
|
||||
Color="{x:Bind LyricsSettingsControlViewModel.LyricsCustomFontColor, Mode=TwoWay}">
|
||||
Color="{x:Bind ViewModel.LyricsCustomStrokeFontColor, Mode=TwoWay}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind LyricsSettingsControlViewModel.LyricsFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
Binding="{x:Bind ViewModel.LyricsStrokeFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="2">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind LyricsSettingsControlViewModel.LyricsFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
Binding="{x:Bind ViewModel.LyricsStrokeFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="2">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ColorPicker>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsBgFontColor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsBgFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<ColorPicker
|
||||
ColorSpectrumShape="Box"
|
||||
IsAlphaEnabled="True"
|
||||
IsAlphaSliderVisible="True"
|
||||
IsAlphaTextInputVisible="True"
|
||||
IsColorChannelTextInputVisible="True"
|
||||
IsColorSliderVisible="True"
|
||||
IsHexInputVisible="True"
|
||||
IsMoreButtonVisible="True"
|
||||
Color="{x:Bind ViewModel.LyricsCustomBgFontColor, Mode=TwoWay}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LyricsBgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="2">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LyricsBgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="2">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ColorPicker>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsFgFontColor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsFgFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<ColorPicker
|
||||
ColorSpectrumShape="Box"
|
||||
IsAlphaEnabled="True"
|
||||
IsAlphaSliderVisible="True"
|
||||
IsAlphaTextInputVisible="True"
|
||||
IsColorChannelTextInputVisible="True"
|
||||
IsColorSliderVisible="True"
|
||||
IsHexInputVisible="True"
|
||||
IsMoreButtonVisible="True"
|
||||
Color="{x:Bind ViewModel.LyricsCustomFgFontColor, Mode=TwoWay}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LyricsFgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="2">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LyricsFgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="2">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
@@ -381,7 +529,7 @@
|
||||
StepFrequency="2"
|
||||
TickFrequency="2"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind LyricsSettingsControlViewModel.LyricsFontSize, Mode=TwoWay}" />
|
||||
Value="{x:Bind ViewModel.LyricsFontSize, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
@@ -401,7 +549,7 @@
|
||||
StepFrequency="0.1"
|
||||
TickFrequency="0.1"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind LyricsSettingsControlViewModel.LyricsLineSpacingFactor, Mode=TwoWay}" />
|
||||
Value="{x:Bind ViewModel.LyricsLineSpacingFactor, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
@@ -428,7 +576,7 @@
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind LyricsSettingsControlViewModel.LyricsVerticalEdgeOpacity, Mode=TwoWay}" />
|
||||
Value="{x:Bind ViewModel.LyricsVerticalEdgeOpacity, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
@@ -447,7 +595,7 @@
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind LyricsSettingsControlViewModel.LyricsBlurAmount, Mode=TwoWay}" />
|
||||
Value="{x:Bind ViewModel.LyricsBlurAmount, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
@@ -455,11 +603,11 @@
|
||||
x:Uid="SettingsPageLyricsGlowEffect"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind LyricsSettingsControlViewModel.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsSettingsControlViewModel.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
||||
IsExpanded="{x:Bind ViewModel.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsGlowEffectScope" IsEnabled="{x:Bind LyricsSettingsControlViewModel.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsSettingsControlViewModel.LyricsGlowEffectScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsGlowEffectScope" IsEnabled="{x:Bind ViewModel.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsGlowEffectScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsGlowEffectScopeCurrentLine" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsGlowEffectScopeCurrentChar" />
|
||||
</ComboBox>
|
||||
@@ -468,14 +616,84 @@
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageFan" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsSettingsControlViewModel.IsFanLyricsEnabled, Mode=TwoWay}" />
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsFanLyricsEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- About -->
|
||||
<!-- Lyrics translation -->
|
||||
<controls:Case Value="Translation">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageTranslationConfig"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="SettingsPageLibreTranslateServer">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBox
|
||||
x:Name="LibreTranslateServerTextBox"
|
||||
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||
PlaceholderText="http://localhost:5000"
|
||||
Text="{x:Bind ViewModel.LibreTranslateServer, Mode=TwoWay}" />
|
||||
<Button
|
||||
x:Uid="SettingsPageServerTestButton"
|
||||
Command="{x:Bind ViewModel.LibreTranslateServerTestCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard x:Uid="SettingsPageTargetLanguage">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.SelectedTargetLanguageIndex, Mode=TwoWay}">
|
||||
<ComboBoxItem Content="العربية" Tag="ar" />
|
||||
<ComboBoxItem Content="Azərbaycan dili" Tag="az" />
|
||||
<ComboBoxItem Content="中文" Tag="zh" />
|
||||
<ComboBoxItem Content="Čeština" Tag="cs" />
|
||||
<ComboBoxItem Content="Dansk" Tag="da" />
|
||||
<ComboBoxItem Content="Nederlands" Tag="nl" />
|
||||
<ComboBoxItem Content="English" Tag="en" />
|
||||
<ComboBoxItem Content="Esperanto" Tag="eo" />
|
||||
<ComboBoxItem Content="Suomi" Tag="fi" />
|
||||
<ComboBoxItem Content="Français" Tag="fr" />
|
||||
<ComboBoxItem Content="Deutsch" Tag="de" />
|
||||
<ComboBoxItem Content="Ελληνικά" Tag="el" />
|
||||
<ComboBoxItem Content="עברית" Tag="he" />
|
||||
<ComboBoxItem Content="हिन्दी" Tag="hi" />
|
||||
<ComboBoxItem Content="Magyar" Tag="hu" />
|
||||
<ComboBoxItem Content="Bahasa Indonesia" Tag="id" />
|
||||
<ComboBoxItem Content="Gaeilge" Tag="ga" />
|
||||
<ComboBoxItem Content="Italiano" Tag="it" />
|
||||
<ComboBoxItem Content="日本語" Tag="ja" />
|
||||
<ComboBoxItem Content="한국어" Tag="ko" />
|
||||
<ComboBoxItem Content="فارسی" Tag="fa" />
|
||||
<ComboBoxItem Content="Polski" Tag="pl" />
|
||||
<ComboBoxItem Content="Português" Tag="pt" />
|
||||
<ComboBoxItem Content="Русский" Tag="ru" />
|
||||
<ComboBoxItem Content="Slovenčina" Tag="sk" />
|
||||
<ComboBoxItem Content="Español" Tag="es" />
|
||||
<ComboBoxItem Content="Svenska" Tag="sv" />
|
||||
<ComboBoxItem Content="Türkçe" Tag="tr" />
|
||||
<ComboBoxItem Content="Українська" Tag="uk" />
|
||||
<ComboBoxItem Content="Tiếng Việt" Tag="vi" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard x:Uid="SettingsPageTranslationInfo">
|
||||
<controls:SettingsCard.Description>
|
||||
<HyperlinkButton Margin="0,6,0,0" NavigateUri="https://github.com/LibreTranslate/LibreTranslate">
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageTranslationInfoLink"
|
||||
FontSize="14"
|
||||
TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
</controls:SettingsCard.Description>
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- About -->
|
||||
<controls:Case Value="About">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsCard Header="BetterLyrics" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/Logo.png}">
|
||||
@@ -502,6 +720,7 @@
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Dev -->
|
||||
<controls:Case Value="Dev">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsCard x:Uid="SettingsPageMockMusicPlaying">
|
||||
@@ -524,5 +743,16 @@
|
||||
</ScrollViewer>
|
||||
</NavigationView>
|
||||
|
||||
<scontrols:NotificationPanel
|
||||
x:Name="TipContainerCenter"
|
||||
Margin="0,0,0,52"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Canvas.ZIndex="1"
|
||||
FlowDirection="RightToLeft"
|
||||
Loaded="TipContainerCenter_Loaded"
|
||||
Orientation="Vertical"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterInAppLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using Windows.System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
public sealed partial class SettingsPage : Page
|
||||
{
|
||||
private bool _isUserToggle;
|
||||
|
||||
public SettingsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
DataContext = Ioc.Default.GetRequiredService<SettingsPageViewModel>();
|
||||
}
|
||||
|
||||
public LyricsSettingsControlViewModel LyricsSettingsControlViewModel =>
|
||||
Ioc.Default.GetRequiredService<LyricsSettingsControlViewModel>();
|
||||
|
||||
public SettingsPageViewModel ViewModel => (SettingsPageViewModel)DataContext;
|
||||
|
||||
private void LocalLyricsFolderToggleSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||
@@ -67,5 +67,43 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
ViewModel.RemoveFolderAsync((LocalLyricsFolder)(sender as HyperlinkButton)!.Tag);
|
||||
}
|
||||
|
||||
private void MediaSourceProviderToggleSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ToggleSwitch toggleSwitch)
|
||||
{
|
||||
if (toggleSwitch.DataContext is MediaSourceProviderInfo providerInfo)
|
||||
{
|
||||
ViewModel.ToggleMediaSourceProvider(providerInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void LocalFolderHyperlinkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is HyperlinkButton button && button.Tag is string uriStr)
|
||||
{
|
||||
if (Uri.TryCreate(uriStr, UriKind.Absolute, out var uri))
|
||||
{
|
||||
await Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TipContainerCenter_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel = TipContainerCenter;
|
||||
}
|
||||
|
||||
private async void AutoStartupToggleSwitch_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AutoStartupToggleSwitch.IsOn = await ViewModel.DetectIsAutoStartupEnabledAsync();
|
||||
AutoStartupToggleSwitch.Toggled += AutoStartupToggleSwitch_Toggled;
|
||||
}
|
||||
|
||||
private void AutoStartupToggleSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.ToggleAutoStartupAsync(AutoStartupToggleSwitch.IsOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user