mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
fix
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.7.0" />
|
||||
Version="1.0.8.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ 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;
|
||||
@@ -94,7 +93,6 @@ namespace BetterLyrics.WinUI3
|
||||
.AddSingleton<SettingsPageViewModel>()
|
||||
.AddSingleton<LyricsPageViewModel>()
|
||||
.AddSingleton<LyricsRendererViewModel>()
|
||||
.AddSingleton<LyricsSettingsControlViewModel>()
|
||||
.BuildServiceProvider()
|
||||
);
|
||||
}
|
||||
@@ -107,7 +105,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 +115,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,7 +40,9 @@
|
||||
<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" />
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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,13 +18,15 @@ 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;
|
||||
|
||||
@@ -44,16 +47,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 +74,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 +91,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 +125,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 +169,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,5 +81,10 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.5f;
|
||||
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; }
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace BetterLyrics.WinUI3.Serialization
|
||||
{
|
||||
|
||||
[JsonSerializable(typeof(List<LyricsSearchProviderInfo>))]
|
||||
[JsonSerializable(typeof(List<MediaSourceProviderInfo>))]
|
||||
[JsonSerializable(typeof(List<LocalLyricsFolder>))]
|
||||
[JsonSerializable(typeof(List<string>))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
|
||||
@@ -8,14 +8,13 @@ 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(
|
||||
string title,
|
||||
string artist,
|
||||
string album = "",
|
||||
double durationMs = 0.0,
|
||||
MusicSearchMatchMode matchMode = MusicSearchMatchMode.TitleAndArtist
|
||||
double durationMs = 0.0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
// Lyrics style and effetc
|
||||
|
||||
LyricsAlignmentType LyricsAlignmentType { get; set; }
|
||||
TextAlignmentType LyricsAlignmentType { get; set; }
|
||||
TextAlignmentType SongInfoAlignmentType { get; set; }
|
||||
|
||||
int LyricsBlurAmount { get; set; }
|
||||
|
||||
@@ -55,7 +56,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
List<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
|
||||
|
||||
int LyricsVerticalEdgeOpacity { get; set; }
|
||||
List<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; }
|
||||
|
||||
int LyricsVerticalEdgeOpacity { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using iTunesSearch.Library;
|
||||
using Lyricify.Lyrics.Providers.Web.Kugou;
|
||||
using Lyricify.Lyrics.Searchers;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -7,11 +13,6 @@ using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
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
|
||||
{
|
||||
@@ -21,17 +22,23 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private readonly HttpClient _lrcLibHttpClient;
|
||||
|
||||
private readonly HttpClient _iTunesHttpClinet;
|
||||
|
||||
private readonly iTunesSearchManager _iTunesSearchManager;
|
||||
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
public MusicSearchService(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_lrcLibHttpClient = new HttpClient();
|
||||
_lrcLibHttpClient = new();
|
||||
_lrcLibHttpClient.DefaultRequestHeaders.Add(
|
||||
"User-Agent",
|
||||
$"{AppInfo.AppName} {AppInfo.AppVersion} ({AppInfo.GithubUrl})"
|
||||
);
|
||||
_amllTtmlDbHttpClient = new HttpClient();
|
||||
_amllTtmlDbHttpClient = new();
|
||||
_iTunesHttpClinet = new();
|
||||
_iTunesSearchManager = new();
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadAmllTtmlDbIndexAsync()
|
||||
@@ -59,7 +66,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,12 +102,19 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
//var resultItems = await _iTunesSearchManager.GetAlbumsAsync(album, 1, countryCode: GuessCountryCode(album, artist));
|
||||
//var url = resultItems.Albums.Where(al => Normalize(al.ArtistName).Contains(Normalize(artist)))
|
||||
// .FirstOrDefault()?.ArtworkUrl100.Replace("100x100bb.jpg", "100000x100000-999.jpg");
|
||||
//if (url != null)
|
||||
//{
|
||||
// return await _iTunesHttpClinet.GetByteArrayAsync(url);
|
||||
//}
|
||||
|
||||
return await ImageHelper.CreateTextPlaceholderBytesAsync($"{artist} - {title}", 400, 400);
|
||||
}
|
||||
|
||||
public async Task<(string?, LyricsFormat?)> SearchLyricsAsync(
|
||||
string title, string artist, string album = "", double durationMs = 0.0,
|
||||
MusicSearchMatchMode matchMode = MusicSearchMatchMode.TitleArtistAlbumAndDuration
|
||||
string title, string artist, string album = "", double durationMs = 0.0
|
||||
)
|
||||
{
|
||||
foreach (var provider in _settingsService.LyricsSearchProvidersInfo)
|
||||
@@ -126,16 +155,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);
|
||||
@@ -161,9 +190,22 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
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();
|
||||
@@ -303,20 +345,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,30 +384,28 @@ 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,
|
||||
}
|
||||
} //, Lyricify.Lyrics.Searchers.Helpers.CompareHelper.MatchType.Perfect
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
else if (result is NeteaseSearchResult neteaseResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyricNew(neteaseResult.Id);
|
||||
return response?.Lrc.Lyric;
|
||||
}
|
||||
else if (result is KugouSearchResult kugouResult)
|
||||
|
||||
@@ -1,208 +1,218 @@
|
||||
// 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.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Windows.ApplicationModel;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Media.Control;
|
||||
using Windows.Storage.Streams;
|
||||
using WindowsMediaController;
|
||||
using static Lyricify.Lyrics.Providers.Web.Musixmatch.GetTokenResponse;
|
||||
|
||||
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 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) { }
|
||||
|
||||
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)
|
||||
);
|
||||
_musicSearchService = musicSearchService;
|
||||
_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 async 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
MediaManager_OnAnyMediaPropertyChanged(mediaSession, await mediaSession.ControlSession.TryGetMediaPropertiesAsync());
|
||||
MediaManager_OnAnyPlaybackStateChanged(mediaSession, mediaSession.ControlSession.GetPlaybackInfo());
|
||||
}
|
||||
_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;
|
||||
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)
|
||||
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;
|
||||
|
||||
if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
{
|
||||
bytes = await ImageHelper.ToByteArrayAsync(
|
||||
streamReference
|
||||
);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes = await _musicSearchService.SearchAlbumArtAsync(
|
||||
songInfo.Title,
|
||||
songInfo.Artist,
|
||||
songInfo.Album
|
||||
);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
var decoder = await ImageHelper.GetDecoderFromByte(bytes);
|
||||
token.ThrowIfCancellationRequested();
|
||||
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)
|
||||
{
|
||||
string id = mediaSession.ControlSession.SourceAppUserModelId;
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
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";
|
||||
@@ -41,7 +41,8 @@ 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 LyricsFontSizeKey = "LyricsFontSize";
|
||||
@@ -51,6 +52,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private const string LyricsSearchProvidersInfoKey = "LyricsSearchProvidersInfo";
|
||||
private const string LyricsVerticalEdgeOpacityKey = "LyricsVerticalEdgeOpacity";
|
||||
|
||||
private const string MediaSourceProvidersInfoKey = "MediaSourceProvidersInfo";
|
||||
|
||||
private readonly ApplicationDataContainer _localSettings;
|
||||
|
||||
public SettingsService()
|
||||
@@ -69,6 +72,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SourceGenerationContext.Default.ListLyricsSearchProviderInfo
|
||||
)
|
||||
);
|
||||
SetDefault(MediaSourceProvidersInfoKey, "[]");
|
||||
if (LyricsSearchProvidersInfo.Count != Enum.GetValues<LyricsSearchProvider>().Length)
|
||||
{
|
||||
LyricsSearchProvidersInfo = Enum.GetValues<LyricsSearchProvider>()
|
||||
@@ -97,7 +101,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SetDefault(CoverOverlayBlurAmountKey, 200);
|
||||
SetDefault(CoverImageRadiusKey, 24); // 24 %
|
||||
// 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);
|
||||
@@ -210,12 +215,18 @@ 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);
|
||||
@@ -275,6 +286,23 @@ 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);
|
||||
|
||||
@@ -228,15 +228,27 @@
|
||||
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
|
||||
<value>Alignment</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>Alignment</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
|
||||
<value>Center</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>Center</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>Left</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>Left</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>Right</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>Right</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
|
||||
<value>Lyrics background blur amount</value>
|
||||
</data>
|
||||
@@ -328,7 +340,7 @@
|
||||
<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>
|
||||
@@ -540,4 +552,22 @@
|
||||
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
|
||||
<value>Auto-lock when activating desktop mode</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>
|
||||
</root>
|
||||
@@ -228,15 +228,27 @@
|
||||
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
|
||||
<value>アライメント</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>アライメント</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
|
||||
<value>中心</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>中心</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>左</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>左</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>右</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>右</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
|
||||
<value>歌詞の背景ぼやけ</value>
|
||||
</data>
|
||||
@@ -328,7 +340,7 @@
|
||||
<value>歌詞の背景に適応する(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>アルバムアートスタイル</value>
|
||||
<value>アルバムエリアスタイル</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>コーナー半径</value>
|
||||
@@ -540,4 +552,22 @@
|
||||
<data name="SettingsPageAutoLock.Header" 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>
|
||||
</root>
|
||||
@@ -228,15 +228,27 @@
|
||||
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
|
||||
<value>조정</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>조정</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
|
||||
<value>센터</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>센터</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>왼쪽</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>왼쪽</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>오른쪽</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>오른쪽</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
|
||||
<value>가사 배경 블러</value>
|
||||
</data>
|
||||
@@ -328,7 +340,7 @@
|
||||
<value>가사 배경 (회색)에 적응</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>앨범 아트 스타일</value>
|
||||
<value>앨범 영역 스타일</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>코너 반경</value>
|
||||
@@ -540,4 +552,22 @@
|
||||
<data name="SettingsPageAutoLock.Header" 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>
|
||||
</root>
|
||||
@@ -228,15 +228,27 @@
|
||||
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
|
||||
<value>对齐方式</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>对齐方式</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
|
||||
<value>居中</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>居中</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>靠左</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>靠左</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
|
||||
<value>歌词背景模糊度</value>
|
||||
</data>
|
||||
@@ -328,7 +340,7 @@
|
||||
<value>适应歌词背景(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>专辑封面样式</value>
|
||||
<value>专辑区域样式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>圆角半径</value>
|
||||
@@ -540,4 +552,22 @@
|
||||
<data name="SettingsPageAutoLock.Header" 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>
|
||||
</root>
|
||||
@@ -228,15 +228,27 @@
|
||||
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
|
||||
<value>對齊方式</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
|
||||
<value>對齊方式</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
|
||||
<value>居中</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
|
||||
<value>居中</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
|
||||
<value>靠左</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
|
||||
<value>靠左</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
|
||||
<value>歌詞背景模糊度</value>
|
||||
</data>
|
||||
@@ -328,7 +340,7 @@
|
||||
<value>適應歌詞背景(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>專輯封面樣式</value>
|
||||
<value>专辑区域样式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>圓角半徑</value>
|
||||
@@ -540,4 +552,22 @@
|
||||
<data name="SettingsPageAutoLock.Header" 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>
|
||||
</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;
|
||||
@@ -13,9 +9,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
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;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
@@ -28,35 +22,23 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
public LyricsPageViewModel(ISettingsService settingsService, IPlaybackService playbackService) : base(settingsService)
|
||||
{
|
||||
LyricsFontSize = _settingsService.LyricsFontSize;
|
||||
CoverImageRadius = _settingsService.CoverImageRadius;
|
||||
|
||||
_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;
|
||||
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; }
|
||||
@@ -70,28 +52,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[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;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial SongInfo? SongInfo { get; set; } = null;
|
||||
|
||||
public void OpenMatchedFileFolderInFileExplorer(string path)
|
||||
{
|
||||
Process.Start(
|
||||
new ProcessStartInfo
|
||||
{
|
||||
FileName = "explorer.exe",
|
||||
Arguments = $"/select,\"{path}\"",
|
||||
UseShellExecute = true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
{
|
||||
if (message.Sender is LyricsWindowViewModel)
|
||||
@@ -114,37 +80,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()
|
||||
{
|
||||
@@ -185,24 +127,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,16 @@ 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.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,18 +29,7 @@ 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);
|
||||
}
|
||||
|
||||
using var combined = new CanvasCommandList(control);
|
||||
@@ -60,6 +53,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
ds.DrawImage(combined);
|
||||
}
|
||||
|
||||
DrawAlbumArt(control, ds);
|
||||
|
||||
DrawTitleAndArtist(control, ds);
|
||||
|
||||
if (_isDebugOverlayEnabled)
|
||||
{
|
||||
var currentPlayingLineIndex = GetCurrentPlayingLineIndex();
|
||||
@@ -77,48 +74,88 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
);
|
||||
|
||||
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}",
|
||||
$"[DEBUG]\n" +
|
||||
$"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" +
|
||||
$"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.Text} {line.ScaleTransition.Value}",
|
||||
// new Vector2(10, 30 + (i - _startVisibleLineIndex) * 20),
|
||||
// ThemeTypeSent == Microsoft.UI.Xaml.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, SoftwareBitmap swBitmap, float opacity)
|
||||
{
|
||||
using var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, softwareBitmap);
|
||||
using var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, swBitmap);
|
||||
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;
|
||||
|
||||
ds.DrawImage(new OpacityEffect
|
||||
{
|
||||
Source = new ScaleEffect
|
||||
{
|
||||
Scale = new Vector2(scaleFactor),
|
||||
Source = canvasBitmap,
|
||||
},
|
||||
Opacity = opacity,
|
||||
}, new Vector2(x, y)
|
||||
);
|
||||
}
|
||||
|
||||
private void DrawForegroundImgae(ICanvasAnimatedControl control, CanvasDrawingSession ds, SoftwareBitmap swBitmap, float opacity)
|
||||
{
|
||||
using var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, swBitmap);
|
||||
float imageWidth = (float)canvasBitmap.Size.Width;
|
||||
float imageHeight = (float)canvasBitmap.Size.Height;
|
||||
|
||||
float scaleFactor = _albumArtSize / Math.Min(imageWidth, imageHeight);
|
||||
if (scaleFactor < 0.1f) return;
|
||||
|
||||
_albumArtY = 36 + (_canvasHeight - 36 * 2) * 3 / 16;
|
||||
|
||||
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
|
||||
{
|
||||
InterpolationMode = CanvasImageInterpolation.HighQualityCubic,
|
||||
BorderMode = EffectBorderMode.Hard,
|
||||
Scale = new Vector2(scaleFactor),
|
||||
Source = canvasBitmap,
|
||||
},
|
||||
Opacity = opacity,
|
||||
AlphaMask = cornerRadiusMask,
|
||||
},
|
||||
(float)control.Size.Width / 2 - imageWidth * scaleFactor / 2,
|
||||
(float)control.Size.Height / 2 - imageHeight * scaleFactor / 2
|
||||
Opacity = opacity,
|
||||
}, new Vector2(_albumArtXTransition.Value, _albumArtY)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -129,23 +166,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
var overlappedCovers = new CanvasCommandList(control.Device);
|
||||
using var overlappedCoversDs = overlappedCovers.CreateDrawingSession();
|
||||
|
||||
if (_lastAlbumArtBitmap != null)
|
||||
if (_lastAlbumArtSwBitmap != null)
|
||||
{
|
||||
DrawImgae(
|
||||
control,
|
||||
overlappedCoversDs,
|
||||
_lastAlbumArtBitmap,
|
||||
1 - _albumArtBgTransition.Value
|
||||
);
|
||||
DrawBackgroundImgae(control, overlappedCoversDs, _lastAlbumArtSwBitmap, 1 - _albumArtBgTransition.Value);
|
||||
}
|
||||
if (_albumArtBitmap != null)
|
||||
if (_albumArtSwBitmap != null)
|
||||
{
|
||||
DrawImgae(
|
||||
control,
|
||||
overlappedCoversDs,
|
||||
_albumArtBitmap,
|
||||
_albumArtBgTransition.Value
|
||||
);
|
||||
DrawBackgroundImgae(control, overlappedCoversDs, _albumArtSwBitmap, _albumArtBgTransition.Value);
|
||||
}
|
||||
|
||||
using var coverOverlayEffect = new OpacityEffect
|
||||
@@ -162,6 +189,70 @@ 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 (_albumArtSwBitmap != null)
|
||||
{
|
||||
DrawForegroundImgae(control, albumArtDs, _albumArtSwBitmap, _albumArtBgTransition.Value);
|
||||
}
|
||||
if (_lastAlbumArtSwBitmap != null)
|
||||
{
|
||||
DrawForegroundImgae(control, albumArtDs, _lastAlbumArtSwBitmap, 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)
|
||||
{
|
||||
float titleY = _albumArtY + _albumArtSize + 12;
|
||||
|
||||
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),
|
||||
_fontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 255 * opacity)));
|
||||
ds.DrawTextLayout(
|
||||
artistLayout,
|
||||
new Vector2(_albumArtXTransition.Value, titleY + (float)titleLayout.LayoutBounds.Height),
|
||||
_fontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 128 * opacity)));
|
||||
}
|
||||
|
||||
private void DrawBlurredLyrics(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
var currentPlayingLineIndex = GetCurrentPlayingLineIndex();
|
||||
@@ -206,35 +297,26 @@ 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);
|
||||
@@ -255,138 +337,132 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
);
|
||||
|
||||
// 再叠加当前行歌词层
|
||||
// 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)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
GetLinePlayingProgress(
|
||||
line,
|
||||
out int charStartIndex,
|
||||
out int charLength,
|
||||
out float charProgress
|
||||
);
|
||||
var regions = textLayout.GetCharacterRegions(0, charStartIndex);
|
||||
var highlightRegion = textLayout
|
||||
.GetCharacterRegions(charStartIndex, charLength)
|
||||
.FirstOrDefault();
|
||||
if (regions.Length > 0)
|
||||
{
|
||||
GetLinePlayingProgress(
|
||||
line,
|
||||
out int charStartIndex,
|
||||
out int charLength,
|
||||
out float charProgress
|
||||
);
|
||||
var regions = textLayout.GetCharacterRegions(0, charStartIndex);
|
||||
var highlightRegion = textLayout
|
||||
.GetCharacterRegions(charStartIndex, charLength)
|
||||
.FirstOrDefault();
|
||||
if (regions.Length > 0)
|
||||
// Draw the mask for the current line
|
||||
for (int j = 0; j < regions.Length; j++)
|
||||
{
|
||||
// Draw the mask for the current line
|
||||
for (int j = 0; j < regions.Length; j++)
|
||||
{
|
||||
var region = regions[j];
|
||||
var rect = new Rect(
|
||||
region.LayoutBounds.X,
|
||||
region.LayoutBounds.Y + position.Y,
|
||||
region.LayoutBounds.Width,
|
||||
region.LayoutBounds.Height
|
||||
);
|
||||
maskDs.FillRectangle(rect, Colors.Black);
|
||||
}
|
||||
var region = regions[j];
|
||||
var rect = new Rect(
|
||||
region.LayoutBounds.X,
|
||||
region.LayoutBounds.Y + position.Y,
|
||||
region.LayoutBounds.Width,
|
||||
region.LayoutBounds.Height
|
||||
);
|
||||
maskDs.FillRectangle(rect, Colors.Black);
|
||||
}
|
||||
|
||||
float highlightTotalWidth = (float)highlightRegion.LayoutBounds.Width;
|
||||
// Draw the highlight for the current character
|
||||
float highlightWidth = highlightTotalWidth * charProgress;
|
||||
|
||||
float fadingWidth = (float)highlightRegion.LayoutBounds.Height / 2;
|
||||
|
||||
// Rects
|
||||
var highlightRect = new Rect(
|
||||
highlightRegion.LayoutBounds.X,
|
||||
highlightRegion.LayoutBounds.Y + position.Y,
|
||||
highlightWidth,
|
||||
highlightRegion.LayoutBounds.Height
|
||||
);
|
||||
|
||||
var fadeInRect = new Rect(
|
||||
highlightRect.Right - fadingWidth,
|
||||
highlightRegion.LayoutBounds.Y + position.Y,
|
||||
fadingWidth,
|
||||
highlightRegion.LayoutBounds.Height
|
||||
);
|
||||
var fadeOutRect = new Rect(
|
||||
highlightRect.Right,
|
||||
highlightRegion.LayoutBounds.Y + position.Y,
|
||||
fadingWidth,
|
||||
highlightRegion.LayoutBounds.Height
|
||||
);
|
||||
|
||||
// Brushes
|
||||
using var fadeInBrush = GetHorizontalFillBrush(
|
||||
control,
|
||||
[(0f, 0f), (1f, 1f)],
|
||||
(float)highlightRect.Right - fadingWidth,
|
||||
fadingWidth
|
||||
);
|
||||
using var fadeOutBrush = GetHorizontalFillBrush(
|
||||
control,
|
||||
[(0f, 1f), (1f, 0f)],
|
||||
(float)highlightRect.Right,
|
||||
fadingWidth
|
||||
);
|
||||
|
||||
maskDs.FillRectangle(highlightRect, Colors.White);
|
||||
maskDs.FillRectangle(fadeOutRect, fadeOutBrush);
|
||||
|
||||
highlightMaskDs.FillRectangle(fadeInRect, fadeInBrush);
|
||||
highlightMaskDs.FillRectangle(fadeOutRect, fadeOutBrush);
|
||||
}
|
||||
else
|
||||
{
|
||||
maskDs.FillRectangle(
|
||||
new Rect(
|
||||
textLayout.LayoutBounds.X,
|
||||
position.Y,
|
||||
textLayout.LayoutBounds.Width,
|
||||
textLayout.LayoutBounds.Height
|
||||
),
|
||||
Colors.White
|
||||
);
|
||||
}
|
||||
|
||||
ds.DrawImage(
|
||||
new OpacityEffect
|
||||
{
|
||||
Source = new BlendEffect
|
||||
{
|
||||
Background = IsLyricsGlowEffectEnabled
|
||||
? new GaussianBlurEffect
|
||||
{
|
||||
Source = new AlphaMaskEffect
|
||||
{
|
||||
Source = lyrics,
|
||||
AlphaMask = LyricsGlowEffectScope switch
|
||||
{
|
||||
LineRenderingType.UntilCurrentChar => mask,
|
||||
LineRenderingType.CurrentCharOnly => highlightMask,
|
||||
_ => mask,
|
||||
},
|
||||
},
|
||||
BlurAmount = _lyricsGlowEffectAmount,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
}
|
||||
: new CanvasCommandList(control.Device),
|
||||
Foreground = new AlphaMaskEffect
|
||||
{
|
||||
Source = lyrics,
|
||||
AlphaMask = mask,
|
||||
},
|
||||
},
|
||||
Opacity = line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value,
|
||||
}
|
||||
float highlightTotalWidth = (float)highlightRegion.LayoutBounds.Width;
|
||||
// Draw the highlight for the current character
|
||||
float highlightWidth = highlightTotalWidth * charProgress;
|
||||
|
||||
float fadingWidth = (float)highlightRegion.LayoutBounds.Height / 2;
|
||||
|
||||
// Rects
|
||||
var highlightRect = new Rect(
|
||||
highlightRegion.LayoutBounds.X,
|
||||
highlightRegion.LayoutBounds.Y + position.Y,
|
||||
highlightWidth,
|
||||
highlightRegion.LayoutBounds.Height
|
||||
);
|
||||
|
||||
var fadeInRect = new Rect(
|
||||
highlightRect.Right - fadingWidth,
|
||||
highlightRegion.LayoutBounds.Y + position.Y,
|
||||
fadingWidth,
|
||||
highlightRegion.LayoutBounds.Height
|
||||
);
|
||||
var fadeOutRect = new Rect(
|
||||
highlightRect.Right,
|
||||
highlightRegion.LayoutBounds.Y + position.Y,
|
||||
fadingWidth,
|
||||
highlightRegion.LayoutBounds.Height
|
||||
);
|
||||
|
||||
// Brushes
|
||||
using var fadeInBrush = GetHorizontalFillBrush(
|
||||
control,
|
||||
[(0f, 0f), (1f, 1f)],
|
||||
(float)highlightRect.Right - fadingWidth,
|
||||
fadingWidth
|
||||
);
|
||||
using var fadeOutBrush = GetHorizontalFillBrush(
|
||||
control,
|
||||
[(0f, 1f), (1f, 0f)],
|
||||
(float)highlightRect.Right,
|
||||
fadingWidth
|
||||
);
|
||||
|
||||
maskDs.FillRectangle(highlightRect, Colors.White);
|
||||
maskDs.FillRectangle(fadeOutRect, fadeOutBrush);
|
||||
|
||||
highlightMaskDs.FillRectangle(fadeInRect, fadeInBrush);
|
||||
highlightMaskDs.FillRectangle(fadeOutRect, fadeOutBrush);
|
||||
}
|
||||
else
|
||||
{
|
||||
maskDs.FillRectangle(
|
||||
new Rect(
|
||||
textLayout.LayoutBounds.X,
|
||||
position.Y,
|
||||
textLayout.LayoutBounds.Width,
|
||||
textLayout.LayoutBounds.Height
|
||||
),
|
||||
Colors.White
|
||||
);
|
||||
}
|
||||
|
||||
ds.DrawImage(
|
||||
new OpacityEffect
|
||||
{
|
||||
Source = new BlendEffect
|
||||
{
|
||||
Background = IsLyricsGlowEffectEnabled
|
||||
? new GaussianBlurEffect
|
||||
{
|
||||
Source = new AlphaMaskEffect
|
||||
{
|
||||
Source = lyrics,
|
||||
AlphaMask = LyricsGlowEffectScope switch
|
||||
{
|
||||
LineRenderingType.UntilCurrentChar => mask,
|
||||
LineRenderingType.CurrentCharOnly => highlightMask,
|
||||
_ => mask,
|
||||
},
|
||||
},
|
||||
BlurAmount = _lyricsGlowEffectAmount,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
}
|
||||
: new CanvasCommandList(control.Device),
|
||||
Foreground = new AlphaMaskEffect
|
||||
{
|
||||
Source = lyrics,
|
||||
AlphaMask = mask,
|
||||
},
|
||||
},
|
||||
Opacity = line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value,
|
||||
}
|
||||
);
|
||||
|
||||
// Reset scale
|
||||
ds.Transform = Matrix3x2.Identity;
|
||||
}
|
||||
@@ -399,7 +475,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 +500,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 +527,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,8 @@
|
||||
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.UI.Xaml;
|
||||
using Windows.Graphics.Imaging;
|
||||
using System.Collections.ObjectModel;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
@@ -17,20 +10,17 @@ 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<ObservableCollection<LyricsSearchProviderInfo>>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>>>
|
||||
{
|
||||
public async void Receive(
|
||||
PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>> message
|
||||
)
|
||||
public async void Receive(PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
@@ -42,9 +32,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public async void Receive(
|
||||
PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>> message
|
||||
)
|
||||
public async void Receive(PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
@@ -62,33 +50,19 @@ 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;
|
||||
}
|
||||
@@ -118,9 +92,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsSettingsControlViewModel)
|
||||
else if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsCustomFontColor))
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsCustomFontColor))
|
||||
{
|
||||
_customFontColor = message.NewValue;
|
||||
UpdateFontColor();
|
||||
@@ -128,25 +102,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<double> message)
|
||||
{
|
||||
if (message.Sender is LyricsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsPageViewModel.MaxLyricsWidth))
|
||||
{
|
||||
_maxLyricsWidthTransition.StartTransition((float)message.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,37 +119,25 @@ 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;
|
||||
}
|
||||
else if (
|
||||
message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsBlurAmount)
|
||||
)
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBlurAmount))
|
||||
{
|
||||
LyricsBlurAmount = message.NewValue;
|
||||
}
|
||||
else if (
|
||||
message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontSize)
|
||||
)
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize))
|
||||
{
|
||||
LyricsFontSize = message.NewValue;
|
||||
}
|
||||
@@ -198,45 +146,41 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
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.LyricsFontColorType))
|
||||
{
|
||||
LyricsFontColorType = message.NewValue;
|
||||
}
|
||||
@@ -245,9 +189,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -266,7 +210,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
partial void OnLyricsFontWeightChanged(LyricsFontWeight value)
|
||||
{
|
||||
_textFormat.FontWeight = value.ToFontWeight();
|
||||
_lyricsTextFormat.FontWeight = value.ToFontWeight();
|
||||
_isRelayoutNeeded = true;
|
||||
}
|
||||
|
||||
@@ -274,40 +218,5 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
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: 1f,
|
||||
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: 1f
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
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
|
||||
@@ -15,6 +16,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public void Update(ICanvasAnimatedControl control, CanvasAnimatedUpdateEventArgs args)
|
||||
{
|
||||
bool isCanvasWidthChanged = _canvasWidth != control.Size.Width;
|
||||
bool isDisplayTypeChanged = _displayType != _displayTypeReceived;
|
||||
|
||||
_canvasWidth = (float)control.Size.Width;
|
||||
_canvasHeight = (float)control.Size.Height;
|
||||
_displayType = _displayTypeReceived;
|
||||
|
||||
if (_isPlaying)
|
||||
{
|
||||
TotalTime += args.Timing.ElapsedTime;
|
||||
@@ -22,15 +30,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
ElapsedTime = args.Timing.ElapsedTime;
|
||||
|
||||
if (_immersiveBgTransition.IsTransitioning)
|
||||
{
|
||||
_immersiveBgTransition.Update(ElapsedTime);
|
||||
}
|
||||
|
||||
if (_albumArtBgTransition.IsTransitioning)
|
||||
{
|
||||
_albumArtBgTransition.Update(ElapsedTime);
|
||||
}
|
||||
_immersiveBgTransition.Update(ElapsedTime);
|
||||
_albumArtBgTransition.Update(ElapsedTime);
|
||||
_songInfoOpacityTransition.Update(ElapsedTime);
|
||||
|
||||
if (IsDynamicCoverOverlayEnabled)
|
||||
{
|
||||
@@ -38,41 +40,53 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_rotateAngle %= MathF.PI * 2;
|
||||
}
|
||||
|
||||
if (_maxLyricsWidthTransition.IsTransitioning)
|
||||
_albumArtSize = MathF.Min(
|
||||
(_canvasHeight - _topMargin - _bottomMargin) * 8.5f / 16,
|
||||
(_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2);
|
||||
_albumArtSize = MathF.Max(0, _albumArtSize);
|
||||
|
||||
if (isDisplayTypeChanged || isCanvasWidthChanged)
|
||||
{
|
||||
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);
|
||||
_isRelayoutNeeded = true;
|
||||
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);
|
||||
_isRelayoutNeeded = true;
|
||||
break;
|
||||
case LyricsDisplayType.PlaceholderOnly:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_lyricsXTransition.Update(ElapsedTime);
|
||||
_albumArtXTransition.Update(ElapsedTime);
|
||||
_lyricsOpacityTransition.Update(ElapsedTime);
|
||||
_albumArtOpacityTransition.Update(ElapsedTime);
|
||||
|
||||
if (_lyricsXTransition.IsTransitioning)
|
||||
{
|
||||
_maxLyricsWidthTransition.Update(ElapsedTime);
|
||||
_isRelayoutNeeded = true;
|
||||
}
|
||||
|
||||
switch (DisplayType)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (_lyricsOpacityTransition.IsTransitioning)
|
||||
{
|
||||
_lyricsOpacityTransition.Update(ElapsedTime);
|
||||
}
|
||||
|
||||
// 神光角度目标值(左右±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);
|
||||
|
||||
if (_shenGuangAngleTransition.IsTransitioning)
|
||||
{
|
||||
_shenGuangAngleTransition.Update(ElapsedTime);
|
||||
}
|
||||
_maxLyricsWidth = _canvasWidth - _lyricsXTransition.Value - _rightMargin;
|
||||
_maxLyricsWidth = Math.Max(_maxLyricsWidth, 0);
|
||||
|
||||
if (_isRelayoutNeeded)
|
||||
{
|
||||
@@ -93,9 +107,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++)
|
||||
@@ -117,9 +131,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
line.CanvasTextLayout = new CanvasTextLayout(
|
||||
control,
|
||||
line.Text,
|
||||
_textFormat,
|
||||
(float)_maxLyricsWidthTransition.Value,
|
||||
(float)control.Size.Height
|
||||
_lyricsTextFormat,
|
||||
_maxLyricsWidth,
|
||||
_canvasHeight
|
||||
);
|
||||
|
||||
line.Position = new Vector2(0, y);
|
||||
@@ -167,13 +181,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else if (!withAnimation)
|
||||
{
|
||||
_canvasYScrollTransition.JumpTo(targetYScrollOffset);
|
||||
_canvasYScrollTransition.StartTransition(targetYScrollOffset, true);
|
||||
}
|
||||
|
||||
if (_canvasYScrollTransition.IsTransitioning)
|
||||
{
|
||||
_canvasYScrollTransition.Update(ElapsedTime);
|
||||
}
|
||||
_canvasYScrollTransition.Update(ElapsedTime);
|
||||
|
||||
_startVisibleLineIndex = _endVisibleLineIndex = -1;
|
||||
|
||||
@@ -191,7 +202,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
if (
|
||||
_canvasYScrollTransition.Value
|
||||
+ (float)(control.Size.Height / 2)
|
||||
+ _canvasHeight / 2
|
||||
+ line.Position.Y
|
||||
+ textLayout.LayoutBounds.Height
|
||||
>= 0
|
||||
@@ -204,7 +215,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
if (
|
||||
_canvasYScrollTransition.Value
|
||||
+ (float)(control.Size.Height / 2)
|
||||
+ _canvasHeight / 2
|
||||
+ line.Position.Y
|
||||
+ textLayout.LayoutBounds.Height
|
||||
>= control.Size.Height
|
||||
@@ -225,8 +236,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private protected void UpdateFontColor()
|
||||
{
|
||||
ThemeTypeSent =
|
||||
Helper.ColorHelper.GetElementThemeFromBackgroundColor(_lyricsWindowBgColor);
|
||||
ThemeTypeSent = Helper.ColorHelper.GetElementThemeFromBackgroundColor(_lyricsWindowBgColor);
|
||||
|
||||
Color fallbackFg = Colors.Transparent;
|
||||
switch (ThemeTypeSent)
|
||||
@@ -261,13 +271,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
var currentPlayingLineIndex = GetCurrentPlayingLineIndex();
|
||||
|
||||
int halfVisibleLineCount =
|
||||
Math.Max(1, Math.Max(
|
||||
currentPlayingLineIndex - _startVisibleLineIndex,
|
||||
_endVisibleLineIndex - currentPlayingLineIndex
|
||||
));
|
||||
var currentPlayingLine = _multiLangLyrics
|
||||
.SafeGet(_langIndex)
|
||||
?.SafeGet(currentPlayingLineIndex);
|
||||
|
||||
if (halfVisibleLineCount < 1)
|
||||
if (currentPlayingLine == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -281,61 +289,45 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
continue;
|
||||
}
|
||||
|
||||
int distanceFromPlayingLine = Math.Abs(i - currentPlayingLineIndex);
|
||||
if (distanceFromPlayingLine > halfVisibleLineCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
float distanceFromPlayingLine = Math.Abs(line.Position.Y - currentPlayingLine.Position.Y);
|
||||
|
||||
float distanceFactor = distanceFromPlayingLine / (float)halfVisibleLineCount;
|
||||
float distanceFactor = Math.Clamp(distanceFromPlayingLine / (_canvasHeight / 2), 0, 1);
|
||||
|
||||
line.AngleTransition.StartTransition(
|
||||
_isFanLyricsEnabled
|
||||
? (float)Math.PI
|
||||
* (30f / 180f)
|
||||
* distanceFactor
|
||||
* (i - currentPlayingLineIndex > 0 ? 1 : -1)
|
||||
: 0
|
||||
);
|
||||
line.BlurAmountTransition.StartTransition(LyricsBlurAmount * distanceFactor);
|
||||
line.ScaleTransition.StartTransition(
|
||||
if (!line.AngleTransition.IsTransitioning)
|
||||
line.AngleTransition.StartTransition(
|
||||
_isFanLyricsEnabled
|
||||
? (float)Math.PI
|
||||
* (30f / 180f)
|
||||
* distanceFactor
|
||||
* (i - currentPlayingLineIndex > 0 ? 1 : -1)
|
||||
: 0
|
||||
);
|
||||
|
||||
if (!line.BlurAmountTransition.IsTransitioning)
|
||||
line.BlurAmountTransition.StartTransition(LyricsBlurAmount * distanceFactor);
|
||||
|
||||
if (!line.ScaleTransition.IsTransitioning)
|
||||
line.ScaleTransition.StartTransition(
|
||||
_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale)
|
||||
);
|
||||
line.OpacityTransition.StartTransition(_defaultOpacity - distanceFactor * _defaultOpacity * (1 - LyricsVerticalEdgeOpacity / 100f));
|
||||
|
||||
if (!line.OpacityTransition.IsTransitioning)
|
||||
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)
|
||||
if (!line.HighlightOpacityTransition.IsTransitioning)
|
||||
{
|
||||
line.HighlightOpacityTransition.StartTransition(
|
||||
distanceFromPlayingLine == 0 ? 1 : 0
|
||||
distanceFromPlayingLine == 0 ? 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
@@ -9,8 +11,11 @@ using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
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 Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
@@ -19,103 +24,92 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class LyricsRendererViewModel : BaseViewModel
|
||||
{
|
||||
private readonly ValueTransition<float> _albumArtBgTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 1.0f
|
||||
);
|
||||
private SoftwareBitmap? _lastAlbumArtSwBitmap = null;
|
||||
private SoftwareBitmap? _albumArtSwBitmap = null;
|
||||
|
||||
private readonly ValueTransition<float> _canvasYScrollTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.8f,
|
||||
easingType: EasingType.SmootherStep
|
||||
);
|
||||
private float _albumArtSize = 0f;
|
||||
private int _albumArtCornerRadius = 0;
|
||||
|
||||
private readonly float _coverRotateSpeed = 0.003f;
|
||||
private float _albumArtY = 0f;
|
||||
|
||||
private string? _lastSongTitle;
|
||||
private string? _songTitle;
|
||||
|
||||
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 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 _fontColor;
|
||||
private Color? _adaptiveFontColor = null;
|
||||
|
||||
private Color? _albumArtAccentColor = null;
|
||||
|
||||
private SoftwareBitmap? _albumArtBitmap = null;
|
||||
|
||||
private Color? _customFontColor;
|
||||
|
||||
private Color _lightFontColor = Colors.White;
|
||||
private Color _darkFontColor = Colors.Black;
|
||||
private Color? _albumArtAccentColor = null;
|
||||
private Color _lyricsWindowBgColor = Colors.Transparent;
|
||||
|
||||
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 _isRelayoutNeeded = 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 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.Wrap,
|
||||
};
|
||||
private CanvasTextFormat _artistTextFormat = new()
|
||||
{
|
||||
FontSize = 16,
|
||||
FontWeight = FontWeights.Bold,
|
||||
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
||||
WordWrapping = CanvasWordWrapping.Wrap,
|
||||
};
|
||||
|
||||
private Task? _refreshLyricsTask;
|
||||
private CancellationTokenSource? _refreshLyricsCts;
|
||||
|
||||
public LyricsRendererViewModel(
|
||||
ISettingsService settingsService, IPlaybackService playbackService,
|
||||
@@ -125,7 +119,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_playbackService = playbackService;
|
||||
_libWatcherService = libWatcherService;
|
||||
|
||||
CoverImageRadius = _settingsService.CoverImageRadius;
|
||||
_albumArtCornerRadius = _settingsService.CoverImageRadius;
|
||||
IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
|
||||
CoverOverlayOpacity = _settingsService.CoverOverlayOpacity;
|
||||
CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount;
|
||||
@@ -141,6 +135,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
|
||||
_customFontColor = _settingsService.LyricsCustomFontColor;
|
||||
|
||||
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.LyricsAlignmentType.ToCanvasHorizontalAlignment();
|
||||
|
||||
_libWatcherService.MusicLibraryFilesChanged +=
|
||||
LibWatcherService_MusicLibraryFilesChanged;
|
||||
|
||||
@@ -148,20 +144,15 @@ 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; }
|
||||
|
||||
public int CoverOverlayBlurAmount { get; set; }
|
||||
|
||||
public int CoverOverlayOpacity { get; set; }
|
||||
|
||||
public LyricsDisplayType DisplayType { get; set; }
|
||||
private LyricsDisplayType _displayTypeReceived = LyricsDisplayType.PlaceholderOnly;
|
||||
private LyricsDisplayType _displayType = LyricsDisplayType.PlaceholderOnly;
|
||||
|
||||
public TimeSpan ElapsedTime { get; set; } = TimeSpan.Zero;
|
||||
|
||||
@@ -169,7 +160,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
public bool IsLyricsGlowEffectEnabled { get; set; }
|
||||
|
||||
public LyricsAlignmentType LyricsAlignmentType { get; set; }
|
||||
public TextAlignmentType LyricsAlignmentType { get; set; }
|
||||
|
||||
public int LyricsBlurAmount { get; set; }
|
||||
|
||||
@@ -303,36 +294,88 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
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?.Title != _songTitle || SongInfo?.Artist != _songArtist)
|
||||
{
|
||||
_lastSongTitle = _songTitle;
|
||||
_songTitle = SongInfo?.Title;
|
||||
|
||||
_lastSongArtist = _songArtist;
|
||||
_songArtist = SongInfo?.Artist;
|
||||
|
||||
_songInfoOpacityTransition.Reset(0f);
|
||||
_songInfoOpacityTransition.StartTransition(1f);
|
||||
|
||||
await RefreshLyricsAsync();
|
||||
|
||||
TotalTime = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
if (SongInfo?.AlbumArtSwBitmap != _albumArtSwBitmap)
|
||||
{
|
||||
_lastAlbumArtSwBitmap = _albumArtSwBitmap;
|
||||
_albumArtSwBitmap = SongInfo?.AlbumArtSwBitmap;
|
||||
|
||||
_albumArtAccentColor = SongInfo?.AlbumArtAccentColor;
|
||||
_lyricsWindowBgColor = _albumArtAccentColor ?? Colors.Gray;
|
||||
|
||||
_albumArtBgTransition.Reset(0f);
|
||||
_albumArtBgTransition.StartTransition(1f);
|
||||
|
||||
if (!_isDesktopMode && !_isDockMode) _adaptiveFontColor = Helper.ColorHelper.GetForegroundColor(_lyricsWindowBgColor);
|
||||
UpdateFontColor();
|
||||
}
|
||||
}
|
||||
|
||||
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 RefreshLyricsCoreAsync(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetLyricsLoadingPlaceholder();
|
||||
|
||||
string? lyricsRaw = null;
|
||||
LyricsFormat? lyricsFormat = null;
|
||||
|
||||
if (SongInfo != null)
|
||||
{
|
||||
(lyricsRaw, lyricsFormat) = await _musicSearchService.SearchLyricsAsync(
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist,
|
||||
SongInfo.Album ?? "",
|
||||
SongInfo.DurationMs ?? 0
|
||||
);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
_multiLangLyrics = new LyricsParser().Parse(
|
||||
lyricsRaw,
|
||||
lyricsFormat,
|
||||
SongInfo?.Title,
|
||||
SongInfo?.Artist,
|
||||
(int)(SongInfo?.DurationMs ?? 0)
|
||||
);
|
||||
_isRelayoutNeeded = true;
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
private void SetLyricsLoadingPlaceholder()
|
||||
@@ -344,7 +387,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||
Text = App.ResourceLoader!.GetString("LyricsLoading"),
|
||||
Text = "● ● ●",
|
||||
CharTimings = [],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -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,7 +1,6 @@
|
||||
// 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;
|
||||
@@ -104,9 +103,9 @@ 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)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@@ -19,19 +20,20 @@ using Microsoft.UI.Xaml;
|
||||
using Windows.Globalization;
|
||||
using Windows.Media.Playback;
|
||||
using Windows.System;
|
||||
using Windows.UI;
|
||||
using WinRT.Interop;
|
||||
|
||||
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;
|
||||
|
||||
public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService)
|
||||
public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IPlaybackService playbackService) : base(settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_libWatcherService = libWatcherService;
|
||||
_playbackService = playbackService;
|
||||
|
||||
LocalLyricsFolders = [.. _settingsService.LocalLyricsFolders];
|
||||
LyricsSearchProvidersInfo = [.. _settingsService.LyricsSearchProvidersInfo];
|
||||
@@ -46,12 +48,33 @@ 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;
|
||||
LyricsFontColorType = _settingsService.LyricsFontColorType;
|
||||
LyricsCustomFontColor = _settingsService.LyricsCustomFontColor;
|
||||
|
||||
MediaSourceProvidersInfo = [.. _settingsService.MediaSourceProvidersInfo];
|
||||
_playbackService.MediaSourceProvidersInfoChanged += PlaybackService_SessionIdsChanged;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
BuildDate = (await AppInfo.GetBuildDate()).ToString("(yyyy/MM/dd HH:mm:ss)");
|
||||
});
|
||||
}
|
||||
|
||||
private void PlaybackService_SessionIdsChanged(object? sender, Events.MediaSourceProvidersInfoEventArgs e)
|
||||
{
|
||||
MediaSourceProvidersInfo = [.. e.MediaSourceProviersInfo];
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial AutoStartWindowType AutoStartWindowType { get; set; }
|
||||
|
||||
@@ -88,12 +111,63 @@ 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 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; }
|
||||
|
||||
[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;
|
||||
|
||||
public void OnLyricsSearchProvidersReordered()
|
||||
{
|
||||
@@ -134,6 +208,15 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
);
|
||||
}
|
||||
|
||||
public void ToggleMediaSourceProvider(MediaSourceProviderInfo providerInfo)
|
||||
{
|
||||
Broadcast(
|
||||
MediaSourceProvidersInfo,
|
||||
MediaSourceProvidersInfo,
|
||||
nameof(MediaSourceProvidersInfo)
|
||||
);
|
||||
}
|
||||
|
||||
private void AddFolderAsync(string path)
|
||||
{
|
||||
var normalizedPath =
|
||||
@@ -315,5 +398,65 @@ 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,224 +20,25 @@
|
||||
|
||||
<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 -->
|
||||
<Grid
|
||||
x:Name="BottomCommandGrid"
|
||||
Margin="0,0,4,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="Transparent"
|
||||
Opacity="0"
|
||||
PointerEntered="BottomCommandGrid_PointerEntered"
|
||||
PointerExited="BottomCommandGrid_PointerExited"
|
||||
@@ -246,48 +47,18 @@
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
|
||||
<TextBlock Margin="12,0,0,0" Text="{x:Bind ViewModel.SongInfo.SourceAppUserModelId, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" />
|
||||
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
|
||||
<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>
|
||||
</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>
|
||||
|
||||
<!-- 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>
|
||||
@@ -310,38 +81,13 @@
|
||||
</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},
|
||||
Glyph=}"
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
|
||||
</StackPanel>
|
||||
@@ -361,23 +107,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,8 @@
|
||||
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.Threading.Tasks;
|
||||
using WinUIEx.Messaging;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
@@ -21,36 +15,6 @@ 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;
|
||||
@@ -73,101 +37,24 @@ namespace BetterLyrics.WinUI3.Views
|
||||
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()
|
||||
{
|
||||
await BeforeSwitchDisplayTypeAsync();
|
||||
|
||||
Grid.SetColumn(LyricsPlaceholderGrid, 0);
|
||||
Grid.SetColumnSpan(LyricsPlaceholderGrid, 3);
|
||||
|
||||
LyricsPlaceholderGrid.Opacity = 1;
|
||||
LyricsGrid.Opacity = 1;
|
||||
}
|
||||
|
||||
|
||||
private async Task SwitchToAlbumArtOnlyDisplayTypeAsync()
|
||||
{
|
||||
await BeforeSwitchDisplayTypeAsync();
|
||||
|
||||
Grid.SetColumn(SongInfoInnerGrid, 0);
|
||||
Grid.SetColumnSpan(SongInfoInnerGrid, 3);
|
||||
|
||||
SongInfoInnerGrid.Opacity = 1;
|
||||
LyricsGrid.Opacity = 1;
|
||||
}
|
||||
|
||||
|
||||
private async Task BeforeSwitchDisplayTypeAsync()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
<local:LyricsPage />
|
||||
|
||||
<!-- Top command -->
|
||||
|
||||
<Grid
|
||||
x:Name="TopCommandGrid"
|
||||
Height="{x:Bind ViewModel.TitleBarHeight, Mode=OneWay}"
|
||||
|
||||
@@ -33,6 +33,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},
|
||||
@@ -69,111 +74,6 @@
|
||||
</TransitionCollection>
|
||||
</controls:SwitchPresenter.ContentTransitions>
|
||||
|
||||
<!-- Lyrics lib -->
|
||||
|
||||
<controls:Case Value="LyricsLib">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsExpander
|
||||
x:Uid="SettingsPageMusicLib"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True"
|
||||
ItemsSource="{x:Bind ViewModel.LocalLyricsFolders, Mode=OneWay}">
|
||||
<controls:SettingsExpander.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:SettingsCard>
|
||||
<controls:SettingsCard.Header>
|
||||
<HyperlinkButton Content="{Binding Path, Mode=OneWay}" NavigateUri="{Binding Path, Mode=OneWay}" />
|
||||
</controls:SettingsCard.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<HyperlinkButton
|
||||
x:Uid="SettingsPageRemovePath"
|
||||
Click="SettingsPageRemovePathButton_Click"
|
||||
Tag="{Binding}" />
|
||||
<ToggleSwitch
|
||||
DataContext="{Binding}"
|
||||
IsOn="{Binding IsEnabled, Mode=TwoWay}"
|
||||
Toggled="LocalLyricsFolderToggleSwitch_Toggled" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
</DataTemplate>
|
||||
</controls:SettingsExpander.ItemTemplate>
|
||||
<controls:SettingsExpander.ItemsHeader>
|
||||
<InfoBar
|
||||
x:Uid="SettingsPageRemoveInfo"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0"
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
Severity="Success">
|
||||
|
||||
<interactivity:Interaction.Behaviors>
|
||||
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LocalLyricsFolders.Count, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LocalLyricsFolders.Count, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
|
||||
</interactivity:Interaction.Behaviors>
|
||||
|
||||
</InfoBar>
|
||||
</controls:SettingsExpander.ItemsHeader>
|
||||
<controls:SettingsExpander.ItemsFooter>
|
||||
<controls:SettingsCard x:Uid="SettingsPageAddFolder" Style="{StaticResource DefaultSettingsExpanderItemStyle}">
|
||||
<Button
|
||||
x:Uid="SettingsPageAddFolderButton"
|
||||
Command="{x:Bind ViewModel.SelectAndAddFolderCommand}"
|
||||
CommandParameter="{Binding ElementName=RootGrid}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.ItemsFooter>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<controls:SettingsCard
|
||||
x:Name="LyricsSearchProvidersSettingsExpander"
|
||||
x:Uid="SettingsPageLyricsSearchProvidersConfig"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}" />
|
||||
|
||||
<ListView
|
||||
x:Name="LyricsSearchProvidersListView"
|
||||
Margin="0,-4,0,0"
|
||||
AllowDrop="True"
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
DragItemsCompleted="LyricsSearchProvidersListView_DragItemsCompleted"
|
||||
ItemsSource="{x:Bind ViewModel.LyricsSearchProvidersInfo, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</ListView.OpacityTransition>
|
||||
<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:LyricsSearchProviderInfo">
|
||||
<controls:SettingsCard Padding="60,0,48,0" Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" Toggled="LyricsSearchProviderToggleSwitch_Toggled" />
|
||||
</controls:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- App appearance and behavior -->
|
||||
|
||||
<controls:Case Value="App">
|
||||
@@ -273,10 +173,13 @@
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
<!-- Album art style -->
|
||||
<!-- 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" />
|
||||
@@ -296,6 +199,149 @@
|
||||
</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
|
||||
x:Uid="SettingsPageMusicLib"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True"
|
||||
ItemsSource="{x:Bind ViewModel.LocalLyricsFolders, Mode=OneWay}">
|
||||
<controls:SettingsExpander.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:SettingsCard>
|
||||
<controls:SettingsCard.Header>
|
||||
<HyperlinkButton
|
||||
Click="LocalFolderHyperlinkButton_Click"
|
||||
Content="{Binding Path, Mode=OneWay}"
|
||||
Tag="{Binding Path, Mode=OneWay}" />
|
||||
</controls:SettingsCard.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<HyperlinkButton
|
||||
x:Uid="SettingsPageRemovePath"
|
||||
Click="SettingsPageRemovePathButton_Click"
|
||||
Tag="{Binding}" />
|
||||
<ToggleSwitch
|
||||
DataContext="{Binding}"
|
||||
IsOn="{Binding IsEnabled, Mode=TwoWay}"
|
||||
Toggled="LocalLyricsFolderToggleSwitch_Toggled" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
</DataTemplate>
|
||||
</controls:SettingsExpander.ItemTemplate>
|
||||
<controls:SettingsExpander.ItemsHeader>
|
||||
<InfoBar
|
||||
x:Uid="SettingsPageRemoveInfo"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0"
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
Severity="Success">
|
||||
|
||||
<interactivity:Interaction.Behaviors>
|
||||
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LocalLyricsFolders.Count, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LocalLyricsFolders.Count, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
|
||||
</interactivity:Interaction.Behaviors>
|
||||
|
||||
</InfoBar>
|
||||
</controls:SettingsExpander.ItemsHeader>
|
||||
<controls:SettingsExpander.ItemsFooter>
|
||||
<controls:SettingsCard x:Uid="SettingsPageAddFolder" Style="{StaticResource DefaultSettingsExpanderItemStyle}">
|
||||
<Button
|
||||
x:Uid="SettingsPageAddFolderButton"
|
||||
Command="{x:Bind ViewModel.SelectAndAddFolderCommand}"
|
||||
CommandParameter="{Binding ElementName=RootGrid}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.ItemsFooter>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<controls:SettingsCard
|
||||
x:Name="LyricsSearchProvidersSettingsExpander"
|
||||
x:Uid="SettingsPageLyricsSearchProvidersConfig"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}" />
|
||||
|
||||
<ListView
|
||||
x:Name="LyricsSearchProvidersListView"
|
||||
Margin="0,-4,0,0"
|
||||
AllowDrop="True"
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
DragItemsCompleted="LyricsSearchProvidersListView_DragItemsCompleted"
|
||||
ItemsSource="{x:Bind ViewModel.LyricsSearchProvidersInfo, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</ListView.OpacityTransition>
|
||||
<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:LyricsSearchProviderInfo">
|
||||
<controls:SettingsCard Padding="60,0,48,0" Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" Toggled="LyricsSearchProviderToggleSwitch_Toggled" />
|
||||
</controls:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
@@ -309,7 +355,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 +363,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" />
|
||||
@@ -333,7 +379,7 @@
|
||||
</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}}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
|
||||
@@ -349,16 +395,16 @@
|
||||
IsColorSliderVisible="True"
|
||||
IsHexInputVisible="True"
|
||||
IsMoreButtonVisible="True"
|
||||
Color="{x:Bind LyricsSettingsControlViewModel.LyricsCustomFontColor, Mode=TwoWay}">
|
||||
Color="{x:Bind ViewModel.LyricsCustomFontColor, Mode=TwoWay}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind LyricsSettingsControlViewModel.LyricsFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
Binding="{x:Bind ViewModel.LyricsFontColorType, 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.LyricsFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="2">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
@@ -381,7 +427,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 +447,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 +474,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 +493,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 +501,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,7 +514,7 @@
|
||||
</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>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// 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
|
||||
{
|
||||
@@ -17,9 +18,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
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 +65,27 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user